/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ package org.mozilla.android.sync.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import org.mozilla.android.sync.test.SynchronizerHelpers.FailFetchWBORepository; import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositorySessionCreationDelegate; import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositorySessionFinishDelegate; import org.mozilla.android.sync.test.helpers.WBORepository; import org.mozilla.android.sync.test.helpers.WaitHelper; import org.mozilla.gecko.sync.repositories.InactiveSessionException; import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException; import org.mozilla.gecko.sync.repositories.Repository; import org.mozilla.gecko.sync.repositories.RepositorySession; import org.mozilla.gecko.sync.repositories.RepositorySessionBundle; import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord; import org.mozilla.gecko.sync.synchronizer.RecordsChannel; import org.mozilla.gecko.sync.synchronizer.RecordsChannelDelegate; public class TestRecordsChannel { protected WBORepository remote; protected WBORepository local; protected RepositorySession source; protected RepositorySession sink; protected RecordsChannelDelegate rcDelegate; protected AtomicInteger numFlowFetchFailed; protected AtomicInteger numFlowStoreFailed; protected AtomicInteger numFlowCompleted; protected AtomicBoolean flowBeginFailed; protected AtomicBoolean flowFinishFailed; public void doFlow(final Repository remote, final Repository local) throws Exception { WaitHelper.getTestWaiter().performWait(new Runnable() { @Override public void run() { remote.createSession(new ExpectSuccessRepositorySessionCreationDelegate(WaitHelper.getTestWaiter()) { @Override public void onSessionCreated(RepositorySession session) { source = session; local.createSession(new ExpectSuccessRepositorySessionCreationDelegate(WaitHelper.getTestWaiter()) { @Override public void onSessionCreated(RepositorySession session) { sink = session; WaitHelper.getTestWaiter().performNotify(); } }, null); } }, null); } }); assertNotNull(source); assertNotNull(sink); numFlowFetchFailed = new AtomicInteger(0); numFlowStoreFailed = new AtomicInteger(0); numFlowCompleted = new AtomicInteger(0); flowBeginFailed = new AtomicBoolean(false); flowFinishFailed = new AtomicBoolean(false); rcDelegate = new RecordsChannelDelegate() { @Override public void onFlowFetchFailed(RecordsChannel recordsChannel, Exception ex) { numFlowFetchFailed.incrementAndGet(); } @Override public void onFlowStoreFailed(RecordsChannel recordsChannel, Exception ex, String recordGuid) { numFlowStoreFailed.incrementAndGet(); } @Override public void onFlowFinishFailed(RecordsChannel recordsChannel, Exception ex) { flowFinishFailed.set(true); WaitHelper.getTestWaiter().performNotify(); } @Override public void onFlowCompleted(RecordsChannel recordsChannel, long fetchEnd, long storeEnd) { numFlowCompleted.incrementAndGet(); try { sink.finish(new ExpectSuccessRepositorySessionFinishDelegate(WaitHelper.getTestWaiter()) { @Override public void onFinishSucceeded(RepositorySession session, RepositorySessionBundle bundle) { try { source.finish(new ExpectSuccessRepositorySessionFinishDelegate(WaitHelper.getTestWaiter()) { @Override public void onFinishSucceeded(RepositorySession session, RepositorySessionBundle bundle) { performNotify(); } }); } catch (InactiveSessionException e) { WaitHelper.getTestWaiter().performNotify(e); } } }); } catch (InactiveSessionException e) { WaitHelper.getTestWaiter().performNotify(e); } } @Override public void onFlowBeginFailed(RecordsChannel recordsChannel, Exception ex) { flowBeginFailed.set(true); WaitHelper.getTestWaiter().performNotify(); } }; final RecordsChannel rc = new RecordsChannel(source, sink, rcDelegate); WaitHelper.getTestWaiter().performWait(new Runnable() { @Override public void run() { try { rc.beginAndFlow(); } catch (InvalidSessionTransitionException e) { WaitHelper.getTestWaiter().performNotify(e); } } }); } public static final BookmarkRecord[] inbounds = new BookmarkRecord[] { new BookmarkRecord("inboundSucc1", "bookmarks", 1, false), new BookmarkRecord("inboundSucc2", "bookmarks", 1, false), new BookmarkRecord("inboundFail1", "bookmarks", 1, false), new BookmarkRecord("inboundSucc3", "bookmarks", 1, false), new BookmarkRecord("inboundSucc4", "bookmarks", 1, false), new BookmarkRecord("inboundFail2", "bookmarks", 1, false), }; public static final BookmarkRecord[] outbounds = new BookmarkRecord[] { new BookmarkRecord("outboundSucc1", "bookmarks", 1, false), new BookmarkRecord("outboundSucc2", "bookmarks", 1, false), new BookmarkRecord("outboundSucc3", "bookmarks", 1, false), new BookmarkRecord("outboundSucc4", "bookmarks", 1, false), new BookmarkRecord("outboundSucc5", "bookmarks", 1, false), new BookmarkRecord("outboundFail6", "bookmarks", 1, false), }; protected WBORepository empty() { WBORepository repo = new SynchronizerHelpers.TrackingWBORepository(); return repo; } protected WBORepository full() { WBORepository repo = new SynchronizerHelpers.TrackingWBORepository(); for (BookmarkRecord outbound : outbounds) { repo.wbos.put(outbound.guid, outbound); } return repo; } protected WBORepository failingFetch() { WBORepository repo = new FailFetchWBORepository(); for (BookmarkRecord outbound : outbounds) { repo.wbos.put(outbound.guid, outbound); } return repo; } @Test public void testSuccess() throws Exception { WBORepository source = full(); WBORepository sink = empty(); doFlow(source, sink); assertEquals(1, numFlowCompleted.get()); assertEquals(0, numFlowFetchFailed.get()); assertEquals(0, numFlowStoreFailed.get()); assertEquals(source.wbos, sink.wbos); } @Test public void testFetchFail() throws Exception { WBORepository source = failingFetch(); WBORepository sink = empty(); doFlow(source, sink); assertEquals(1, numFlowCompleted.get()); assertTrue(numFlowFetchFailed.get() > 0); assertEquals(0, numFlowStoreFailed.get()); assertTrue(sink.wbos.size() < 6); } @Test public void testStoreSerialFail() throws Exception { WBORepository source = full(); WBORepository sink = new SynchronizerHelpers.SerialFailStoreWBORepository(); doFlow(source, sink); assertEquals(1, numFlowCompleted.get()); assertEquals(0, numFlowFetchFailed.get()); assertEquals(1, numFlowStoreFailed.get()); assertEquals(5, sink.wbos.size()); } @Test public void testStoreBatchesFail() throws Exception { WBORepository source = full(); WBORepository sink = new SynchronizerHelpers.BatchFailStoreWBORepository(3); doFlow(source, sink); assertEquals(1, numFlowCompleted.get()); assertEquals(0, numFlowFetchFailed.get()); assertEquals(3, numFlowStoreFailed.get()); // One batch fails. assertEquals(3, sink.wbos.size()); // One batch succeeds. } @Test public void testStoreOneBigBatchFail() throws Exception { WBORepository source = full(); WBORepository sink = new SynchronizerHelpers.BatchFailStoreWBORepository(50); doFlow(source, sink); assertEquals(1, numFlowCompleted.get()); assertEquals(0, numFlowFetchFailed.get()); assertEquals(6, numFlowStoreFailed.get()); // One (big) batch fails. assertEquals(0, sink.wbos.size()); // No batches succeed. } }