Program: 160290
Project: 160290
Buggy source:
/** 
 * https://github.com/couchbase/couchbase-lite-java-core/issues/358
 * @related : https://github.com/couchbase/couchbase-lite-java-core/issues/55 related: testContinuousPushReplicationGoesIdle() <p/> test steps: - start replicator - make sure replicator becomes idle state - add N docs - when callback state == idle - assert that mock has received N docs
 */
public void testContinuousPushReplicationGoesIdleTwice() throws Exception {
  final int EXPECTED_REQUEST_COUNT=4;
  assertEquals(0,database.getLastSequenceNumber());
  MockDispatcher dispatcher=new MockDispatcher();
  final MockWebServer server=MockHelper.getMockWebServer(dispatcher);
  dispatcher.setServerType(MockDispatcher.ServerType.SYNC_GW);
  MockCheckpointPut mockCheckpointPut=new MockCheckpointPut();
  mockCheckpointPut.setSticky(true);
  mockCheckpointPut.setDelayMs(500);
  dispatcher.enqueueResponse(MockHelper.PATH_REGEX_CHECKPOINT,mockCheckpointPut);
  MockRevsDiff mockRevsDiff=new MockRevsDiff();
  dispatcher.enqueueResponse(MockHelper.PATH_REGEX_REVS_DIFF,mockRevsDiff);
  MockBulkDocs mockBulkDocs=new MockBulkDocs();
  dispatcher.enqueueResponse(MockHelper.PATH_REGEX_BULK_DOCS,mockBulkDocs);
  server.play();
  Replication replication=database.createPushReplication(server.getUrl("String_Node_Str"));
  replication.setContinuous(true);
  CountDownLatch replicationIdle=new CountDownLatch(1);
  ReplicationIdleObserver idleObserver=new ReplicationIdleObserver(replicationIdle);
  replication.addChangeListener(idleObserver);
  replication.start();
  boolean success=replicationIdle.await(30,TimeUnit.SECONDS);
  assertTrue(success);
  replication.removeChangeListener(idleObserver);
  RecordedRequest request1=dispatcher.takeRequestBlocking(MockHelper.PATH_REGEX_CHECKPOINT);
  assertNotNull(request1);
  dispatcher.takeRecordedResponseBlocking(request1);
  assertEquals(1,server.getRequestCount());
class ReplicationTransitionToIdleObserver implements Replication.ChangeListener {
    private CountDownLatch doneSignal;
    private CountDownLatch checkSignal;
    public ReplicationTransitionToIdleObserver(    CountDownLatch doneSignal,    CountDownLatch checkSignal){
      this.doneSignal=doneSignal;
      this.checkSignal=checkSignal;
    }
    public void changed(    Replication.ChangeEvent event){
      Log.w(Log.TAG_SYNC,"String_Node_Str" + event.toString());
      if (event.getTransition() != null) {
        if (event.getTransition().getSource() != event.getTransition().getDestination() && event.getTransition().getDestination() == ReplicationState.IDLE) {
          Log.w(Log.TAG_SYNC,"String_Node_Str");
          Log.w(Log.TAG_SYNC,"String_Node_Str" + server.getRequestCount());
          this.doneSignal.countDown();
          if (EXPECTED_REQUEST_COUNT == server.getRequestCount() || EXPECTED_REQUEST_COUNT - 1 == server.getRequestCount()) {
            this.checkSignal.countDown();
          }
        }
      }
    }
  }
  CountDownLatch checkStateToIdle=new CountDownLatch(1);
  CountDownLatch checkRequestCount=new CountDownLatch(1);
  ReplicationTransitionToIdleObserver replicationTransitionToIdleObserver=new ReplicationTransitionToIdleObserver(checkStateToIdle,checkRequestCount);
  replication.addChangeListener(replicationTransitionToIdleObserver);
  Log.w(Log.TAG_SYNC,"String_Node_Str");
  for (int i=1; i <= 1; i++) {
    Map<String,Object> properties1=new HashMap<String,Object>();
    properties1.put("String_Node_Str" + String.valueOf(i),"String_Node_Str" + String.valueOf(i));
    final Document doc=createDocWithProperties(properties1);
  }
  success=checkStateToIdle.await(20,TimeUnit.SECONDS);
  assertTrue(success);
  success=checkRequestCount.await(20,TimeUnit.SECONDS);
  assertTrue(success);
  RecordedRequest request3=dispatcher.takeRequestBlocking(MockHelper.PATH_REGEX_BULK_DOCS);
  assertNotNull(request3);
  dispatcher.takeRecordedResponseBlocking(request3);
  RecordedRequest request4=dispatcher.takeRequestBlocking(MockHelper.PATH_REGEX_CHECKPOINT);
  assertNotNull(request4);
  dispatcher.takeRecordedResponseBlocking(request4);
  Log.w(Log.TAG_SYNC,"String_Node_Str" + server.getRequestCount());
  assertEquals(EXPECTED_REQUEST_COUNT,server.getRequestCount());
  replication.removeChangeListener(replicationTransitionToIdleObserver);
  stopReplication(replication);
}

