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 077 @ClassRule 078 public static final HBaseClassTestRule CLASS_RULE = 079 HBaseClassTestRule.forClass(TestOperation.class); 080 081 private static byte [] ROW = Bytes.toBytes("testRow"); 082 private static byte [] FAMILY = Bytes.toBytes("testFamily"); 083 private static byte [] QUALIFIER = Bytes.toBytes("testQualifier"); 084 private static byte [] VALUE = Bytes.toBytes("testValue"); 085 086 private static ObjectMapper mapper = new ObjectMapper(); 087 088 private static List<Long> TS_LIST = Arrays.asList(2L, 3L, 5L); 089 private static TimestampsFilter TS_FILTER = new TimestampsFilter(TS_LIST); 090 private static String STR_TS_FILTER = TS_FILTER.getClass().getSimpleName() + " (3/3): [2, 3, 5]"; 091 092 private static List<Long> L_TS_LIST = Arrays.asList(0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L); 093 private static TimestampsFilter L_TS_FILTER = new TimestampsFilter(L_TS_LIST); 094 private static String STR_L_TS_FILTER = 095 L_TS_FILTER.getClass().getSimpleName() + " (5/11): [0, 1, 2, 3, 4]"; 096 097 private static String COL_NAME_1 = "col1"; 098 private static ColumnPrefixFilter COL_PRE_FILTER = 099 new ColumnPrefixFilter(Bytes.toBytes(COL_NAME_1)); 100 private static String STR_COL_PRE_FILTER = 101 COL_PRE_FILTER.getClass().getSimpleName() + " " + COL_NAME_1; 102 103 private static String COL_NAME_2 = "col2"; 104 private static ColumnRangeFilter CR_FILTER = 105 new ColumnRangeFilter(Bytes.toBytes(COL_NAME_1), true, Bytes.toBytes(COL_NAME_2), false); 106 private static String STR_CR_FILTER = CR_FILTER.getClass().getSimpleName() 107 + " [" + COL_NAME_1 + ", " + COL_NAME_2 + ")"; 108 109 private static int COL_COUNT = 9; 110 private static ColumnCountGetFilter CCG_FILTER = new ColumnCountGetFilter(COL_COUNT); 111 private static String STR_CCG_FILTER = CCG_FILTER.getClass().getSimpleName() + " " + COL_COUNT; 112 113 private static int LIMIT = 3; 114 private static int OFFSET = 4; 115 private static ColumnPaginationFilter CP_FILTER = new ColumnPaginationFilter(LIMIT, OFFSET); 116 private static String STR_CP_FILTER = CP_FILTER.getClass().getSimpleName() 117 + " (" + LIMIT + ", " + OFFSET + ")"; 118 119 private static String STOP_ROW_KEY = "stop"; 120 private static InclusiveStopFilter IS_FILTER = 121 new InclusiveStopFilter(Bytes.toBytes(STOP_ROW_KEY)); 122 private static String STR_IS_FILTER = 123 IS_FILTER.getClass().getSimpleName() + " " + STOP_ROW_KEY; 124 125 private static String PREFIX = "prefix"; 126 private static PrefixFilter PREFIX_FILTER = new PrefixFilter(Bytes.toBytes(PREFIX)); 127 private static String STR_PREFIX_FILTER = "PrefixFilter " + PREFIX; 128 129 private static byte[][] PREFIXES = { Bytes.toBytes("0"), Bytes.toBytes("1"), Bytes.toBytes("2") }; 130 private static MultipleColumnPrefixFilter MCP_FILTER = new MultipleColumnPrefixFilter(PREFIXES); 131 private static String STR_MCP_FILTER = 132 MCP_FILTER.getClass().getSimpleName() + " (3/3): [0, 1, 2]"; 133 134 private static byte[][] L_PREFIXES = { 135 Bytes.toBytes("0"), Bytes.toBytes("1"), Bytes.toBytes("2"), Bytes.toBytes("3"), 136 Bytes.toBytes("4"), Bytes.toBytes("5"), Bytes.toBytes("6"), Bytes.toBytes("7") }; 137 private static MultipleColumnPrefixFilter L_MCP_FILTER = 138 new MultipleColumnPrefixFilter(L_PREFIXES); 139 private static String STR_L_MCP_FILTER = 140 L_MCP_FILTER.getClass().getSimpleName() + " (5/8): [0, 1, 2, 3, 4]"; 141 142 private static int PAGE_SIZE = 9; 143 private static PageFilter PAGE_FILTER = new PageFilter(PAGE_SIZE); 144 private static String STR_PAGE_FILTER = PAGE_FILTER.getClass().getSimpleName() + " " + PAGE_SIZE; 145 146 private static SkipFilter SKIP_FILTER = new SkipFilter(L_TS_FILTER); 147 private static String STR_SKIP_FILTER = 148 SKIP_FILTER.getClass().getSimpleName() + " " + STR_L_TS_FILTER; 149 150 private static WhileMatchFilter WHILE_FILTER = new WhileMatchFilter(L_TS_FILTER); 151 private static String STR_WHILE_FILTER = 152 WHILE_FILTER.getClass().getSimpleName() + " " + STR_L_TS_FILTER; 153 154 private static KeyOnlyFilter KEY_ONLY_FILTER = new KeyOnlyFilter(); 155 private static String STR_KEY_ONLY_FILTER = KEY_ONLY_FILTER.getClass().getSimpleName(); 156 157 private static FirstKeyOnlyFilter FIRST_KEY_ONLY_FILTER = new FirstKeyOnlyFilter(); 158 private static String STR_FIRST_KEY_ONLY_FILTER = 159 FIRST_KEY_ONLY_FILTER.getClass().getSimpleName(); 160 161 private static CompareOp CMP_OP = CompareOp.EQUAL; 162 private static byte[] CMP_VALUE = Bytes.toBytes("value"); 163 private static BinaryComparator BC = new BinaryComparator(CMP_VALUE); 164 private static DependentColumnFilter DC_FILTER = 165 new DependentColumnFilter(FAMILY, QUALIFIER, true, CMP_OP, BC); 166 private static String STR_DC_FILTER = String.format( 167 "%s (%s, %s, %s, %s, %s)", DC_FILTER.getClass().getSimpleName(), 168 Bytes.toStringBinary(FAMILY), Bytes.toStringBinary(QUALIFIER), true, 169 CMP_OP.name(), Bytes.toStringBinary(BC.getValue())); 170 171 private static FamilyFilter FAMILY_FILTER = new FamilyFilter(CMP_OP, BC); 172 private static String STR_FAMILY_FILTER = 173 FAMILY_FILTER.getClass().getSimpleName() + " (EQUAL, value)"; 174 175 private static QualifierFilter QUALIFIER_FILTER = new QualifierFilter(CMP_OP, BC); 176 private static String STR_QUALIFIER_FILTER = 177 QUALIFIER_FILTER.getClass().getSimpleName() + " (EQUAL, value)"; 178 179 private static RowFilter ROW_FILTER = new RowFilter(CMP_OP, BC); 180 private static String STR_ROW_FILTER = ROW_FILTER.getClass().getSimpleName() + " (EQUAL, value)"; 181 182 private static ValueFilter VALUE_FILTER = new ValueFilter(CMP_OP, BC); 183 private static String STR_VALUE_FILTER = 184 VALUE_FILTER.getClass().getSimpleName() + " (EQUAL, value)"; 185 186 private static SingleColumnValueFilter SCV_FILTER = 187 new SingleColumnValueFilter(FAMILY, QUALIFIER, CMP_OP, CMP_VALUE); 188 private static String STR_SCV_FILTER = String.format("%s (%s, %s, %s, %s)", 189 SCV_FILTER.getClass().getSimpleName(), Bytes.toStringBinary(FAMILY), 190 Bytes.toStringBinary(QUALIFIER), CMP_OP.name(), 191 Bytes.toStringBinary(CMP_VALUE)); 192 193 private static SingleColumnValueExcludeFilter SCVE_FILTER = 194 new SingleColumnValueExcludeFilter(FAMILY, QUALIFIER, CMP_OP, CMP_VALUE); 195 private static String STR_SCVE_FILTER = String.format("%s (%s, %s, %s, %s)", 196 SCVE_FILTER.getClass().getSimpleName(), Bytes.toStringBinary(FAMILY), 197 Bytes.toStringBinary(QUALIFIER), CMP_OP.name(), Bytes.toStringBinary(CMP_VALUE)); 198 199 private static FilterList AND_FILTER_LIST = new FilterList( 200 Operator.MUST_PASS_ALL, Arrays.asList((Filter) TS_FILTER, L_TS_FILTER, CR_FILTER)); 201 private static String STR_AND_FILTER_LIST = String.format( 202 "%s AND (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 OR_FILTER_LIST = new FilterList( 206 Operator.MUST_PASS_ONE, Arrays.asList((Filter) TS_FILTER, L_TS_FILTER, CR_FILTER)); 207 private static String STR_OR_FILTER_LIST = String.format( 208 "%s OR (3/3): [%s, %s, %s]", AND_FILTER_LIST.getClass().getSimpleName(), 209 STR_TS_FILTER, STR_L_TS_FILTER, STR_CR_FILTER); 210 211 private static FilterList L_FILTER_LIST = new FilterList( 212 Arrays.asList((Filter) TS_FILTER, L_TS_FILTER, CR_FILTER, COL_PRE_FILTER, 213 CCG_FILTER, CP_FILTER, PREFIX_FILTER, PAGE_FILTER)); 214 private static String STR_L_FILTER_LIST = String.format( 215 "%s AND (5/8): [%s, %s, %s, %s, %s, %s]", 216 L_FILTER_LIST.getClass().getSimpleName(), STR_TS_FILTER, STR_L_TS_FILTER, 217 STR_CR_FILTER, STR_COL_PRE_FILTER, STR_CCG_FILTER, STR_CP_FILTER); 218 219 private static Filter[] FILTERS = { 220 TS_FILTER, // TimestampsFilter 221 L_TS_FILTER, // TimestampsFilter 222 COL_PRE_FILTER, // ColumnPrefixFilter 223 CP_FILTER, // ColumnPaginationFilter 224 CR_FILTER, // ColumnRangeFilter 225 CCG_FILTER, // ColumnCountGetFilter 226 IS_FILTER, // InclusiveStopFilter 227 PREFIX_FILTER, // PrefixFilter 228 PAGE_FILTER, // PageFilter 229 SKIP_FILTER, // SkipFilter 230 WHILE_FILTER, // WhileMatchFilter 231 KEY_ONLY_FILTER, // KeyOnlyFilter 232 FIRST_KEY_ONLY_FILTER, // FirstKeyOnlyFilter 233 MCP_FILTER, // MultipleColumnPrefixFilter 234 L_MCP_FILTER, // MultipleColumnPrefixFilter 235 DC_FILTER, // DependentColumnFilter 236 FAMILY_FILTER, // FamilyFilter 237 QUALIFIER_FILTER, // QualifierFilter 238 ROW_FILTER, // RowFilter 239 VALUE_FILTER, // ValueFilter 240 SCV_FILTER, // SingleColumnValueFilter 241 SCVE_FILTER, // SingleColumnValueExcludeFilter 242 AND_FILTER_LIST, // FilterList 243 OR_FILTER_LIST, // FilterList 244 L_FILTER_LIST, // FilterList 245 }; 246 247 private static String[] FILTERS_INFO = { 248 STR_TS_FILTER, // TimestampsFilter 249 STR_L_TS_FILTER, // TimestampsFilter 250 STR_COL_PRE_FILTER, // ColumnPrefixFilter 251 STR_CP_FILTER, // ColumnPaginationFilter 252 STR_CR_FILTER, // ColumnRangeFilter 253 STR_CCG_FILTER, // ColumnCountGetFilter 254 STR_IS_FILTER, // InclusiveStopFilter 255 STR_PREFIX_FILTER, // PrefixFilter 256 STR_PAGE_FILTER, // PageFilter 257 STR_SKIP_FILTER, // SkipFilter 258 STR_WHILE_FILTER, // WhileMatchFilter 259 STR_KEY_ONLY_FILTER, // KeyOnlyFilter 260 STR_FIRST_KEY_ONLY_FILTER, // FirstKeyOnlyFilter 261 STR_MCP_FILTER, // MultipleColumnPrefixFilter 262 STR_L_MCP_FILTER, // MultipleColumnPrefixFilter 263 STR_DC_FILTER, // DependentColumnFilter 264 STR_FAMILY_FILTER, // FamilyFilter 265 STR_QUALIFIER_FILTER, // QualifierFilter 266 STR_ROW_FILTER, // RowFilter 267 STR_VALUE_FILTER, // ValueFilter 268 STR_SCV_FILTER, // SingleColumnValueFilter 269 STR_SCVE_FILTER, // SingleColumnValueExcludeFilter 270 STR_AND_FILTER_LIST, // FilterList 271 STR_OR_FILTER_LIST, // FilterList 272 STR_L_FILTER_LIST, // FilterList 273 }; 274 275 static { 276 assertEquals("The sizes of static arrays do not match: " 277 + "[FILTERS: %d <=> FILTERS_INFO: %d]", 278 FILTERS.length, FILTERS_INFO.length); 279 } 280 281 /** 282 * Test the client Operations' JSON encoding to ensure that produced JSON is 283 * parseable and that the details are present and not corrupted. 284 * @throws IOException 285 */ 286 @Test 287 public void testOperationJSON() 288 throws IOException { 289 // produce a Scan Operation 290 Scan scan = new Scan(ROW); 291 scan.addColumn(FAMILY, QUALIFIER); 292 // get its JSON representation, and parse it 293 String json = scan.toJSON(); 294 Map<String, Object> parsedJSON = mapper.readValue(json, HashMap.class); 295 // check for the row 296 assertEquals("startRow incorrect in Scan.toJSON()", 297 Bytes.toStringBinary(ROW), parsedJSON.get("startRow")); 298 // check for the family and the qualifier. 299 List familyInfo = (List) ((Map) parsedJSON.get("families")).get( 300 Bytes.toStringBinary(FAMILY)); 301 assertNotNull("Family absent in Scan.toJSON()", familyInfo); 302 assertEquals("Qualifier absent in Scan.toJSON()", 1, familyInfo.size()); 303 assertEquals("Qualifier incorrect in Scan.toJSON()", 304 Bytes.toStringBinary(QUALIFIER), 305 familyInfo.get(0)); 306 307 // produce a Get Operation 308 Get get = new Get(ROW); 309 get.addColumn(FAMILY, QUALIFIER); 310 // get its JSON representation, and parse it 311 json = get.toJSON(); 312 parsedJSON = mapper.readValue(json, HashMap.class); 313 // check for the row 314 assertEquals("row incorrect in Get.toJSON()", 315 Bytes.toStringBinary(ROW), parsedJSON.get("row")); 316 // check for the family and the qualifier. 317 familyInfo = (List) ((Map) parsedJSON.get("families")).get( 318 Bytes.toStringBinary(FAMILY)); 319 assertNotNull("Family absent in Get.toJSON()", familyInfo); 320 assertEquals("Qualifier absent in Get.toJSON()", 1, familyInfo.size()); 321 assertEquals("Qualifier incorrect in Get.toJSON()", 322 Bytes.toStringBinary(QUALIFIER), 323 familyInfo.get(0)); 324 325 // produce a Put operation 326 Put put = new Put(ROW); 327 put.addColumn(FAMILY, QUALIFIER, VALUE); 328 // get its JSON representation, and parse it 329 json = put.toJSON(); 330 parsedJSON = mapper.readValue(json, HashMap.class); 331 // check for the row 332 assertEquals("row absent in Put.toJSON()", 333 Bytes.toStringBinary(ROW), parsedJSON.get("row")); 334 // check for the family and the qualifier. 335 familyInfo = (List) ((Map) parsedJSON.get("families")).get( 336 Bytes.toStringBinary(FAMILY)); 337 assertNotNull("Family absent in Put.toJSON()", familyInfo); 338 assertEquals("KeyValue absent in Put.toJSON()", 1, familyInfo.size()); 339 Map kvMap = (Map) familyInfo.get(0); 340 assertEquals("Qualifier incorrect in Put.toJSON()", 341 Bytes.toStringBinary(QUALIFIER), 342 kvMap.get("qualifier")); 343 assertEquals("Value length incorrect in Put.toJSON()", 344 VALUE.length, kvMap.get("vlen")); 345 346 // produce a Delete operation 347 Delete delete = new Delete(ROW); 348 delete.addColumn(FAMILY, QUALIFIER); 349 // get its JSON representation, and parse it 350 json = delete.toJSON(); 351 parsedJSON = mapper.readValue(json, HashMap.class); 352 // check for the row 353 assertEquals("row absent in Delete.toJSON()", 354 Bytes.toStringBinary(ROW), parsedJSON.get("row")); 355 // check for the family and the qualifier. 356 familyInfo = (List) ((Map) parsedJSON.get("families")).get( 357 Bytes.toStringBinary(FAMILY)); 358 assertNotNull("Family absent in Delete.toJSON()", familyInfo); 359 assertEquals("KeyValue absent in Delete.toJSON()", 1, familyInfo.size()); 360 kvMap = (Map) familyInfo.get(0); 361 assertEquals("Qualifier incorrect in Delete.toJSON()", 362 Bytes.toStringBinary(QUALIFIER), kvMap.get("qualifier")); 363 } 364 365 @Test 366 public void testPutCreationWithByteBuffer() { 367 Put p = new Put(ROW); 368 List<Cell> c = p.get(FAMILY, QUALIFIER); 369 Assert.assertEquals(0, c.size()); 370 Assert.assertEquals(HConstants.LATEST_TIMESTAMP, p.getTimestamp()); 371 372 p.addColumn(FAMILY, ByteBuffer.wrap(QUALIFIER), 1984L, ByteBuffer.wrap(VALUE)); 373 c = p.get(FAMILY, QUALIFIER); 374 Assert.assertEquals(1, c.size()); 375 Assert.assertEquals(1984L, c.get(0).getTimestamp()); 376 Assert.assertArrayEquals(VALUE, CellUtil.cloneValue(c.get(0))); 377 Assert.assertEquals(HConstants.LATEST_TIMESTAMP, p.getTimestamp()); 378 Assert.assertEquals(0, CellComparatorImpl.COMPARATOR.compare(c.get(0), new KeyValue(c.get(0)))); 379 380 p = new Put(ROW); 381 p.addColumn(FAMILY, ByteBuffer.wrap(QUALIFIER), 2013L, null); 382 c = p.get(FAMILY, QUALIFIER); 383 Assert.assertEquals(1, c.size()); 384 Assert.assertEquals(2013L, c.get(0).getTimestamp()); 385 Assert.assertArrayEquals(new byte[]{}, CellUtil.cloneValue(c.get(0))); 386 Assert.assertEquals(HConstants.LATEST_TIMESTAMP, p.getTimestamp()); 387 Assert.assertEquals(0, CellComparatorImpl.COMPARATOR.compare(c.get(0), new KeyValue(c.get(0)))); 388 389 p = new Put(ByteBuffer.wrap(ROW)); 390 p.addColumn(FAMILY, ByteBuffer.wrap(QUALIFIER), 2001L, null); 391 c = p.get(FAMILY, QUALIFIER); 392 Assert.assertEquals(1, c.size()); 393 Assert.assertEquals(2001L, c.get(0).getTimestamp()); 394 Assert.assertArrayEquals(new byte[]{}, CellUtil.cloneValue(c.get(0))); 395 Assert.assertArrayEquals(ROW, CellUtil.cloneRow(c.get(0))); 396 Assert.assertEquals(HConstants.LATEST_TIMESTAMP, p.getTimestamp()); 397 Assert.assertEquals(0, CellComparatorImpl.COMPARATOR.compare(c.get(0), new KeyValue(c.get(0)))); 398 399 p = new Put(ByteBuffer.wrap(ROW), 1970L); 400 p.addColumn(FAMILY, ByteBuffer.wrap(QUALIFIER), 2001L, null); 401 c = p.get(FAMILY, QUALIFIER); 402 Assert.assertEquals(1, c.size()); 403 Assert.assertEquals(2001L, c.get(0).getTimestamp()); 404 Assert.assertArrayEquals(new byte[]{}, CellUtil.cloneValue(c.get(0))); 405 Assert.assertArrayEquals(ROW, CellUtil.cloneRow(c.get(0))); 406 Assert.assertEquals(1970L, p.getTimestamp()); 407 Assert.assertEquals(0, CellComparatorImpl.COMPARATOR.compare(c.get(0), new KeyValue(c.get(0)))); 408 } 409 410 @Test 411 @SuppressWarnings("rawtypes") 412 public void testOperationSubClassMethodsAreBuilderStyle() { 413 /* All Operation subclasses should have a builder style setup where setXXX/addXXX methods 414 * can be chainable together: 415 * . For example: 416 * Scan scan = new Scan() 417 * .setFoo(foo) 418 * .setBar(bar) 419 * .setBuz(buz) 420 * 421 * This test ensures that all methods starting with "set" returns the declaring object 422 */ 423 424 // TODO: We should ensure all subclasses of Operation is checked. 425 Class[] classes = new Class[] { 426 Operation.class, 427 OperationWithAttributes.class, 428 Mutation.class, 429 Query.class, 430 Delete.class, 431 Increment.class, 432 Append.class, 433 Put.class, 434 Get.class, 435 Scan.class}; 436 437 BuilderStyleTest.assertClassesAreBuilderStyle(classes); 438 } 439 440}