@Test
  public void testSerialize() {
    ObserverAggregators.HCol[] hcols =
        new ObserverAggregators.HCol[] { //
          newHCol("f", "c1", new String[] {"SUM", "COUNT"}, new String[] {"decimal", "long"}), //
          newHCol("f", "c2", new String[] {"SUM", "SUM"}, new String[] {"long", "long"})
        };
    ObserverAggregators sample = new ObserverAggregators(hcols);

    byte[] bytes = ObserverAggregators.serialize(sample);
    ObserverAggregators copy = ObserverAggregators.deserialize(bytes);

    assertTrue(sample.nHCols == copy.nHCols);
    assertTrue(sample.nTotalMeasures == copy.nTotalMeasures);
    assertEquals(sample.hcols[0], copy.hcols[0]);
    assertEquals(sample.hcols[1], copy.hcols[1]);
  }
  @SuppressWarnings("rawtypes")
  ObserverAggregationCache buildAggrCache(
      final RegionScanner innerScanner,
      CoprocessorRowType type,
      CoprocessorProjector projector,
      ObserverAggregators aggregators,
      CoprocessorFilter filter,
      Stats stats)
      throws IOException {

    ObserverAggregationCache aggCache = new ObserverAggregationCache(aggregators);

    ObserverTuple tuple = new ObserverTuple(type);
    boolean hasMore = true;
    List<Cell> results = new ArrayList<Cell>();
    while (hasMore) {
      results.clear();
      hasMore = innerScanner.nextRaw(results);
      if (results.isEmpty()) continue;

      if (stats != null) stats.countInputRow(results);

      Cell cell = results.get(0);
      tuple.setUnderlying(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());

      if (behavior.ordinal() >= ObserverBehavior.SCAN_FILTER.ordinal()) {
        if (filter != null && filter.evaluate(tuple) == false) continue;

        if (behavior.ordinal() >= ObserverBehavior.SCAN_FILTER_AGGR.ordinal()) {
          AggrKey aggKey = projector.getAggrKey(results);
          MeasureAggregator[] bufs = aggCache.getBuffer(aggKey);
          aggregators.aggregate(bufs, results);

          aggCache.checkMemoryUsage();
        }
      }
    }
    return aggCache;
  }