/** A mock flushing operation sink. */
  private static final class MockFlushingOperationSink
      implements FlushingOperationSink<WaveletOperation> {

    private static enum Method {
      CONSUME,
      FLUSH
    }

    private final Queue<Object[]> expectations = CollectionUtils.newLinkedList();
    private Runnable resumeCommand;

    /**
     * Expects a call to consume an op. Optionally performs an action when the consume() call is
     * made.
     */
    void expectConsume(WaveletOperation op, Runnable action) {
      expectations.add(new Object[] {Method.CONSUME, op, action});
    }

    /**
     * Expects a call to flush and operation with a resume command. If {@code} succeed is true then
     * the call will return true, else it will return false (and the caller will expect the resume
     * command to be later invoked).
     */
    void expectFlush(WaveletOperation operation, boolean succeed) {
      expectations.add(new Object[] {Method.FLUSH, operation, succeed});
    }

    void checkExpectationsSatisfied() {
      assertTrue(expectations.isEmpty());
    }

    /** @return the last command passed to flush() */
    Runnable getLastResumeCommand() {
      return resumeCommand;
    }

    @Override
    public void consume(WaveletOperation op) {
      Object[] expected = expectations.remove();
      assertEquals(expected[0], Method.CONSUME);
      assertSame(expected[1], op);
      if (expected[2] != null) {
        ((Runnable) expected[2]).run();
      }
    }

    @Override
    public boolean flush(WaveletOperation operation, Runnable resume) {
      Object[] expected = expectations.remove();
      assertEquals(expected[0], Method.FLUSH);
      assertSame(expected[1], operation);
      resumeCommand = resume;
      return (Boolean) expected[2];
    }
  }