public void doTestTableScanners() throws Exception {
    // Setup
    ThriftHBaseServiceHandler handler = new ThriftHBaseServiceHandler(UTIL.getConfiguration(),
      UserProvider.instantiate(UTIL.getConfiguration()));
    handler.createTable(tableAname, getColumnDescriptors());

    // Apply timestamped Mutations to rowA
    long time1 = EnvironmentEdgeManager.currentTime();
    handler.mutateRowTs(tableAname, rowAname, getMutations(), time1, null);

    // Sleep to assure that 'time1' and 'time2' will be different even with a
    // coarse grained system timer.
    Thread.sleep(1000);

    // Apply timestamped BatchMutations for rowA and rowB
    long time2 = EnvironmentEdgeManager.currentTime();
    handler.mutateRowsTs(tableAname, getBatchMutations(), time2, null);

    time1 += 1;

    // Test a scanner on all rows and all columns, no timestamp
    int scanner1 = handler.scannerOpen(tableAname, rowAname, getColumnList(true, true), null);
    TRowResult rowResult1a = handler.scannerGet(scanner1).get(0);
    assertEquals(rowResult1a.row, rowAname);
    // This used to be '1'. I don't know why when we are asking for two columns
    // and when the mutations above would seem to add two columns to the row.
    // -- St.Ack 05/12/2009
    assertEquals(1, rowResult1a.columns.size());
    assertEquals(rowResult1a.columns.get(columnBname).value, valueCname);

    TRowResult rowResult1b = handler.scannerGet(scanner1).get(0);
    assertEquals(rowResult1b.row, rowBname);
    assertEquals(2, rowResult1b.columns.size());
    assertEquals(rowResult1b.columns.get(columnAname).value, valueCname);
    assertEquals(rowResult1b.columns.get(columnBname).value, valueDname);
    closeScanner(scanner1, handler);

    // Test a scanner on all rows and all columns, with timestamp
    int scanner2 =
      handler.scannerOpenTs(tableAname, rowAname, getColumnList(true, true), time1, null);
    TRowResult rowResult2a = handler.scannerGet(scanner2).get(0);
    assertEquals(1, rowResult2a.columns.size());
    // column A deleted, does not exist.
    // assertTrue(Bytes.equals(rowResult2a.columns.get(columnAname).value, valueAname));
    assertEquals(rowResult2a.columns.get(columnBname).value, valueBname);
    closeScanner(scanner2, handler);

    // Test a scanner on the first row and first column only, no timestamp
    int scanner3 =
      handler.scannerOpenWithStop(tableAname, rowAname, rowBname, getColumnList(true, false), null);
    closeScanner(scanner3, handler);

    // Test a scanner on the first row and second column only, with timestamp
    int scanner4 = handler.scannerOpenWithStopTs(tableAname, rowAname, rowBname,
      getColumnList(false, true), time1, null);
    TRowResult rowResult4a = handler.scannerGet(scanner4).get(0);
    assertEquals(1, rowResult4a.columns.size());
    assertEquals(rowResult4a.columns.get(columnBname).value, valueBname);

    // Test scanner using a TScan object once with sortColumns False and once with sortColumns true
    TScan scanNoSortColumns = new TScan();
    scanNoSortColumns.setStartRow(rowAname);
    scanNoSortColumns.setStopRow(rowBname);

    int scanner5 = handler.scannerOpenWithScan(tableAname, scanNoSortColumns, null);
    TRowResult rowResult5 = handler.scannerGet(scanner5).get(0);
    assertEquals(1, rowResult5.columns.size());
    assertEquals(rowResult5.columns.get(columnBname).value, valueCname);

    TScan scanSortColumns = new TScan();
    scanSortColumns.setStartRow(rowAname);
    scanSortColumns.setStopRow(rowBname);
    scanSortColumns = scanSortColumns.setSortColumns(true);

    int scanner6 = handler.scannerOpenWithScan(tableAname, scanSortColumns, null);
    TRowResult rowResult6 = handler.scannerGet(scanner6).get(0);
    assertEquals(1, rowResult6.sortedColumns.size());
    assertEquals(rowResult6.sortedColumns.get(0).getCell().value, valueCname);

    List<Mutation> rowBmutations = new ArrayList<>(20);
    for (int i = 0; i < 20; i++) {
      rowBmutations.add(new Mutation(false, asByteBuffer("columnA:" + i), valueCname, true));
    }
    ByteBuffer rowC = asByteBuffer("rowC");
    handler.mutateRow(tableAname, rowC, rowBmutations, null);

    TScan scanSortMultiColumns = new TScan();
    scanSortMultiColumns.setStartRow(rowC);
    scanSortMultiColumns = scanSortMultiColumns.setSortColumns(true);
    int scanner7 = handler.scannerOpenWithScan(tableAname, scanSortMultiColumns, null);
    TRowResult rowResult7 = handler.scannerGet(scanner7).get(0);

    ByteBuffer smallerColumn = asByteBuffer("columnA:");
    for (int i = 0; i < 20; i++) {
      ByteBuffer currentColumn = rowResult7.sortedColumns.get(i).columnName;
      assertTrue(Bytes.compareTo(smallerColumn.array(), currentColumn.array()) < 0);
      smallerColumn = currentColumn;
    }

    TScan reversedScan = new TScan();
    reversedScan.setReversed(true);
    reversedScan.setStartRow(rowBname);
    reversedScan.setStopRow(rowAname);

    int scanner8 = handler.scannerOpenWithScan(tableAname, reversedScan, null);
    List<TRowResult> results = handler.scannerGet(scanner8);
    handler.scannerClose(scanner8);
    assertEquals(1, results.size());
    assertEquals(ByteBuffer.wrap(results.get(0).getRow()), rowBname);

    // Teardown
    handler.disableTable(tableAname);
    handler.deleteTable(tableAname);
  }
Tests the four different scanner-opening methods (with and without a stoprow, with and without
a timestamp).
hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift/TestThriftServer.java