@Test
  public void start_errorThenRestarted() throws Exception {
    startAndVerify();

    replicator.stop();
    Assert.assertEquals(Replicator.State.STOPPING, replicator.getState());

    ErrorInfo err = new ErrorInfo(new RuntimeException("Mocked error"));
    ReplicationStrategyErrored rse = new ReplicationStrategyErrored(mockStrategy, err);
    ReplicationErrored re = new ReplicationErrored(replicator, err);
    replicator.error(rse);
    Assert.assertEquals(Replicator.State.ERROR, replicator.getState());
    verify(mockListener).error(re);
    verify(mockListener, never()).complete(any(ReplicationCompleted.class));

    doAnswer(
            new Answer() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                Thread.sleep(1000); // sleep for a second, will give enough time to check state
                return null;
              }
            })
        .when(mockStrategy)
        .run();
    replicator.start();
    Assert.assertEquals(Replicator.State.STARTED, replicator.getState());
    Assert.assertTrue(replicator.strategyThread().isAlive());
    replicator.await();
    Assert.assertFalse(replicator.strategyThread().isAlive());
    verify(mockStrategy, times(2)).run();
  }
  @Test(expected = IllegalStateException.class)
  public void start_startWhenStopping_exception() throws Exception {
    startAndVerify();

    replicator.stop();
    Assert.assertEquals(Replicator.State.STOPPING, replicator.getState());
    replicator.start(); // can not restart until full stopped
  }
  @Test
  public void startAndThenStopComplete_stoppedState() throws Exception {
    startAndVerify();

    replicator.stop();
    Assert.assertEquals(Replicator.State.STOPPING, replicator.getState());
    Assert.assertFalse(replicator.strategyThread().isAlive());

    ReplicationStrategyCompleted rsc = new ReplicationStrategyCompleted(mockStrategy);
    ReplicationCompleted rc = new ReplicationCompleted(replicator, 0, 0);

    replicator.complete(rsc);
    verify(mockListener).complete(rc);
    verify(mockListener, never()).error(any(ReplicationErrored.class));

    Assert.assertEquals(Replicator.State.STOPPED, replicator.getState());
  }
  @Test
  public void startAndThenStopError_errorState() throws Exception {
    startAndVerify();

    replicator.stop();
    Assert.assertEquals(Replicator.State.STOPPING, replicator.getState());
    Assert.assertFalse(replicator.strategyThread().isAlive());

    ErrorInfo err = new ErrorInfo(new RuntimeException("Mocked error"));
    ReplicationStrategyErrored rse = new ReplicationStrategyErrored(mockStrategy, err);
    ReplicationErrored re = new ReplicationErrored(replicator, err);
    replicator.error(rse);

    verify(mockListener).error(re);
    verify(mockListener, never()).complete(any(ReplicationCompleted.class));

    Assert.assertEquals(Replicator.State.ERROR, replicator.getState());
  }
  private void startAndVerify() throws Exception {
    Assert.assertEquals(Replicator.State.PENDING, replicator.getState());
    Assert.assertNull(replicator.strategyThread());

    replicator.getEventBus().register(mockListener);
    replicator.start();

    Assert.assertNotNull(replicator.strategyThread());
    Assert.assertEquals(Replicator.State.STARTED, replicator.getState());

    // Make sure the strategy returned before move on, other wise
    // the mock strategy might not be called when you try to verify
    replicator.await();
    Assert.assertFalse(replicator.strategyThread().isAlive());

    verify(mockStrategy).run();

    // No interaction to listener yet
    verifyZeroInteractions(mockListener);
  }
  @Test
  public void error_exceptionInErrorCallback_nothingShouldBeCorrupted() throws Exception {
    startAndVerify();

    ErrorInfo err = new ErrorInfo(new RuntimeException("Mocked error"));
    ReplicationStrategyErrored rse = new ReplicationStrategyErrored(mockStrategy, err);
    ReplicationErrored re = new ReplicationErrored(replicator, err);

    doThrow(new RuntimeException("Another mocked error")).when(mockListener).error(re);

    replicator.error(rse);

    verify(mockListener).error(re);
    verify(mockListener, never()).complete(any(ReplicationCompleted.class));

    Assert.assertEquals(Replicator.State.ERROR, replicator.getState());
    Assert.assertFalse(replicator.strategyThread().isAlive());
  }
  @Test
  public void complete_exceptionInCompleteCallback_exceptionShouldCorruptAnything()
      throws Exception {
    startAndVerify();

    ReplicationStrategyCompleted rsc = new ReplicationStrategyCompleted(mockStrategy);
    ReplicationCompleted rc = new ReplicationCompleted(replicator, 0, 0);

    doThrow(new RuntimeException("Mocked error")).when(mockListener).complete(rc);

    replicator.complete(rsc);

    verify(mockListener).complete(rc);
    verify(mockListener, never()).error(any(ReplicationErrored.class));

    Assert.assertEquals(Replicator.State.COMPLETE, replicator.getState());
    Assert.assertFalse(replicator.strategyThread().isAlive());
  }
 @Test
 public void constructor() throws Exception {
   Assert.assertEquals(Replicator.State.PENDING, replicator.getState());
   Assert.assertNull(replicator.strategyThread());
 }
 @Test
 public void stopBeforeStart_stopped() throws Exception {
   replicator.stop();
   Assert.assertEquals(Replicator.State.STOPPED, replicator.getState());
   verify(mockStrategy, never()).run();
 }