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}