Fixed version (ground truth):
/** 
 * https://github.com/couchbase/couchbase-lite-java-core/issues/358
 * @related : https://github.com/couchbase/couchbase-lite-java-core/issues/55 related: testContinuousPushReplicationGoesIdle() <p/> test steps: - start replicator - make sure replicator becomes idle state - add N docs - when callback state == idle - assert that mock has received N docs
 */
public void testContinuousPushReplicationGoesIdleTwice() throws Exception {
  final int EXPECTED_REQUEST_COUNT=4;
  assertEquals(0,database.getLastSequenceNumber());
  MockDispatcher dispatcher=new MockDispatcher();
  final MockWebServer server=MockHelper.getMockWebServer(dispatcher);
  dispatcher.setServerType(MockDispatcher.ServerType.SYNC_GW);
  MockCheckpointPut mockCheckpointPut=new MockCheckpointPut();
  mockCheckpointPut.setSticky(true);
  mockCheckpointPut.setDelayMs(500);
  dispatcher.enqueueResponse(MockHelper.PATH_REGEX_CHECKPOINT,mockCheckpointPut);
  MockRevsDiff mockRevsDiff=new MockRevsDiff();
  dispatcher.enqueueResponse(MockHelper.PATH_REGEX_REVS_DIFF,mockRevsDiff);
  MockBulkDocs mockBulkDocs=new MockBulkDocs();
  dispatcher.enqueueResponse(MockHelper.PATH_REGEX_BULK_DOCS,mockBulkDocs);
  server.play();
  Replication replication=database.createPushReplication(server.getUrl("String_Node_Str"));
  replication.setContinuous(true);
  CountDownLatch replicationIdle=new CountDownLatch(1);
  ReplicationIdleObserver idleObserver=new ReplicationIdleObserver(replicationIdle);
  replication.addChangeListener(idleObserver);
  replication.start();
  boolean success=replicationIdle.await(30,TimeUnit.SECONDS);
  assertTrue(success);
  replication.removeChangeListener(idleObserver);
  RecordedRequest request1=dispatcher.takeRequestBlocking(MockHelper.PATH_REGEX_CHECKPOINT);
  assertNotNull(request1);
  dispatcher.takeRecordedResponseBlocking(request1);
  assertEquals(1,server.getRequestCount());
class ReplicationTransitionToIdleObserver implements Replication.ChangeListener {
    private CountDownLatch doneSignal;
    private CountDownLatch checkSignal;
    public ReplicationTransitionToIdleObserver(    CountDownLatch doneSignal,    CountDownLatch checkSignal){
      this.doneSignal=doneSignal;
      this.checkSignal=checkSignal;
    }
    public void changed(    Replication.ChangeEvent event){
      Log.w(Log.TAG_SYNC,"String_Node_Str" + event.toString());
      if (event.getTransition() != null) {
        if (event.getTransition().getSource() != event.getTransition().getDestination() && event.getTransition().getDestination() == ReplicationState.IDLE) {
          Log.w(Log.TAG_SYNC,"String_Node_Str");
          Log.w(Log.TAG_SYNC,"String_Node_Str" + server.getRequestCount());
          this.doneSignal.countDown();
          if (EXPECTED_REQUEST_COUNT == server.getRequestCount() || EXPECTED_REQUEST_COUNT - 1 == server.getRequestCount()) {
            this.checkSignal.countDown();
          }
        }
      }
    }
  }
  CountDownLatch checkStateToIdle=new CountDownLatch(1);
  CountDownLatch checkRequestCount=new CountDownLatch(1);
  ReplicationTransitionToIdleObserver replicationTransitionToIdleObserver=new ReplicationTransitionToIdleObserver(checkStateToIdle,checkRequestCount);
  replication.addChangeListener(replicationTransitionToIdleObserver);
  Log.w(Log.TAG_SYNC,"String_Node_Str");
  for (int i=1; i <= 1; i++) {
    Map<String,Object> properties1=new HashMap<String,Object>();
    properties1.put("String_Node_Str" + String.valueOf(i),"String_Node_Str" + String.valueOf(i));
    final Document doc=createDocWithProperties(properties1);
  }
  success=checkStateToIdle.await(20,TimeUnit.SECONDS);
  assertTrue(success);
  success=checkRequestCount.await(20,TimeUnit.SECONDS);
  assertTrue(success);
  RecordedRequest request3=dispatcher.takeRequestBlocking(MockHelper.PATH_REGEX_BULK_DOCS);
  assertNotNull(request3);
  dispatcher.takeRecordedResponseBlocking(request3);
  Log.w(Log.TAG_SYNC,"String_Node_Str" + server.getRequestCount());
  assertTrue(EXPECTED_REQUEST_COUNT == server.getRequestCount() || EXPECTED_REQUEST_COUNT - 1 == server.getRequestCount());
  replication.removeChangeListener(replicationTransitionToIdleObserver);
  stopReplication(replication);
}