@Override
  protected void runTest(
      ISerializerDeserializer[] fieldSerdes,
      int numKeys,
      BTreeLeafFrameType leafType,
      ITupleReference lowKey,
      ITupleReference highKey,
      ITupleReference prefixLowKey,
      ITupleReference prefixHighKey)
      throws Exception {
    OrderedIndexTestContext ctx = createTestContext(fieldSerdes, numKeys, leafType);
    ctx.getIndex().create();
    ctx.getIndex().activate();
    // We assume all fieldSerdes are of the same type. Check the first one
    // to determine which field types to generate.
    if (fieldSerdes[0] instanceof IntegerSerializerDeserializer) {
      orderedIndexTestUtils.upsertIntTuples(ctx, numTuplesToInsert, getRandom());
    } else if (fieldSerdes[0] instanceof UTF8StringSerializerDeserializer) {
      orderedIndexTestUtils.upsertStringTuples(ctx, numTuplesToInsert, getRandom());
    }

    orderedIndexTestUtils.checkPointSearches(ctx);
    orderedIndexTestUtils.checkScan(ctx);
    orderedIndexTestUtils.checkDiskOrderScan(ctx);

    orderedIndexTestUtils.checkRangeSearch(ctx, lowKey, highKey, true, true);
    if (prefixLowKey != null && prefixHighKey != null) {
      orderedIndexTestUtils.checkRangeSearch(ctx, prefixLowKey, prefixHighKey, true, true);
    }
    ctx.getIndex().validate();
    ctx.getIndex().deactivate();
    ctx.getIndex().destroy();
  }
  @SuppressWarnings("unchecked")
  public void updateTuples(IIndexTestContext ictx, int numTuples, Random rnd) throws Exception {
    OrderedIndexTestContext ctx = (OrderedIndexTestContext) ictx;
    int fieldCount = ctx.getFieldCount();
    int keyFieldCount = ctx.getKeyFieldCount();
    // This is a noop because we can only update non-key fields.
    if (fieldCount == keyFieldCount) {
      return;
    }
    ArrayTupleBuilder updateTupleBuilder = new ArrayTupleBuilder(fieldCount);
    ArrayTupleReference updateTuple = new ArrayTupleReference();
    int numCheckTuples = ctx.getCheckTuples().size();
    // Copy CheckTuple references into array, so we can randomly pick from
    // there.
    CheckTuple[] checkTuples = new CheckTuple[numCheckTuples];
    int idx = 0;
    for (CheckTuple checkTuple : ctx.getCheckTuples()) {
      checkTuples[idx++] = checkTuple;
    }
    for (int i = 0; i < numTuples && numCheckTuples > 0; i++) {
      if (LOGGER.isLoggable(Level.INFO)) {
        if ((i + 1) % (numTuples / Math.min(10, numTuples)) == 0) {
          LOGGER.info("Updating Tuple " + (i + 1) + "/" + numTuples);
        }
      }
      int checkTupleIdx = Math.abs(rnd.nextInt() % numCheckTuples);
      CheckTuple checkTuple = checkTuples[checkTupleIdx];
      // Update check tuple's non-key fields.
      for (int j = keyFieldCount; j < fieldCount; j++) {
        Comparable newValue = getRandomUpdateValue(ctx.getFieldSerdes()[j], rnd);
        checkTuple.setField(j, newValue);
      }

      createTupleFromCheckTuple(checkTuple, updateTupleBuilder, updateTuple, ctx.getFieldSerdes());
      ctx.getIndexAccessor().update(updateTuple);

      // Swap with last "valid" CheckTuple.
      CheckTuple tmp = checkTuples[numCheckTuples - 1];
      checkTuples[numCheckTuples - 1] = checkTuple;
      checkTuples[checkTupleIdx] = tmp;
      numCheckTuples--;
    }
  }
 public void upsertStringTuples(IIndexTestContext ictx, int numTuples, Random rnd)
     throws Exception {
   OrderedIndexTestContext ctx = (OrderedIndexTestContext) ictx;
   int fieldCount = ctx.getFieldCount();
   int numKeyFields = ctx.getKeyFieldCount();
   String[] fieldValues = new String[fieldCount];
   for (int i = 0; i < numTuples; i++) {
     if (LOGGER.isLoggable(Level.INFO)) {
       if ((i + 1) % (numTuples / Math.min(10, numTuples)) == 0) {
         LOGGER.info("Inserting Tuple " + (i + 1) + "/" + numTuples);
       }
     }
     // Set keys.
     for (int j = 0; j < numKeyFields; j++) {
       int length = (Math.abs(rnd.nextInt()) % 10) + 1;
       fieldValues[j] = getRandomString(length, rnd);
     }
     // Set values.
     for (int j = numKeyFields; j < fieldCount; j++) {
       fieldValues[j] = getRandomString(5, rnd);
     }
     TupleUtils.createTuple(
         ctx.getTupleBuilder(), ctx.getTuple(), ctx.getFieldSerdes(), (Object[]) fieldValues);
     ctx.getIndexAccessor().upsert(ctx.getTuple());
     ctx.upsertCheckTuple(
         createStringCheckTuple(fieldValues, ctx.getKeyFieldCount()), ctx.getCheckTuples());
   }
 }
 public void upsertIntTuples(IIndexTestContext ictx, int numTuples, Random rnd) throws Exception {
   OrderedIndexTestContext ctx = (OrderedIndexTestContext) ictx;
   int fieldCount = ctx.getFieldCount();
   int numKeyFields = ctx.getKeyFieldCount();
   int[] fieldValues = new int[ctx.getFieldCount()];
   // Scale range of values according to number of keys.
   // For example, for 2 keys we want the square root of numTuples, for 3
   // keys the cube root of numTuples, etc.
   int maxValue = (int) Math.ceil(Math.pow(numTuples, 1.0 / (double) numKeyFields));
   for (int i = 0; i < numTuples; i++) {
     // Set keys.
     setIntKeyFields(fieldValues, numKeyFields, maxValue, rnd);
     // Set values.
     setIntPayloadFields(fieldValues, numKeyFields, fieldCount);
     TupleUtils.createIntegerTuple(ctx.getTupleBuilder(), ctx.getTuple(), fieldValues);
     if (LOGGER.isLoggable(Level.INFO)) {
       if ((i + 1) % (numTuples / Math.min(10, numTuples)) == 0) {
         LOGGER.info("Inserting Tuple " + (i + 1) + "/" + numTuples);
       }
     }
     ctx.getIndexAccessor().upsert(ctx.getTuple());
     ctx.upsertCheckTuple(
         createIntCheckTuple(fieldValues, ctx.getKeyFieldCount()), ctx.getCheckTuples());
   }
 }
  public void checkPointSearches(IIndexTestContext ictx) throws Exception {
    if (LOGGER.isLoggable(Level.INFO)) {
      LOGGER.info("Testing Point Searches On All Expected Keys.");
    }
    OrderedIndexTestContext ctx = (OrderedIndexTestContext) ictx;
    IIndexCursor searchCursor = ctx.getIndexAccessor().createSearchCursor(false);

    ArrayTupleBuilder lowKeyBuilder = new ArrayTupleBuilder(ctx.getKeyFieldCount());
    ArrayTupleReference lowKey = new ArrayTupleReference();
    ArrayTupleBuilder highKeyBuilder = new ArrayTupleBuilder(ctx.getKeyFieldCount());
    ArrayTupleReference highKey = new ArrayTupleReference();
    RangePredicate rangePred = new RangePredicate(lowKey, highKey, true, true, null, null);

    // Iterate through expected tuples, and perform a point search in the
    // BTree to verify the tuple can be reached.
    for (CheckTuple checkTuple : ctx.getCheckTuples()) {
      createTupleFromCheckTuple(checkTuple, lowKeyBuilder, lowKey, ctx.getFieldSerdes());
      createTupleFromCheckTuple(checkTuple, highKeyBuilder, highKey, ctx.getFieldSerdes());
      MultiComparator lowKeyCmp =
          BTreeUtils.getSearchMultiComparator(ctx.getComparatorFactories(), lowKey);
      MultiComparator highKeyCmp =
          BTreeUtils.getSearchMultiComparator(ctx.getComparatorFactories(), highKey);

      rangePred.setLowKey(lowKey, true);
      rangePred.setHighKey(highKey, true);
      rangePred.setLowKeyComparator(lowKeyCmp);
      rangePred.setHighKeyComparator(highKeyCmp);

      ctx.getIndexAccessor().search(searchCursor, rangePred);

      try {
        // We expect exactly one answer.
        if (searchCursor.hasNext()) {
          searchCursor.next();
          ITupleReference tuple = searchCursor.getTuple();
          compareActualAndExpected(tuple, checkTuple, ctx.getFieldSerdes());
        }
        if (searchCursor.hasNext()) {
          fail("Point search returned more than one answer.");
        }
      } finally {
        searchCursor.close();
      }
    }
  }