// depends on testRunPushReplication
  private void testRunPullReplication() throws Throwable {
    URL remoteDbURL = new URL(syncGatewayUrl + pushThenPullDBName);
    Database db = startDatabase();

    Log.i(TAG, "Pulling...");
    Replication repl = db.getPullReplication(remoteDbURL);
    runReplication(repl);
    assertNull(repl.getLastError());

    Log.i(TAG, "Verifying documents...");
    for (int i = 1; i <= kNDocuments; i++) {
      Document doc = db.getDocument("doc-" + i);
      assertEquals(i, doc.getProperty("index"));
      assertEquals(false, doc.getProperty("bar"));
    }
  }
  public void failingRunPushReplication() throws Throwable {
    URL remoteDbURL = new URL(syncGatewayUrl + pushThenPullDBName);
    // java.net.ConnectException: failed to connect to /127.0.0.1 (port 4985): connect failed:
    // ECONNREFUSED (Connection refused)
    // RemoteRequest remoteRequest = new RemoteRequest(null,
    // CouchbaseLiteHttpClientFactory.INSTANCE, "DELETE", remoteDbURL, null, null);
    // remoteRequest.run();
    Database db = startDatabase();

    Log.i(TAG, "Creating " + kNDocuments + " documents...");
    createDocumentsAsync(db, kNDocuments);

    Log.i(TAG, "Pushing...");
    Replication repl = db.getPullReplication(remoteDbURL);
    repl.setCreateTarget(true);
    runReplication(repl);
    assertNull(repl.getLastError());

    testRunPullReplication();
  }
  /** https://github.com/couchbase/couchbase-lite-java-core/issues/291 */
  public void testCallReplicateTwice() throws Exception {

    // create mock sync gateway that will serve as a pull target and return random docs
    int numMockDocsToServe = 0;
    MockDispatcher dispatcher = new MockDispatcher();
    MockWebServer server =
        MockHelper.getPreloadedPullTargetMockCouchDB(dispatcher, numMockDocsToServe, 1);
    dispatcher.setServerType(MockDispatcher.ServerType.COUCHDB);
    server.setDispatcher(dispatcher);
    server.play();

    // kick off 1st replication via REST api
    Map<String, Object> replicateJsonMap = getPullReplicationParsedJson(server.getUrl("/db"));
    Log.i(TAG, "map: " + replicateJsonMap);

    Log.i(TAG, "Call 1st /_replicate");
    Map<String, Object> result =
        (Map<String, Object>) sendBody("POST", "/_replicate", replicateJsonMap, Status.OK, null);
    Log.i(TAG, "result: " + result);
    assertNotNull(result.get("session_id"));
    String sessionId1 = (String) result.get("session_id");

    // NOTE: one short replication should be blocked. sendBody() waits till response is ready.
    //      https://github.com/couchbase/couchbase-lite-android/issues/204

    // kick off 2nd replication via REST api
    Log.i(TAG, "Call 2nd /_replicate");
    Map<String, Object> result2 =
        (Map<String, Object>) sendBody("POST", "/_replicate", replicateJsonMap, Status.OK, null);
    Log.i(TAG, "result2: " + result2);
    assertNotNull(result2.get("session_id"));
    String sessionId2 = (String) result2.get("session_id");

    // wait for replication to finish
    boolean success = waitForReplicationToFinish();
    assertTrue(success);

    // kick off 3rd replication via REST api
    Log.i(TAG, "Call 3rd /_replicate");
    Map<String, Object> result3 =
        (Map<String, Object>) sendBody("POST", "/_replicate", replicateJsonMap, Status.OK, null);
    Log.i(TAG, "result3: " + result3);
    assertNotNull(result3.get("session_id"));
    String sessionId3 = (String) result3.get("session_id");

    // wait for replication to finish
    boolean success3 = waitForReplicationToFinish();
    assertTrue(success3);

    assertFalse(sessionId1.equals(sessionId2));
    assertFalse(sessionId1.equals(sessionId3));
    assertFalse(sessionId2.equals(sessionId3));

    // cleanup
    server.shutdown();
  }
 static void runReplication(Replication repl) throws InterruptedException {
   Log.i(TAG, "Waiting for " + repl + " to finish...");
   boolean started = false, done = false;
   repl.start();
   long lastTime = System.currentTimeMillis();
   ;
   while (!done) {
     if (repl.isRunning()) {
       started = true;
     }
     // TODO getMode() always throws UnsupportedOperationException (see ios test)
     if (started
         && (repl.getMode() == Replication.ReplicationMode.REPLICATION_ACTIVE
             || repl.getMode() == Replication.ReplicationMode.REPLICATION_ACTIVE)) {
       done = true;
     }
     // Replication runs on a background thread, so the main runloop should not be blocked.
     // Make sure it's spinning in a timely manner:
     long now = System.currentTimeMillis();
     if (lastTime > 0 && now - lastTime > 25)
       Log.w(TAG, "Runloop was blocked for " + (now - lastTime) * 100 + " sec");
     lastTime = now;
     Thread.sleep(100);
     break;
   }
   if (repl.getLastError() == null) {
     Log.i(
         TAG,
         String.format(
             "...replicator finished. progress %d/%d without error",
             repl.getCompletedChangesCount(), repl.getChangesCount()));
   } else {
     Log.i(
         TAG,
         String.format(
             "...replicator finished. progress %d/%d, error=%s",
             repl.getCompletedChangesCount(),
             repl.getChangesCount(),
             repl.getLastError().toString()));
   }
 }
  /** https://github.com/couchbase/couchbase-lite-java-core/issues/291 */
  public void testCallContinuousReplicateTwice() throws Exception {

    // create mock sync gateway that will serve as a pull target and return random docs
    int numMockDocsToServe = 0;
    MockDispatcher dispatcher = new MockDispatcher();
    MockWebServer server =
        MockHelper.getPreloadedPullTargetMockCouchDB(dispatcher, numMockDocsToServe, 1);
    dispatcher.setServerType(MockDispatcher.ServerType.COUCHDB);
    server.setDispatcher(dispatcher);
    server.play();

    // kick off 1st replication via REST api
    Map<String, Object> replicateJsonMap = getPullReplicationParsedJson(server.getUrl("/db"));
    replicateJsonMap.put("continuous", true);
    Log.i(TAG, "map: " + replicateJsonMap);

    Log.i(TAG, "Call 1st /_replicate");
    Map<String, Object> result =
        (Map<String, Object>) sendBody("POST", "/_replicate", replicateJsonMap, Status.OK, null);
    Log.i(TAG, "result: " + result);
    assertNotNull(result.get("session_id"));
    String sessionId1 = (String) result.get("session_id");

    // no wait, immediately call new _replicate REST API

    // kick off 2nd replication via REST api => Should be
    Log.i(TAG, "Call 2nd /_replicate");
    Map<String, Object> result2 =
        (Map<String, Object>) sendBody("POST", "/_replicate", replicateJsonMap, Status.OK, null);
    Log.i(TAG, "result2: " + result2);
    assertNotNull(result2.get("session_id"));
    String sessionId2 = (String) result2.get("session_id");

    // 20 sec is to wait replicator becomes IDLE
    try {
      Thread.sleep(20 * 1000);
    } catch (Exception e) {
    }

    // kick off 34d replication via REST api => Should be
    Log.i(TAG, "Call 3rd /_replicate");
    Map<String, Object> result3 =
        (Map<String, Object>) sendBody("POST", "/_replicate", replicateJsonMap, Status.OK, null);
    Log.i(TAG, "result3: " + result3);
    assertNotNull(result3.get("session_id"));
    String sessionId3 = (String) result3.get("session_id");

    // 20 sec is to wait replicator becomes IDLE
    try {
      Thread.sleep(20 * 1000);
    } catch (Exception e) {
    }

    // Cancel Replicator
    replicateJsonMap.put("cancel", true);
    Log.i(TAG, "map: " + replicateJsonMap);
    Map<String, Object> result4 =
        (Map<String, Object>) sendBody("POST", "/_replicate", replicateJsonMap, Status.OK, null);
    Log.i(TAG, "result4: " + result4);

    // wait for replication to finish
    boolean success = waitForReplicationToFinish();
    assertTrue(success);

    assertTrue(sessionId1.equals(sessionId2));
    assertTrue(sessionId1.equals(sessionId3));

    // cleanup
    server.shutdown();
  }