View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.client;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertNotNull;
22  
23  import java.io.IOException;
24  import java.lang.reflect.Type;
25  import java.nio.ByteBuffer;
26  import java.util.Arrays;
27  import java.util.List;
28  import java.util.Map;
29  
30  import org.apache.hadoop.hbase.Cell;
31  import org.apache.hadoop.hbase.CellUtil;
32  import org.apache.hadoop.hbase.HConstants;
33  import org.apache.hadoop.hbase.KeyValue;
34  import org.apache.hadoop.hbase.filter.BinaryComparator;
35  import org.apache.hadoop.hbase.filter.ColumnCountGetFilter;
36  import org.apache.hadoop.hbase.filter.ColumnPaginationFilter;
37  import org.apache.hadoop.hbase.filter.ColumnPrefixFilter;
38  import org.apache.hadoop.hbase.filter.ColumnRangeFilter;
39  import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
40  import org.apache.hadoop.hbase.filter.DependentColumnFilter;
41  import org.apache.hadoop.hbase.filter.FamilyFilter;
42  import org.apache.hadoop.hbase.filter.Filter;
43  import org.apache.hadoop.hbase.filter.FilterList;
44  import org.apache.hadoop.hbase.filter.FilterList.Operator;
45  import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
46  import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
47  import org.apache.hadoop.hbase.filter.KeyOnlyFilter;
48  import org.apache.hadoop.hbase.filter.MultipleColumnPrefixFilter;
49  import org.apache.hadoop.hbase.filter.PageFilter;
50  import org.apache.hadoop.hbase.filter.PrefixFilter;
51  import org.apache.hadoop.hbase.filter.QualifierFilter;
52  import org.apache.hadoop.hbase.filter.RowFilter;
53  import org.apache.hadoop.hbase.filter.SingleColumnValueExcludeFilter;
54  import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
55  import org.apache.hadoop.hbase.filter.SkipFilter;
56  import org.apache.hadoop.hbase.filter.TimestampsFilter;
57  import org.apache.hadoop.hbase.filter.ValueFilter;
58  import org.apache.hadoop.hbase.filter.WhileMatchFilter;
59  import org.apache.hadoop.hbase.testclassification.SmallTests;
60  import org.apache.hadoop.hbase.util.BuilderStyleTest;
61  import org.apache.hadoop.hbase.util.Bytes;
62  import org.apache.hadoop.hbase.util.GsonUtil;
63  import org.apache.hbase.thirdparty.com.google.gson.Gson;
64  import org.apache.hbase.thirdparty.com.google.gson.reflect.TypeToken;
65  import org.junit.Assert;
66  import org.junit.Test;
67  import org.junit.experimental.categories.Category;
68  
69  /**
70   * Run tests that use the functionality of the Operation superclass for
71   * Puts, Gets, Deletes, Scans, and MultiPuts.
72   */
73  @Category(SmallTests.class)
74  public class TestOperation {
75    private static byte [] ROW = Bytes.toBytes("testRow");
76    private static byte [] FAMILY = Bytes.toBytes("testFamily");
77    private static byte [] QUALIFIER = Bytes.toBytes("testQualifier");
78    private static byte [] VALUE = Bytes.toBytes("testValue");
79  
80    private static Gson GSON = GsonUtil.createGson().create();
81  
82    private static List<Long> TS_LIST = Arrays.asList(2L, 3L, 5L);
83    private static TimestampsFilter TS_FILTER = new TimestampsFilter(TS_LIST);
84    private static String STR_TS_FILTER = TS_FILTER.getClass().getSimpleName() + " (3/3): [2, 3, 5]";
85  
86    private static List<Long> L_TS_LIST = Arrays.asList(0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L);
87    private static TimestampsFilter L_TS_FILTER = new TimestampsFilter(L_TS_LIST);
88    private static String STR_L_TS_FILTER =
89        L_TS_FILTER.getClass().getSimpleName() + " (5/11): [0, 1, 2, 3, 4]";
90  
91    private static String COL_NAME_1 = "col1";
92    private static ColumnPrefixFilter COL_PRE_FILTER =
93        new ColumnPrefixFilter(Bytes.toBytes(COL_NAME_1));
94    private static String STR_COL_PRE_FILTER =
95        COL_PRE_FILTER.getClass().getSimpleName() + " " + COL_NAME_1;
96  
97    private static String COL_NAME_2 = "col2";
98    private static ColumnRangeFilter CR_FILTER =
99        new ColumnRangeFilter(Bytes.toBytes(COL_NAME_1), true, Bytes.toBytes(COL_NAME_2), false);
100   private static String STR_CR_FILTER = CR_FILTER.getClass().getSimpleName()
101       + " [" + COL_NAME_1 + ", " + COL_NAME_2 + ")";
102 
103   private static int COL_COUNT = 9;
104   private static ColumnCountGetFilter CCG_FILTER = new ColumnCountGetFilter(COL_COUNT);
105   private static String STR_CCG_FILTER = CCG_FILTER.getClass().getSimpleName() + " " + COL_COUNT;
106 
107   private static int LIMIT = 3;
108   private static int OFFSET = 4;
109   private static ColumnPaginationFilter CP_FILTER = new ColumnPaginationFilter(LIMIT, OFFSET);
110   private static String STR_CP_FILTER = CP_FILTER.getClass().getSimpleName()
111       + " (" + LIMIT + ", " + OFFSET + ")";
112 
113   private static String STOP_ROW_KEY = "stop";
114   private static InclusiveStopFilter IS_FILTER =
115       new InclusiveStopFilter(Bytes.toBytes(STOP_ROW_KEY));
116   private static String STR_IS_FILTER =
117       IS_FILTER.getClass().getSimpleName() + " " + STOP_ROW_KEY;
118 
119   private static String PREFIX = "prefix";
120   private static PrefixFilter PREFIX_FILTER = new PrefixFilter(Bytes.toBytes(PREFIX));
121   private static String STR_PREFIX_FILTER = "PrefixFilter " + PREFIX;
122 
123   private static byte[][] PREFIXES = { Bytes.toBytes("0"), Bytes.toBytes("1"), Bytes.toBytes("2") };
124   private static MultipleColumnPrefixFilter MCP_FILTER = new MultipleColumnPrefixFilter(PREFIXES);
125   private static String STR_MCP_FILTER =
126       MCP_FILTER.getClass().getSimpleName() + " (3/3): [0, 1, 2]";
127 
128   private static byte[][] L_PREFIXES = {
129     Bytes.toBytes("0"), Bytes.toBytes("1"), Bytes.toBytes("2"), Bytes.toBytes("3"),
130     Bytes.toBytes("4"), Bytes.toBytes("5"), Bytes.toBytes("6"), Bytes.toBytes("7") };
131   private static MultipleColumnPrefixFilter L_MCP_FILTER =
132       new MultipleColumnPrefixFilter(L_PREFIXES);
133   private static String STR_L_MCP_FILTER =
134       L_MCP_FILTER.getClass().getSimpleName() + " (5/8): [0, 1, 2, 3, 4]";
135 
136   private static int PAGE_SIZE = 9;
137   private static PageFilter PAGE_FILTER = new PageFilter(PAGE_SIZE);
138   private static String STR_PAGE_FILTER = PAGE_FILTER.getClass().getSimpleName() + " " + PAGE_SIZE;
139 
140   private static SkipFilter SKIP_FILTER = new SkipFilter(L_TS_FILTER);
141   private static String STR_SKIP_FILTER =
142       SKIP_FILTER.getClass().getSimpleName() + " " + STR_L_TS_FILTER;
143 
144   private static WhileMatchFilter WHILE_FILTER = new WhileMatchFilter(L_TS_FILTER);
145   private static String STR_WHILE_FILTER =
146       WHILE_FILTER.getClass().getSimpleName() + " " + STR_L_TS_FILTER;
147 
148   private static KeyOnlyFilter KEY_ONLY_FILTER = new KeyOnlyFilter();
149   private static String STR_KEY_ONLY_FILTER = KEY_ONLY_FILTER.getClass().getSimpleName();
150 
151   private static FirstKeyOnlyFilter FIRST_KEY_ONLY_FILTER = new FirstKeyOnlyFilter();
152   private static String STR_FIRST_KEY_ONLY_FILTER =
153       FIRST_KEY_ONLY_FILTER.getClass().getSimpleName();
154 
155   private static CompareOp CMP_OP = CompareOp.EQUAL;
156   private static byte[] CMP_VALUE = Bytes.toBytes("value");
157   private static BinaryComparator BC = new BinaryComparator(CMP_VALUE);
158   private static DependentColumnFilter DC_FILTER =
159       new DependentColumnFilter(FAMILY, QUALIFIER, true, CMP_OP, BC);
160   private static String STR_DC_FILTER = String.format(
161       "%s (%s, %s, %s, %s, %s)", DC_FILTER.getClass().getSimpleName(),
162       Bytes.toStringBinary(FAMILY), Bytes.toStringBinary(QUALIFIER), true,
163       CMP_OP.name(), Bytes.toStringBinary(BC.getValue()));
164 
165   private static FamilyFilter FAMILY_FILTER = new FamilyFilter(CMP_OP, BC);
166   private static String STR_FAMILY_FILTER =
167       FAMILY_FILTER.getClass().getSimpleName() + " (EQUAL, value)";
168 
169   private static QualifierFilter QUALIFIER_FILTER = new QualifierFilter(CMP_OP, BC);
170   private static String STR_QUALIFIER_FILTER =
171       QUALIFIER_FILTER.getClass().getSimpleName() + " (EQUAL, value)";
172 
173   private static RowFilter ROW_FILTER = new RowFilter(CMP_OP, BC);
174   private static String STR_ROW_FILTER = ROW_FILTER.getClass().getSimpleName() + " (EQUAL, value)";
175 
176   private static ValueFilter VALUE_FILTER = new ValueFilter(CMP_OP, BC);
177   private static String STR_VALUE_FILTER =
178       VALUE_FILTER.getClass().getSimpleName() + " (EQUAL, value)";
179 
180   private static SingleColumnValueFilter SCV_FILTER =
181       new SingleColumnValueFilter(FAMILY, QUALIFIER, CMP_OP, CMP_VALUE);
182   private static String STR_SCV_FILTER = String.format("%s (%s, %s, %s, %s)",
183       SCV_FILTER.getClass().getSimpleName(), Bytes.toStringBinary(FAMILY),
184       Bytes.toStringBinary(QUALIFIER), CMP_OP.name(),
185       Bytes.toStringBinary(CMP_VALUE));
186 
187   private static SingleColumnValueExcludeFilter SCVE_FILTER =
188       new SingleColumnValueExcludeFilter(FAMILY, QUALIFIER, CMP_OP, CMP_VALUE);
189   private static String STR_SCVE_FILTER = String.format("%s (%s, %s, %s, %s)",
190       SCVE_FILTER.getClass().getSimpleName(), Bytes.toStringBinary(FAMILY),
191       Bytes.toStringBinary(QUALIFIER), CMP_OP.name(), Bytes.toStringBinary(CMP_VALUE));
192 
193   private static FilterList AND_FILTER_LIST = new FilterList(
194       Operator.MUST_PASS_ALL, Arrays.asList((Filter) TS_FILTER, L_TS_FILTER, CR_FILTER));
195   private static String STR_AND_FILTER_LIST = String.format(
196       "%s AND (3/3): [%s, %s, %s]", AND_FILTER_LIST.getClass().getSimpleName(),
197       STR_TS_FILTER, STR_L_TS_FILTER, STR_CR_FILTER);
198 
199   private static FilterList OR_FILTER_LIST = new FilterList(
200       Operator.MUST_PASS_ONE, Arrays.asList((Filter) TS_FILTER, L_TS_FILTER, CR_FILTER));
201   private static String STR_OR_FILTER_LIST = String.format(
202       "%s OR (3/3): [%s, %s, %s]", AND_FILTER_LIST.getClass().getSimpleName(),
203       STR_TS_FILTER, STR_L_TS_FILTER, STR_CR_FILTER);
204 
205   private static FilterList L_FILTER_LIST = new FilterList(
206       Arrays.asList((Filter) TS_FILTER, L_TS_FILTER, CR_FILTER, COL_PRE_FILTER,
207           CCG_FILTER, CP_FILTER, PREFIX_FILTER, PAGE_FILTER));
208   private static String STR_L_FILTER_LIST = String.format(
209       "%s AND (5/8): [%s, %s, %s, %s, %s, %s]",
210       L_FILTER_LIST.getClass().getSimpleName(), STR_TS_FILTER, STR_L_TS_FILTER,
211       STR_CR_FILTER, STR_COL_PRE_FILTER, STR_CCG_FILTER, STR_CP_FILTER);
212 
213   private static Filter[] FILTERS = {
214     TS_FILTER,             // TimestampsFilter
215     L_TS_FILTER,           // TimestampsFilter
216     COL_PRE_FILTER,        // ColumnPrefixFilter
217     CP_FILTER,             // ColumnPaginationFilter
218     CR_FILTER,             // ColumnRangeFilter
219     CCG_FILTER,            // ColumnCountGetFilter
220     IS_FILTER,             // InclusiveStopFilter
221     PREFIX_FILTER,         // PrefixFilter
222     PAGE_FILTER,           // PageFilter
223     SKIP_FILTER,           // SkipFilter
224     WHILE_FILTER,          // WhileMatchFilter
225     KEY_ONLY_FILTER,       // KeyOnlyFilter
226     FIRST_KEY_ONLY_FILTER, // FirstKeyOnlyFilter
227     MCP_FILTER,            // MultipleColumnPrefixFilter
228     L_MCP_FILTER,          // MultipleColumnPrefixFilter
229     DC_FILTER,             // DependentColumnFilter
230     FAMILY_FILTER,         // FamilyFilter
231     QUALIFIER_FILTER,      // QualifierFilter
232     ROW_FILTER,            // RowFilter
233     VALUE_FILTER,          // ValueFilter
234     SCV_FILTER,            // SingleColumnValueFilter
235     SCVE_FILTER,           // SingleColumnValueExcludeFilter
236     AND_FILTER_LIST,       // FilterList
237     OR_FILTER_LIST,        // FilterList
238     L_FILTER_LIST,         // FilterList
239   };
240 
241   private static String[] FILTERS_INFO = {
242     STR_TS_FILTER,             // TimestampsFilter
243     STR_L_TS_FILTER,           // TimestampsFilter
244     STR_COL_PRE_FILTER,        // ColumnPrefixFilter
245     STR_CP_FILTER,             // ColumnPaginationFilter
246     STR_CR_FILTER,             // ColumnRangeFilter
247     STR_CCG_FILTER,            // ColumnCountGetFilter
248     STR_IS_FILTER,             // InclusiveStopFilter
249     STR_PREFIX_FILTER,         // PrefixFilter
250     STR_PAGE_FILTER,           // PageFilter
251     STR_SKIP_FILTER,           // SkipFilter
252     STR_WHILE_FILTER,          // WhileMatchFilter
253     STR_KEY_ONLY_FILTER,       // KeyOnlyFilter
254     STR_FIRST_KEY_ONLY_FILTER, // FirstKeyOnlyFilter
255     STR_MCP_FILTER,            // MultipleColumnPrefixFilter
256     STR_L_MCP_FILTER,          // MultipleColumnPrefixFilter
257     STR_DC_FILTER,             // DependentColumnFilter
258     STR_FAMILY_FILTER,         // FamilyFilter
259     STR_QUALIFIER_FILTER,      // QualifierFilter
260     STR_ROW_FILTER,            // RowFilter
261     STR_VALUE_FILTER,          // ValueFilter
262     STR_SCV_FILTER,            // SingleColumnValueFilter
263     STR_SCVE_FILTER,           // SingleColumnValueExcludeFilter
264     STR_AND_FILTER_LIST,       // FilterList
265     STR_OR_FILTER_LIST,        // FilterList
266     STR_L_FILTER_LIST,         // FilterList
267   };
268 
269   static {
270     assertEquals("The sizes of static arrays do not match: "
271         + "[FILTERS: %d <=> FILTERS_INFO: %d]",
272         FILTERS.length, FILTERS_INFO.length);
273   }
274 
275   /**
276    * Test the client Operations' JSON encoding to ensure that produced JSON is
277    * parseable and that the details are present and not corrupted.
278    *
279    * @throws IOException if the JSON conversion fails
280    */
281   @Test
282   public void testOperationJSON() throws IOException {
283     // produce a Scan Operation
284     Scan scan = new Scan(ROW);
285     scan.addColumn(FAMILY, QUALIFIER);
286     // get its JSON representation, and parse it
287     String json = scan.toJSON();
288     Type typeOfHashMap = new TypeToken<Map<String, Object>>() {
289     }.getType();
290     Map<String, Object> parsedJSON = GSON.fromJson(json, typeOfHashMap);
291     // check for the row
292     assertEquals("startRow incorrect in Scan.toJSON()",
293         Bytes.toStringBinary(ROW), parsedJSON.get("startRow"));
294     // check for the family and the qualifier.
295     List familyInfo = (List) ((Map) parsedJSON.get("families")).get(
296         Bytes.toStringBinary(FAMILY));
297     assertNotNull("Family absent in Scan.toJSON()", familyInfo);
298     assertEquals("Qualifier absent in Scan.toJSON()", 1, familyInfo.size());
299     assertEquals("Qualifier incorrect in Scan.toJSON()",
300         Bytes.toStringBinary(QUALIFIER),
301         familyInfo.get(0));
302 
303     // produce a Get Operation
304     Get get = new Get(ROW);
305     get.addColumn(FAMILY, QUALIFIER);
306     // get its JSON representation, and parse it
307     json = get.toJSON();
308     parsedJSON = GSON.fromJson(json, typeOfHashMap);
309     // check for the row
310     assertEquals("row incorrect in Get.toJSON()",
311         Bytes.toStringBinary(ROW), parsedJSON.get("row"));
312     // check for the family and the qualifier.
313     familyInfo = (List) ((Map) parsedJSON.get("families")).get(
314         Bytes.toStringBinary(FAMILY));
315     assertNotNull("Family absent in Get.toJSON()", familyInfo);
316     assertEquals("Qualifier absent in Get.toJSON()", 1, familyInfo.size());
317     assertEquals("Qualifier incorrect in Get.toJSON()",
318         Bytes.toStringBinary(QUALIFIER),
319         familyInfo.get(0));
320 
321     // produce a Put operation
322     Put put = new Put(ROW);
323     put.add(FAMILY, QUALIFIER, VALUE);
324     // get its JSON representation, and parse it
325     json = put.toJSON();
326     parsedJSON = GSON.fromJson(json, typeOfHashMap);
327     // check for the row
328     assertEquals("row absent in Put.toJSON()",
329         Bytes.toStringBinary(ROW), parsedJSON.get("row"));
330     // check for the family and the qualifier.
331     familyInfo = (List) ((Map) parsedJSON.get("families")).get(
332         Bytes.toStringBinary(FAMILY));
333     assertNotNull("Family absent in Put.toJSON()", familyInfo);
334     assertEquals("KeyValue absent in Put.toJSON()", 1, familyInfo.size());
335     Map kvMap = (Map) familyInfo.get(0);
336     assertEquals("Qualifier incorrect in Put.toJSON()",
337         Bytes.toStringBinary(QUALIFIER),
338         kvMap.get("qualifier"));
339     assertEquals("Value length incorrect in Put.toJSON()",
340       VALUE.length, ((Number) kvMap.get("vlen")).intValue());
341 
342     // produce a Delete operation
343     Delete delete = new Delete(ROW);
344     delete.deleteColumn(FAMILY, QUALIFIER);
345     // get its JSON representation, and parse it
346     json = delete.toJSON();
347     parsedJSON = GSON.fromJson(json, typeOfHashMap);
348     // check for the row
349     assertEquals("row absent in Delete.toJSON()",
350         Bytes.toStringBinary(ROW), parsedJSON.get("row"));
351     // check for the family and the qualifier.
352     familyInfo = (List) ((Map) parsedJSON.get("families")).get(
353         Bytes.toStringBinary(FAMILY));
354     assertNotNull("Family absent in Delete.toJSON()", familyInfo);
355     assertEquals("KeyValue absent in Delete.toJSON()", 1, familyInfo.size());
356     kvMap = (Map) familyInfo.get(0);
357     assertEquals("Qualifier incorrect in Delete.toJSON()",
358         Bytes.toStringBinary(QUALIFIER), kvMap.get("qualifier"));
359   }
360 
361   @Test
362   public void testPutCreationWithByteBuffer() {
363     Put p = new Put(ROW);
364     List<Cell> c = p.get(FAMILY, QUALIFIER);
365     Assert.assertEquals(0, c.size());
366     Assert.assertEquals(HConstants.LATEST_TIMESTAMP, p.getTimeStamp());
367 
368     p.add(FAMILY, ByteBuffer.wrap(QUALIFIER), 1984L, ByteBuffer.wrap(VALUE));
369     c = p.get(FAMILY, QUALIFIER);
370     Assert.assertEquals(1, c.size());
371     Assert.assertEquals(1984L, c.get(0).getTimestamp());
372     Assert.assertArrayEquals(VALUE, CellUtil.cloneValue(c.get(0)));
373     Assert.assertEquals(HConstants.LATEST_TIMESTAMP, p.getTimeStamp());
374     Assert.assertEquals(0, KeyValue.COMPARATOR.compare(c.get(0), new KeyValue(c.get(0))));
375 
376     p = new Put(ROW);
377     p.add(FAMILY, ByteBuffer.wrap(QUALIFIER), 2013L, null);
378     c = p.get(FAMILY, QUALIFIER);
379     Assert.assertEquals(1, c.size());
380     Assert.assertEquals(2013L, c.get(0).getTimestamp());
381     Assert.assertArrayEquals(new byte[]{}, CellUtil.cloneValue(c.get(0)));
382     Assert.assertEquals(HConstants.LATEST_TIMESTAMP, p.getTimeStamp());
383     Assert.assertEquals(0, KeyValue.COMPARATOR.compare(c.get(0), new KeyValue(c.get(0))));
384 
385     p = new Put(ByteBuffer.wrap(ROW));
386     p.add(FAMILY, ByteBuffer.wrap(QUALIFIER), 2001L, null);
387     c = p.get(FAMILY, QUALIFIER);
388     Assert.assertEquals(1, c.size());
389     Assert.assertEquals(2001L, c.get(0).getTimestamp());
390     Assert.assertArrayEquals(new byte[]{}, CellUtil.cloneValue(c.get(0)));
391     Assert.assertArrayEquals(ROW, CellUtil.cloneRow(c.get(0)));
392     Assert.assertEquals(HConstants.LATEST_TIMESTAMP, p.getTimeStamp());
393     Assert.assertEquals(0, KeyValue.COMPARATOR.compare(c.get(0), new KeyValue(c.get(0))));
394 
395     p = new Put(ByteBuffer.wrap(ROW), 1970L);
396     p.add(FAMILY, ByteBuffer.wrap(QUALIFIER), 2001L, null);
397     c = p.get(FAMILY, QUALIFIER);
398     Assert.assertEquals(1, c.size());
399     Assert.assertEquals(2001L, c.get(0).getTimestamp());
400     Assert.assertArrayEquals(new byte[]{}, CellUtil.cloneValue(c.get(0)));
401     Assert.assertArrayEquals(ROW, CellUtil.cloneRow(c.get(0)));
402     Assert.assertEquals(1970L, p.getTimeStamp());
403     Assert.assertEquals(0, KeyValue.COMPARATOR.compare(c.get(0), new KeyValue(c.get(0))));
404   }
405 
406   @Test
407   @SuppressWarnings("rawtypes")
408   public void testOperationSubClassMethodsAreBuilderStyle() {
409     /* All Operation subclasses should have a builder style setup where setXXX/addXXX methods
410      * can be chainable together:
411      * . For example:
412      * Scan scan = new Scan()
413      *     .setFoo(foo)
414      *     .setBar(bar)
415      *     .setBuz(buz)
416      *
417      * This test ensures that all methods starting with "set" returns the declaring object
418      */
419 
420     // TODO: We should ensure all subclasses of Operation is checked.
421     Class[] classes = new Class[] {
422       Operation.class,
423       OperationWithAttributes.class,
424       Mutation.class,
425       Query.class,
426       Delete.class,
427       Increment.class,
428       Append.class,
429       Put.class,
430       Get.class,
431       Scan.class};
432 
433     BuilderStyleTest.assertClassesAreBuilderStyle(classes);
434   }
435 }