/* 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.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Date; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mozilla.android.sync.test.SynchronizerHelpers.TrackingWBORepository; import org.mozilla.android.sync.test.helpers.WBORepository; import org.mozilla.android.sync.test.helpers.WaitHelper; import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.repositories.RepositorySessionBundle; import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord; import org.mozilla.gecko.sync.synchronizer.Synchronizer; import org.mozilla.gecko.sync.synchronizer.SynchronizerDelegate; import org.mozilla.gecko.sync.synchronizer.SynchronizerSession; import org.mozilla.gecko.sync.synchronizer.SynchronizerSessionDelegate; import android.content.Context; public class TestSynchronizer { public static final String LOG_TAG = "TestSynchronizer"; public static void assertInRangeInclusive(long earliest, long value, long latest) { assertTrue(earliest <= value); assertTrue(latest >= value); } public static void recordEquals(BookmarkRecord r, String guid, long lastModified, boolean deleted, String collection) { assertEquals(r.guid, guid); assertEquals(r.lastModified, lastModified); assertEquals(r.deleted, deleted); assertEquals(r.collection, collection); } public static void recordEquals(BookmarkRecord a, BookmarkRecord b) { assertEquals(a.guid, b.guid); assertEquals(a.lastModified, b.lastModified); assertEquals(a.deleted, b.deleted); assertEquals(a.collection, b.collection); } @Before public void setUp() { WaitHelper.resetTestWaiter(); } @After public void tearDown() { WaitHelper.resetTestWaiter(); } @Test public void testSynchronizerSession() { final Context context = null; final WBORepository repoA = new TrackingWBORepository(); final WBORepository repoB = new TrackingWBORepository(); final String collection = "bookmarks"; final boolean deleted = false; final String guidA = "abcdabcdabcd"; final String guidB = "ffffffffffff"; final String guidC = "xxxxxxxxxxxx"; final long lastModifiedA = 312345; final long lastModifiedB = 412340; final long lastModifiedC = 412345; BookmarkRecord bookmarkRecordA = new BookmarkRecord(guidA, collection, lastModifiedA, deleted); BookmarkRecord bookmarkRecordB = new BookmarkRecord(guidB, collection, lastModifiedB, deleted); BookmarkRecord bookmarkRecordC = new BookmarkRecord(guidC, collection, lastModifiedC, deleted); repoA.wbos.put(guidA, bookmarkRecordA); repoB.wbos.put(guidB, bookmarkRecordB); repoB.wbos.put(guidC, bookmarkRecordC); Synchronizer synchronizer = new Synchronizer(); synchronizer.repositoryA = repoA; synchronizer.repositoryB = repoB; final SynchronizerSession syncSession = new SynchronizerSession(synchronizer, new SynchronizerSessionDelegate() { @Override public void onInitialized(SynchronizerSession session) { assertFalse(repoA.wbos.containsKey(guidB)); assertFalse(repoA.wbos.containsKey(guidC)); assertFalse(repoB.wbos.containsKey(guidA)); assertTrue(repoA.wbos.containsKey(guidA)); assertTrue(repoB.wbos.containsKey(guidB)); assertTrue(repoB.wbos.containsKey(guidC)); session.synchronize(); } @Override public void onSynchronized(SynchronizerSession session) { try { assertEquals(1, session.getInboundCount()); assertEquals(2, session.getOutboundCount()); WaitHelper.getTestWaiter().performNotify(); } catch (Throwable e) { WaitHelper.getTestWaiter().performNotify(e); } } @Override public void onSynchronizeFailed(SynchronizerSession session, Exception lastException, String reason) { WaitHelper.getTestWaiter().performNotify(lastException); } @Override public void onSynchronizeSkipped(SynchronizerSession synchronizerSession) { WaitHelper.getTestWaiter().performNotify(new RuntimeException()); } }); WaitHelper.getTestWaiter().performWait(new Runnable() { @Override public void run() { syncSession.init(context, new RepositorySessionBundle(0), new RepositorySessionBundle(0)); } }); // Verify contents. assertTrue(repoA.wbos.containsKey(guidA)); assertTrue(repoA.wbos.containsKey(guidB)); assertTrue(repoA.wbos.containsKey(guidC)); assertTrue(repoB.wbos.containsKey(guidA)); assertTrue(repoB.wbos.containsKey(guidB)); assertTrue(repoB.wbos.containsKey(guidC)); BookmarkRecord aa = (BookmarkRecord) repoA.wbos.get(guidA); BookmarkRecord ab = (BookmarkRecord) repoA.wbos.get(guidB); BookmarkRecord ac = (BookmarkRecord) repoA.wbos.get(guidC); BookmarkRecord ba = (BookmarkRecord) repoB.wbos.get(guidA); BookmarkRecord bb = (BookmarkRecord) repoB.wbos.get(guidB); BookmarkRecord bc = (BookmarkRecord) repoB.wbos.get(guidC); recordEquals(aa, guidA, lastModifiedA, deleted, collection); recordEquals(ab, guidB, lastModifiedB, deleted, collection); recordEquals(ac, guidC, lastModifiedC, deleted, collection); recordEquals(ba, guidA, lastModifiedA, deleted, collection); recordEquals(bb, guidB, lastModifiedB, deleted, collection); recordEquals(bc, guidC, lastModifiedC, deleted, collection); recordEquals(aa, ba); recordEquals(ab, bb); recordEquals(ac, bc); } public abstract class SuccessfulSynchronizerDelegate implements SynchronizerDelegate { public long syncAOne = 0; public long syncBOne = 0; @Override public void onSynchronizeFailed(Synchronizer synchronizer, Exception lastException, String reason) { fail("Should not fail."); } } @Test public void testSynchronizerPersists() { final Object monitor = new Object(); final long earliest = new Date().getTime(); Context context = null; final WBORepository repoA = new WBORepository(); final WBORepository repoB = new WBORepository(); Synchronizer synchronizer = new Synchronizer(); synchronizer.bundleA = new RepositorySessionBundle(0); synchronizer.bundleB = new RepositorySessionBundle(0); synchronizer.repositoryA = repoA; synchronizer.repositoryB = repoB; final SuccessfulSynchronizerDelegate delegateOne = new SuccessfulSynchronizerDelegate() { @Override public void onSynchronized(Synchronizer synchronizer) { Logger.trace(LOG_TAG, "onSynchronized. Success!"); syncAOne = synchronizer.bundleA.getTimestamp(); syncBOne = synchronizer.bundleB.getTimestamp(); synchronized (monitor) { monitor.notify(); } } }; final SuccessfulSynchronizerDelegate delegateTwo = new SuccessfulSynchronizerDelegate() { @Override public void onSynchronized(Synchronizer synchronizer) { Logger.trace(LOG_TAG, "onSynchronized. Success!"); syncAOne = synchronizer.bundleA.getTimestamp(); syncBOne = synchronizer.bundleB.getTimestamp(); synchronized (monitor) { monitor.notify(); } } }; synchronized (monitor) { synchronizer.synchronize(context, delegateOne); try { monitor.wait(); } catch (InterruptedException e) { fail("Interrupted."); } } long now = new Date().getTime(); Logger.trace(LOG_TAG, "Earliest is " + earliest); Logger.trace(LOG_TAG, "syncAOne is " + delegateOne.syncAOne); Logger.trace(LOG_TAG, "syncBOne is " + delegateOne.syncBOne); Logger.trace(LOG_TAG, "Now: " + now); assertInRangeInclusive(earliest, delegateOne.syncAOne, now); assertInRangeInclusive(earliest, delegateOne.syncBOne, now); try { Thread.sleep(10); } catch (InterruptedException e) { fail("Thread interrupted!"); } synchronized (monitor) { synchronizer.synchronize(context, delegateTwo); try { monitor.wait(); } catch (InterruptedException e) { fail("Interrupted."); } } now = new Date().getTime(); Logger.trace(LOG_TAG, "Earliest is " + earliest); Logger.trace(LOG_TAG, "syncAOne is " + delegateTwo.syncAOne); Logger.trace(LOG_TAG, "syncBOne is " + delegateTwo.syncBOne); Logger.trace(LOG_TAG, "Now: " + now); assertInRangeInclusive(earliest, delegateTwo.syncAOne, now); assertInRangeInclusive(earliest, delegateTwo.syncBOne, now); assertTrue(delegateTwo.syncAOne > delegateOne.syncAOne); assertTrue(delegateTwo.syncBOne > delegateOne.syncBOne); Logger.trace(LOG_TAG, "Reached end of test."); } private Synchronizer getTestSynchronizer(long tsA, long tsB) { WBORepository repoA = new TrackingWBORepository(); WBORepository repoB = new TrackingWBORepository(); Synchronizer synchronizer = new Synchronizer(); synchronizer.bundleA = new RepositorySessionBundle(tsA); synchronizer.bundleB = new RepositorySessionBundle(tsB); synchronizer.repositoryA = repoA; synchronizer.repositoryB = repoB; return synchronizer; } /** * Let's put data in two repos and synchronize them with last sync * timestamps later than all of the records. Verify that no records * are exchanged. */ @Test public void testSynchronizerFakeTimestamps() { final Context context = null; final String collection = "bookmarks"; final boolean deleted = false; final String guidA = "abcdabcdabcd"; final String guidB = "ffffffffffff"; final long lastModifiedA = 312345; final long lastModifiedB = 412345; BookmarkRecord bookmarkRecordA = new BookmarkRecord(guidA, collection, lastModifiedA, deleted); BookmarkRecord bookmarkRecordB = new BookmarkRecord(guidB, collection, lastModifiedB, deleted); final Synchronizer synchronizer = getTestSynchronizer(lastModifiedA + 10, lastModifiedB + 10); final WBORepository repoA = (WBORepository) synchronizer.repositoryA; final WBORepository repoB = (WBORepository) synchronizer.repositoryB; repoA.wbos.put(guidA, bookmarkRecordA); repoB.wbos.put(guidB, bookmarkRecordB); WaitHelper.getTestWaiter().performWait(new Runnable() { @Override public void run() { synchronizer.synchronize(context, new SynchronizerDelegate() { @Override public void onSynchronized(Synchronizer synchronizer) { try { // No records get sent either way. final SynchronizerSession synchronizerSession = synchronizer.getSynchronizerSession(); assertNotNull(synchronizerSession); assertEquals(0, synchronizerSession.getInboundCount()); assertEquals(0, synchronizerSession.getOutboundCount()); WaitHelper.getTestWaiter().performNotify(); } catch (Throwable e) { WaitHelper.getTestWaiter().performNotify(e); } } @Override public void onSynchronizeFailed(Synchronizer synchronizer, Exception lastException, String reason) { WaitHelper.getTestWaiter().performNotify(lastException); } }); } }); // Verify contents. assertTrue(repoA.wbos.containsKey(guidA)); assertTrue(repoB.wbos.containsKey(guidB)); assertFalse(repoB.wbos.containsKey(guidA)); assertFalse(repoA.wbos.containsKey(guidB)); BookmarkRecord aa = (BookmarkRecord) repoA.wbos.get(guidA); BookmarkRecord ab = (BookmarkRecord) repoA.wbos.get(guidB); BookmarkRecord ba = (BookmarkRecord) repoB.wbos.get(guidA); BookmarkRecord bb = (BookmarkRecord) repoB.wbos.get(guidB); assertNull(ab); assertNull(ba); recordEquals(aa, guidA, lastModifiedA, deleted, collection); recordEquals(bb, guidB, lastModifiedB, deleted, collection); } @Test public void testSynchronizer() { final Context context = null; final String collection = "bookmarks"; final boolean deleted = false; final String guidA = "abcdabcdabcd"; final String guidB = "ffffffffffff"; final String guidC = "gggggggggggg"; final long lastModifiedA = 312345; final long lastModifiedB = 412340; final long lastModifiedC = 412345; BookmarkRecord bookmarkRecordA = new BookmarkRecord(guidA, collection, lastModifiedA, deleted); BookmarkRecord bookmarkRecordB = new BookmarkRecord(guidB, collection, lastModifiedB, deleted); BookmarkRecord bookmarkRecordC = new BookmarkRecord(guidC, collection, lastModifiedC, deleted); final Synchronizer synchronizer = getTestSynchronizer(0, 0); final WBORepository repoA = (WBORepository) synchronizer.repositoryA; final WBORepository repoB = (WBORepository) synchronizer.repositoryB; repoA.wbos.put(guidA, bookmarkRecordA); repoB.wbos.put(guidB, bookmarkRecordB); repoB.wbos.put(guidC, bookmarkRecordC); WaitHelper.getTestWaiter().performWait(new Runnable() { @Override public void run() { synchronizer.synchronize(context, new SynchronizerDelegate() { @Override public void onSynchronized(Synchronizer synchronizer) { try { // No records get sent either way. final SynchronizerSession synchronizerSession = synchronizer.getSynchronizerSession(); assertNotNull(synchronizerSession); assertEquals(1, synchronizerSession.getInboundCount()); assertEquals(2, synchronizerSession.getOutboundCount()); WaitHelper.getTestWaiter().performNotify(); } catch (Throwable e) { WaitHelper.getTestWaiter().performNotify(e); } } @Override public void onSynchronizeFailed(Synchronizer synchronizer, Exception lastException, String reason) { WaitHelper.getTestWaiter().performNotify(lastException); } }); } }); // Verify contents. assertTrue(repoA.wbos.containsKey(guidA)); assertTrue(repoA.wbos.containsKey(guidB)); assertTrue(repoA.wbos.containsKey(guidC)); assertTrue(repoB.wbos.containsKey(guidA)); assertTrue(repoB.wbos.containsKey(guidB)); assertTrue(repoB.wbos.containsKey(guidC)); BookmarkRecord aa = (BookmarkRecord) repoA.wbos.get(guidA); BookmarkRecord ab = (BookmarkRecord) repoA.wbos.get(guidB); BookmarkRecord ac = (BookmarkRecord) repoA.wbos.get(guidC); BookmarkRecord ba = (BookmarkRecord) repoB.wbos.get(guidA); BookmarkRecord bb = (BookmarkRecord) repoB.wbos.get(guidB); BookmarkRecord bc = (BookmarkRecord) repoB.wbos.get(guidC); recordEquals(aa, guidA, lastModifiedA, deleted, collection); recordEquals(ab, guidB, lastModifiedB, deleted, collection); recordEquals(ac, guidC, lastModifiedC, deleted, collection); recordEquals(ba, guidA, lastModifiedA, deleted, collection); recordEquals(bb, guidB, lastModifiedB, deleted, collection); recordEquals(bc, guidC, lastModifiedC, deleted, collection); recordEquals(aa, ba); recordEquals(ab, bb); recordEquals(ac, bc); } }