package com.couchbase.lite.testapp.ektorp.tests; import android.os.AsyncTask; import android.util.Log; import android.view.ViewGroup; import com.couchbase.lite.Database; import com.couchbase.lite.Emitter; import com.couchbase.lite.Mapper; import com.couchbase.lite.View; import com.couchbase.lite.ektorp.CBLiteHttpClient; import junit.framework.Assert; import org.ektorp.CouchDbConnector; import org.ektorp.CouchDbInstance; import org.ektorp.ViewQuery; import org.ektorp.android.util.ChangesFeedAsyncTask; import org.ektorp.android.util.CouchbaseViewListAdapter; import org.ektorp.android.util.EktorpAsyncTask; import org.ektorp.changes.ChangesCommand; import org.ektorp.changes.DocumentChange; import org.ektorp.http.HttpClient; import org.ektorp.impl.StdCouchDbInstance; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class EktorpAsyncTaskTest extends CBLiteEktorpTestCase { public static final String dDocName = "ddoc"; public static final String dDocId = "_design/" + dDocName; public static final String viewName = "aview"; // Regression test for https://github.com/couchbaselabs/TouchDB-Android/issues/71 public void testWedgedListAdaptor() { final CountDownLatch doneSignal = new CountDownLatch(1); HttpClient httpClient = new CBLiteHttpClient(manager); CouchDbInstance dbInstance = new StdCouchDbInstance(httpClient); // create a local database CouchDbConnector couchDbConnector = dbInstance.createConnector(DEFAULT_TEST_DB, true); createView(database); ViewQuery viewQuery = new ViewQuery().designDocId(dDocId).viewName(viewName); // create a list adapter with follow=true. will follow the changes feed in async task boolean follow = true; new CouchbaseViewListAdapter(couchDbConnector, viewQuery, follow) { @Override public android.view.View getView(int i, android.view.View view, ViewGroup viewGroup) { return null; } }; // wait until list adaptor starts up changes listener task // unfortunately, there is no way to hook into when the couchlistadapter's internal // async task actually starts reading from the changes feed, so using a timer // is the only way to try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } // create a dummy async task. if things are working ok, the doInBackground() // will get called back. OTOH if async tasks are wedged, it will never get called back. EktorpAsyncTask asyncTask = new EktorpAsyncTask() { @Override protected void doInBackground() { doneSignal.countDown(); } }; asyncTask.execute(); try { boolean wasSignalled = doneSignal.await(500, TimeUnit.MILLISECONDS); assertTrue("The async task was wedged by the changes feed task", wasSignalled); } catch (InterruptedException e) { e.printStackTrace(); } } // Regression test for https://github.com/couchbaselabs/TouchDB-Android/issues/71 public void testWedgedAsyncTasks() { final CountDownLatch doneSignal = new CountDownLatch(1); HttpClient httpClient = new CBLiteHttpClient(manager); CouchDbInstance dbInstance = new StdCouchDbInstance(httpClient); // create a local database CouchDbConnector couchDbConnector = dbInstance.createConnector(DEFAULT_TEST_DB, true); //create an ansyc task to get updates ChangesCommand changesCmd = new ChangesCommand.Builder().since(0) .includeDocs(false) .continuous(true) .heartbeat(5000) .build(); ChangesFeedAsyncTask couchChangesAsyncTask = new ChangesFeedAsyncTask(couchDbConnector, changesCmd) { @Override protected void handleDocumentChange(DocumentChange documentChange) { Log.d(TAG, "handleDocumentChange called"); } }; couchChangesAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void)null); // fix by using thread pool executor EktorpAsyncTask asyncTask = new EktorpAsyncTask() { @Override protected void doInBackground() { Log.d(TAG, "doInBackground() called"); doneSignal.countDown(); } }; asyncTask.execute(); try { boolean wasSignalled = doneSignal.await(500, TimeUnit.MILLISECONDS); assertTrue("The async task was wedged by the changes feed task", wasSignalled); } catch (InterruptedException e) { e.printStackTrace(); } } public static View createView(Database db) { View view = db.getView(String.format("%s/%s", dDocName, viewName)); view.setMap(new Mapper() { @Override public void map(Map<String, Object> document, Emitter emitter) { Assert.assertNotNull(document.get("_id")); Assert.assertNotNull(document.get("_rev")); if(document.get("key") != null) { emitter.emit(document.get("key"), null); } } }, "1"); return view; } }