001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertNotNull;
022
023import com.fasterxml.jackson.databind.ObjectMapper;
024import java.io.IOException;
025import java.nio.ByteBuffer;
026import java.util.Arrays;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030import org.apache.hadoop.hbase.Cell;
031import org.apache.hadoop.hbase.CellComparatorImpl;
032import org.apache.hadoop.hbase.CellUtil;
033import org.apache.hadoop.hbase.HBaseClassTestRule;
034import org.apache.hadoop.hbase.HConstants;
035import org.apache.hadoop.hbase.KeyValue;
036import org.apache.hadoop.hbase.filter.BinaryComparator;
037import org.apache.hadoop.hbase.filter.ColumnCountGetFilter;
038import org.apache.hadoop.hbase.filter.ColumnPaginationFilter;
039import org.apache.hadoop.hbase.filter.ColumnPrefixFilter;
040import org.apache.hadoop.hbase.filter.ColumnRangeFilter;
041import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
042import org.apache.hadoop.hbase.filter.DependentColumnFilter;
043import org.apache.hadoop.hbase.filter.FamilyFilter;
044import org.apache.hadoop.hbase.filter.Filter;
045import org.apache.hadoop.hbase.filter.FilterList;
046import org.apache.hadoop.hbase.filter.FilterList.Operator;
047import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
048import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
049import org.apache.hadoop.hbase.filter.KeyOnlyFilter;
050import org.apache.hadoop.hbase.filter.MultipleColumnPrefixFilter;
051import org.apache.hadoop.hbase.filter.PageFilter;
052import org.apache.hadoop.hbase.filter.PrefixFilter;
053import org.apache.hadoop.hbase.filter.QualifierFilter;
054import org.apache.hadoop.hbase.filter.RowFilter;
055import org.apache.hadoop.hbase.filter.SingleColumnValueExcludeFilter;
056import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
057import org.apache.hadoop.hbase.filter.SkipFilter;
058import org.apache.hadoop.hbase.filter.TimestampsFilter;
059import org.apache.hadoop.hbase.filter.ValueFilter;
060import org.apache.hadoop.hbase.filter.WhileMatchFilter;
061import org.apache.hadoop.hbase.testclassification.ClientTests;
062import org.apache.hadoop.hbase.testclassification.SmallTests;
063import org.apache.hadoop.hbase.util.BuilderStyleTest;
064import org.apache.hadoop.hbase.util.Bytes;
065import org.junit.Assert;
066import org.junit.ClassRule;
067import org.junit.Test;
068import org.junit.experimental.categories.Category;
069
070/**
071 * Run tests that use the functionality of the Operation superclass for
072 * Puts, Gets, Deletes, Scans, and MultiPuts.
073 */
074@Category({ClientTests.class, SmallTests.class})
075public class TestOperation {
076  @ClassRule
077  public static final HBaseClassTestRule CLASS_RULE =
078      HBaseClassTestRule.forClass(TestOperation.class);
079
080  private static byte [] ROW = Bytes.toBytes("testRow");
081  private static byte [] FAMILY = Bytes.toBytes("testFamily");
082  private static byte [] QUALIFIER = Bytes.toBytes("testQualifier");
083  private static byte [] VALUE = Bytes.toBytes("testValue");
084
085  private static ObjectMapper mapper = new ObjectMapper();
086
087  private static List<Long> TS_LIST = Arrays.asList(2L, 3L, 5L);
088  private static TimestampsFilter TS_FILTER = new TimestampsFilter(TS_LIST);
089  private static String STR_TS_FILTER = TS_FILTER.getClass().getSimpleName() + " (3/3): [2, 3, 5]";
090
091  private static List<Long> L_TS_LIST = Arrays.asList(0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L);
092  private static TimestampsFilter L_TS_FILTER = new TimestampsFilter(L_TS_LIST);
093  private static String STR_L_TS_FILTER =
094      L_TS_FILTER.getClass().getSimpleName() + " (5/11): [0, 1, 2, 3, 4]";
095
096  private static String COL_NAME_1 = "col1";
097  private static ColumnPrefixFilter COL_PRE_FILTER =
098      new ColumnPrefixFilter(Bytes.toBytes(COL_NAME_1));
099  private static String STR_COL_PRE_FILTER =
100      COL_PRE_FILTER.getClass().getSimpleName() + " " + COL_NAME_1;
101
102  private static String COL_NAME_2 = "col2";
103  private static ColumnRangeFilter CR_FILTER =
104      new ColumnRangeFilter(Bytes.toBytes(COL_NAME_1), true, Bytes.toBytes(COL_NAME_2), false);
105  private static String STR_CR_FILTER = CR_FILTER.getClass().getSimpleName()
106      + " [" + COL_NAME_1 + ", " + COL_NAME_2 + ")";
107
108  private static int COL_COUNT = 9;
109  private static ColumnCountGetFilter CCG_FILTER = new ColumnCountGetFilter(COL_COUNT);
110  private static String STR_CCG_FILTER = CCG_FILTER.getClass().getSimpleName() + " " + COL_COUNT;
111
112  private static int LIMIT = 3;
113  private static int OFFSET = 4;
114  private static ColumnPaginationFilter CP_FILTER = new ColumnPaginationFilter(LIMIT, OFFSET);
115  private static String STR_CP_FILTER = CP_FILTER.getClass().getSimpleName()
116      + " (" + LIMIT + ", " + OFFSET + ")";
117
118  private static String STOP_ROW_KEY = "stop";
119  private static InclusiveStopFilter IS_FILTER =
120      new InclusiveStopFilter(Bytes.toBytes(STOP_ROW_KEY));
121  private static String STR_IS_FILTER =
122      IS_FILTER.getClass().getSimpleName() + " " + STOP_ROW_KEY;
123
124  private static String PREFIX = "prefix";
125  private static PrefixFilter PREFIX_FILTER = new PrefixFilter(Bytes.toBytes(PREFIX));
126  private static String STR_PREFIX_FILTER = "PrefixFilter " + PREFIX;
127
128  private static byte[][] PREFIXES = { Bytes.toBytes("0"), Bytes.toBytes("1"), Bytes.toBytes("2") };
129  private static MultipleColumnPrefixFilter MCP_FILTER = new MultipleColumnPrefixFilter(PREFIXES);
130  private static String STR_MCP_FILTER =
131      MCP_FILTER.getClass().getSimpleName() + " (3/3): [0, 1, 2]";
132
133  private static byte[][] L_PREFIXES = {
134    Bytes.toBytes("0"), Bytes.toBytes("1"), Bytes.toBytes("2"), Bytes.toBytes("3"),
135    Bytes.toBytes("4"), Bytes.toBytes("5"), Bytes.toBytes("6"), Bytes.toBytes("7") };
136  private static MultipleColumnPrefixFilter L_MCP_FILTER =
137      new MultipleColumnPrefixFilter(L_PREFIXES);
138  private static String STR_L_MCP_FILTER =
139      L_MCP_FILTER.getClass().getSimpleName() + " (5/8): [0, 1, 2, 3, 4]";
140
141  private static int PAGE_SIZE = 9;
142  private static PageFilter PAGE_FILTER = new PageFilter(PAGE_SIZE);
143  private static String STR_PAGE_FILTER = PAGE_FILTER.getClass().getSimpleName() + " " + PAGE_SIZE;
144
145  private static SkipFilter SKIP_FILTER = new SkipFilter(L_TS_FILTER);
146  private static String STR_SKIP_FILTER =
147      SKIP_FILTER.getClass().getSimpleName() + " " + STR_L_TS_FILTER;
148
149  private static WhileMatchFilter WHILE_FILTER = new WhileMatchFilter(L_TS_FILTER);
150  private static String STR_WHILE_FILTER =
151      WHILE_FILTER.getClass().getSimpleName() + " " + STR_L_TS_FILTER;
152
153  private static KeyOnlyFilter KEY_ONLY_FILTER = new KeyOnlyFilter();
154  private static String STR_KEY_ONLY_FILTER = KEY_ONLY_FILTER.getClass().getSimpleName();
155
156  private static FirstKeyOnlyFilter FIRST_KEY_ONLY_FILTER = new FirstKeyOnlyFilter();
157  private static String STR_FIRST_KEY_ONLY_FILTER =
158      FIRST_KEY_ONLY_FILTER.getClass().getSimpleName();
159
160  private static CompareOp CMP_OP = CompareOp.EQUAL;
161  private static byte[] CMP_VALUE = Bytes.toBytes("value");
162  private static BinaryComparator BC = new BinaryComparator(CMP_VALUE);
163  private static DependentColumnFilter DC_FILTER =
164      new DependentColumnFilter(FAMILY, QUALIFIER, true, CMP_OP, BC);
165  private static String STR_DC_FILTER = String.format(
166      "%s (%s, %s, %s, %s, %s)", DC_FILTER.getClass().getSimpleName(),
167      Bytes.toStringBinary(FAMILY), Bytes.toStringBinary(QUALIFIER), true,
168      CMP_OP.name(), Bytes.toStringBinary(BC.getValue()));
169
170  private static FamilyFilter FAMILY_FILTER = new FamilyFilter(CMP_OP, BC);
171  private static String STR_FAMILY_FILTER =
172      FAMILY_FILTER.getClass().getSimpleName() + " (EQUAL, value)";
173
174  private static QualifierFilter QUALIFIER_FILTER = new QualifierFilter(CMP_OP, BC);
175  private static String STR_QUALIFIER_FILTER =
176      QUALIFIER_FILTER.getClass().getSimpleName() + " (EQUAL, value)";
177
178  private static RowFilter ROW_FILTER = new RowFilter(CMP_OP, BC);
179  private static String STR_ROW_FILTER = ROW_FILTER.getClass().getSimpleName() + " (EQUAL, value)";
180
181  private static ValueFilter VALUE_FILTER = new ValueFilter(CMP_OP, BC);
182  private static String STR_VALUE_FILTER =
183      VALUE_FILTER.getClass().getSimpleName() + " (EQUAL, value)";
184
185  private static SingleColumnValueFilter SCV_FILTER =
186      new SingleColumnValueFilter(FAMILY, QUALIFIER, CMP_OP, CMP_VALUE);
187  private static String STR_SCV_FILTER = String.format("%s (%s, %s, %s, %s)",
188      SCV_FILTER.getClass().getSimpleName(), Bytes.toStringBinary(FAMILY),
189      Bytes.toStringBinary(QUALIFIER), CMP_OP.name(),
190      Bytes.toStringBinary(CMP_VALUE));
191
192  private static SingleColumnValueExcludeFilter SCVE_FILTER =
193      new SingleColumnValueExcludeFilter(FAMILY, QUALIFIER, CMP_OP, CMP_VALUE);
194  private static String STR_SCVE_FILTER = String.format("%s (%s, %s, %s, %s)",
195      SCVE_FILTER.getClass().getSimpleName(), Bytes.toStringBinary(FAMILY),
196      Bytes.toStringBinary(QUALIFIER), CMP_OP.name(), Bytes.toStringBinary(CMP_VALUE));
197
198  private static FilterList AND_FILTER_LIST = new FilterList(
199      Operator.MUST_PASS_ALL, Arrays.asList((Filter) TS_FILTER, L_TS_FILTER, CR_FILTER));
200  private static String STR_AND_FILTER_LIST = String.format(
201      "%s AND (3/3): [%s, %s, %s]", AND_FILTER_LIST.getClass().getSimpleName(),
202      STR_TS_FILTER, STR_L_TS_FILTER, STR_CR_FILTER);
203
204  private static FilterList OR_FILTER_LIST = new FilterList(
205      Operator.MUST_PASS_ONE, Arrays.asList((Filter) TS_FILTER, L_TS_FILTER, CR_FILTER));
206  private static String STR_OR_FILTER_LIST = String.format(
207      "%s OR (3/3): [%s, %s, %s]", AND_FILTER_LIST.getClass().getSimpleName(),
208      STR_TS_FILTER, STR_L_TS_FILTER, STR_CR_FILTER);
209
210  private static FilterList L_FILTER_LIST = new FilterList(
211      Arrays.asList((Filter) TS_FILTER, L_TS_FILTER, CR_FILTER, COL_PRE_FILTER,
212          CCG_FILTER, CP_FILTER, PREFIX_FILTER, PAGE_FILTER));
213  private static String STR_L_FILTER_LIST = String.format(
214      "%s AND (5/8): [%s, %s, %s, %s, %s, %s]",
215      L_FILTER_LIST.getClass().getSimpleName(), STR_TS_FILTER, STR_L_TS_FILTER,
216      STR_CR_FILTER, STR_COL_PRE_FILTER, STR_CCG_FILTER, STR_CP_FILTER);
217
218  private static Filter[] FILTERS = {
219    TS_FILTER,             // TimestampsFilter
220    L_TS_FILTER,           // TimestampsFilter
221    COL_PRE_FILTER,        // ColumnPrefixFilter
222    CP_FILTER,             // ColumnPaginationFilter
223    CR_FILTER,             // ColumnRangeFilter
224    CCG_FILTER,            // ColumnCountGetFilter
225    IS_FILTER,             // InclusiveStopFilter
226    PREFIX_FILTER,         // PrefixFilter
227    PAGE_FILTER,           // PageFilter
228    SKIP_FILTER,           // SkipFilter
229    WHILE_FILTER,          // WhileMatchFilter
230    KEY_ONLY_FILTER,       // KeyOnlyFilter
231    FIRST_KEY_ONLY_FILTER, // FirstKeyOnlyFilter
232    MCP_FILTER,            // MultipleColumnPrefixFilter
233    L_MCP_FILTER,          // MultipleColumnPrefixFilter
234    DC_FILTER,             // DependentColumnFilter
235    FAMILY_FILTER,         // FamilyFilter
236    QUALIFIER_FILTER,      // QualifierFilter
237    ROW_FILTER,            // RowFilter
238    VALUE_FILTER,          // ValueFilter
239    SCV_FILTER,            // SingleColumnValueFilter
240    SCVE_FILTER,           // SingleColumnValueExcludeFilter
241    AND_FILTER_LIST,       // FilterList
242    OR_FILTER_LIST,        // FilterList
243    L_FILTER_LIST,         // FilterList
244  };
245
246  private static String[] FILTERS_INFO = {
247    STR_TS_FILTER,             // TimestampsFilter
248    STR_L_TS_FILTER,           // TimestampsFilter
249    STR_COL_PRE_FILTER,        // ColumnPrefixFilter
250    STR_CP_FILTER,             // ColumnPaginationFilter
251    STR_CR_FILTER,             // ColumnRangeFilter
252    STR_CCG_FILTER,            // ColumnCountGetFilter
253    STR_IS_FILTER,             // InclusiveStopFilter
254    STR_PREFIX_FILTER,         // PrefixFilter
255    STR_PAGE_FILTER,           // PageFilter
256    STR_SKIP_FILTER,           // SkipFilter
257    STR_WHILE_FILTER,          // WhileMatchFilter
258    STR_KEY_ONLY_FILTER,       // KeyOnlyFilter
259    STR_FIRST_KEY_ONLY_FILTER, // FirstKeyOnlyFilter
260    STR_MCP_FILTER,            // MultipleColumnPrefixFilter
261    STR_L_MCP_FILTER,          // MultipleColumnPrefixFilter
262    STR_DC_FILTER,             // DependentColumnFilter
263    STR_FAMILY_FILTER,         // FamilyFilter
264    STR_QUALIFIER_FILTER,      // QualifierFilter
265    STR_ROW_FILTER,            // RowFilter
266    STR_VALUE_FILTER,          // ValueFilter
267    STR_SCV_FILTER,            // SingleColumnValueFilter
268    STR_SCVE_FILTER,           // SingleColumnValueExcludeFilter
269    STR_AND_FILTER_LIST,       // FilterList
270    STR_OR_FILTER_LIST,        // FilterList
271    STR_L_FILTER_LIST,         // FilterList
272  };
273
274  static {
275    assertEquals("The sizes of static arrays do not match: "
276        + "[FILTERS: %d <=> FILTERS_INFO: %d]",
277        FILTERS.length, FILTERS_INFO.length);
278  }
279
280  /**
281   * Test the client Operations' JSON encoding to ensure that produced JSON is
282   * parseable and that the details are present and not corrupted.
283   *
284   * @throws IOException if the JSON conversion fails
285   */
286  @Test
287  public void testOperationJSON() throws IOException {
288    // produce a Scan Operation
289    Scan scan = new Scan(ROW);
290    scan.addColumn(FAMILY, QUALIFIER);
291    // get its JSON representation, and parse it
292    String json = scan.toJSON();
293    Map<String, Object> parsedJSON = mapper.readValue(json, HashMap.class);
294    // check for the row
295    assertEquals("startRow incorrect in Scan.toJSON()",
296        Bytes.toStringBinary(ROW), parsedJSON.get("startRow"));
297    // check for the family and the qualifier.
298    List familyInfo = (List) ((Map) parsedJSON.get("families")).get(
299        Bytes.toStringBinary(FAMILY));
300    assertNotNull("Family absent in Scan.toJSON()", familyInfo);
301    assertEquals("Qualifier absent in Scan.toJSON()", 1, familyInfo.size());
302    assertEquals("Qualifier incorrect in Scan.toJSON()",
303        Bytes.toStringBinary(QUALIFIER),
304        familyInfo.get(0));
305
306    // produce a Get Operation
307    Get get = new Get(ROW);
308    get.addColumn(FAMILY, QUALIFIER);
309    // get its JSON representation, and parse it
310    json = get.toJSON();
311    parsedJSON = mapper.readValue(json, HashMap.class);
312    // check for the row
313    assertEquals("row incorrect in Get.toJSON()",
314        Bytes.toStringBinary(ROW), parsedJSON.get("row"));
315    // check for the family and the qualifier.
316    familyInfo = (List) ((Map) parsedJSON.get("families")).get(
317        Bytes.toStringBinary(FAMILY));
318    assertNotNull("Family absent in Get.toJSON()", familyInfo);
319    assertEquals("Qualifier absent in Get.toJSON()", 1, familyInfo.size());
320    assertEquals("Qualifier incorrect in Get.toJSON()",
321        Bytes.toStringBinary(QUALIFIER),
322        familyInfo.get(0));
323
324    // produce a Put operation
325    Put put = new Put(ROW);
326    put.addColumn(FAMILY, QUALIFIER, VALUE);
327    // get its JSON representation, and parse it
328    json = put.toJSON();
329    parsedJSON = mapper.readValue(json, HashMap.class);
330    // check for the row
331    assertEquals("row absent in Put.toJSON()",
332        Bytes.toStringBinary(ROW), parsedJSON.get("row"));
333    // check for the family and the qualifier.
334    familyInfo = (List) ((Map) parsedJSON.get("families")).get(
335        Bytes.toStringBinary(FAMILY));
336    assertNotNull("Family absent in Put.toJSON()", familyInfo);
337    assertEquals("KeyValue absent in Put.toJSON()", 1, familyInfo.size());
338    Map kvMap = (Map) familyInfo.get(0);
339    assertEquals("Qualifier incorrect in Put.toJSON()",
340        Bytes.toStringBinary(QUALIFIER),
341        kvMap.get("qualifier"));
342    assertEquals("Value length incorrect in Put.toJSON()",
343        VALUE.length, kvMap.get("vlen"));
344
345    // produce a Delete operation
346    Delete delete = new Delete(ROW);
347    delete.addColumn(FAMILY, QUALIFIER);
348    // get its JSON representation, and parse it
349    json = delete.toJSON();
350    parsedJSON = mapper.readValue(json, HashMap.class);
351    // check for the row
352    assertEquals("row absent in Delete.toJSON()",
353        Bytes.toStringBinary(ROW), parsedJSON.get("row"));
354    // check for the family and the qualifier.
355    familyInfo = (List) ((Map) parsedJSON.get("families")).get(
356        Bytes.toStringBinary(FAMILY));
357    assertNotNull("Family absent in Delete.toJSON()", familyInfo);
358    assertEquals("KeyValue absent in Delete.toJSON()", 1, familyInfo.size());
359    kvMap = (Map) familyInfo.get(0);
360    assertEquals("Qualifier incorrect in Delete.toJSON()",
361        Bytes.toStringBinary(QUALIFIER), kvMap.get("qualifier"));
362  }
363
364  @Test
365  public void testPutCreationWithByteBuffer() {
366    Put p = new Put(ROW);
367    List<Cell> c = p.get(FAMILY, QUALIFIER);
368    Assert.assertEquals(0, c.size());
369    Assert.assertEquals(HConstants.LATEST_TIMESTAMP, p.getTimestamp());
370
371    p.addColumn(FAMILY, ByteBuffer.wrap(QUALIFIER), 1984L, ByteBuffer.wrap(VALUE));
372    c = p.get(FAMILY, QUALIFIER);
373    Assert.assertEquals(1, c.size());
374    Assert.assertEquals(1984L, c.get(0).getTimestamp());
375    Assert.assertArrayEquals(VALUE, CellUtil.cloneValue(c.get(0)));
376    Assert.assertEquals(HConstants.LATEST_TIMESTAMP, p.getTimestamp());
377    Assert.assertEquals(0, CellComparatorImpl.COMPARATOR.compare(c.get(0), new KeyValue(c.get(0))));
378
379    p = new Put(ROW);
380    p.addColumn(FAMILY, ByteBuffer.wrap(QUALIFIER), 2013L, null);
381    c = p.get(FAMILY, QUALIFIER);
382    Assert.assertEquals(1, c.size());
383    Assert.assertEquals(2013L, c.get(0).getTimestamp());
384    Assert.assertArrayEquals(new byte[]{}, CellUtil.cloneValue(c.get(0)));
385    Assert.assertEquals(HConstants.LATEST_TIMESTAMP, p.getTimestamp());
386    Assert.assertEquals(0, CellComparatorImpl.COMPARATOR.compare(c.get(0), new KeyValue(c.get(0))));
387
388    p = new Put(ByteBuffer.wrap(ROW));
389    p.addColumn(FAMILY, ByteBuffer.wrap(QUALIFIER), 2001L, null);
390    c = p.get(FAMILY, QUALIFIER);
391    Assert.assertEquals(1, c.size());
392    Assert.assertEquals(2001L, c.get(0).getTimestamp());
393    Assert.assertArrayEquals(new byte[]{}, CellUtil.cloneValue(c.get(0)));
394    Assert.assertArrayEquals(ROW, CellUtil.cloneRow(c.get(0)));
395    Assert.assertEquals(HConstants.LATEST_TIMESTAMP, p.getTimestamp());
396    Assert.assertEquals(0, CellComparatorImpl.COMPARATOR.compare(c.get(0), new KeyValue(c.get(0))));
397
398    p = new Put(ByteBuffer.wrap(ROW), 1970L);
399    p.addColumn(FAMILY, ByteBuffer.wrap(QUALIFIER), 2001L, null);
400    c = p.get(FAMILY, QUALIFIER);
401    Assert.assertEquals(1, c.size());
402    Assert.assertEquals(2001L, c.get(0).getTimestamp());
403    Assert.assertArrayEquals(new byte[]{}, CellUtil.cloneValue(c.get(0)));
404    Assert.assertArrayEquals(ROW, CellUtil.cloneRow(c.get(0)));
405    Assert.assertEquals(1970L, p.getTimestamp());
406    Assert.assertEquals(0, CellComparatorImpl.COMPARATOR.compare(c.get(0), new KeyValue(c.get(0))));
407  }
408
409  @Test
410  @SuppressWarnings("rawtypes")
411  public void testOperationSubClassMethodsAreBuilderStyle() {
412    /* All Operation subclasses should have a builder style setup where setXXX/addXXX methods
413     * can be chainable together:
414     * . For example:
415     * Scan scan = new Scan()
416     *     .setFoo(foo)
417     *     .setBar(bar)
418     *     .setBuz(buz)
419     *
420     * This test ensures that all methods starting with "set" returns the declaring object
421     */
422
423    // TODO: We should ensure all subclasses of Operation is checked.
424    Class[] classes = new Class[] {
425      Operation.class,
426      OperationWithAttributes.class,
427      Mutation.class,
428      Query.class,
429      Delete.class,
430      Increment.class,
431      Append.class,
432      Put.class,
433      Get.class,
434      Scan.class};
435
436    BuilderStyleTest.assertClassesAreBuilderStyle(classes);
437  }
438}