--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/ServerSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/ServerSyncStage.java
@@ -31,17 +31,16 @@ import org.mozilla.gecko.sync.repositori
import org.mozilla.gecko.sync.repositories.RecordFactory;
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.RepositoryStateProvider;
import org.mozilla.gecko.sync.repositories.Server15Repository;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate;
-import org.mozilla.gecko.sync.synchronizer.ServerLocalSynchronizer;
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.telemetry.TelemetryCollector;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
@@ -225,17 +224,17 @@ public abstract class ServerSyncStage ex
protected void persistConfig(SynchronizerConfiguration synchronizerConfiguration) {
synchronizerConfiguration.persist(session.config.getBranch(bundlePrefix()));
}
public Synchronizer getConfiguredSynchronizer(GlobalSession session) throws NoCollectionKeysSetException, URISyntaxException, NonObjectJSONException, IOException {
Repository remote = wrappedServerRepo();
- Synchronizer synchronizer = new ServerLocalSynchronizer();
+ Synchronizer synchronizer = new Synchronizer();
synchronizer.repositoryA = remote;
synchronizer.repositoryB = this.getLocalRepository();
synchronizer.load(getConfig());
return synchronizer;
}
/**
deleted file mode 100644
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/synchronizer/ServerLocalSynchronizer.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.sync.synchronizer;
-
-/**
- * A <code>SynchronizerSession</code> designed to be used between a remote
- * server and a local repository.
- * <p>
- * See <code>ServerLocalSynchronizerSession</code> for error handling details.
- */
-public class ServerLocalSynchronizer extends Synchronizer {
- @Override
- public SynchronizerSession newSynchronizerSession() {
- return new ServerLocalSynchronizerSession(this, this);
- }
-}
deleted file mode 100644
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/synchronizer/ServerLocalSynchronizerSession.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.sync.synchronizer;
-
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.sync.ReflowIsNecessaryException;
-import org.mozilla.gecko.sync.repositories.FetchFailedException;
-import org.mozilla.gecko.sync.repositories.StoreFailedException;
-
-/**
- * A <code>SynchronizerSession</code> designed to be used between a remote
- * server and a local repository.
- * <p>
- * Handles failure cases as follows (in the order they will occur during a sync):
- * <ul>
- * <li>Remote fetch failures abort.</li>
- * <li>Local store failures are ignored.</li>
- * <li>Local fetch failures abort.</li>
- * <li>Remote store failures abort.</li>
- * </ul>
- */
-public class ServerLocalSynchronizerSession extends SynchronizerSession {
- protected static final String LOG_TAG = "ServLocSynchronizerSess";
-
- public ServerLocalSynchronizerSession(Synchronizer synchronizer, SynchronizerSessionDelegate delegate) {
- super(synchronizer, delegate);
- }
-
- @Override
- public void onFirstFlowCompleted(RecordsChannel recordsChannel) {
- // If a "reflow exception" was thrown, consider this synchronization failed.
- final ReflowIsNecessaryException reflowException = recordsChannel.getReflowException();
- if (reflowException != null) {
- final String message = "Reflow is necessary: " + reflowException;
- Logger.warn(LOG_TAG, message + " Aborting session.");
- delegate.onSynchronizeFailed(this, reflowException, message);
- return;
- }
-
- // Fetch failures always abort.
- int numRemoteFetchFailed = recordsChannel.getFetchFailureCount();
- if (numRemoteFetchFailed > 0) {
- final String message = "Got " + numRemoteFetchFailed + " failures fetching remote records!";
- Logger.warn(LOG_TAG, message + " Aborting session.");
- delegate.onSynchronizeFailed(this, new FetchFailedException(), message);
- return;
- }
- Logger.trace(LOG_TAG, "No failures fetching remote records.");
-
- // Local store failures are ignored.
- int numLocalStoreFailed = recordsChannel.getStoreFailureCount();
- if (numLocalStoreFailed > 0) {
- final String message = "Got " + numLocalStoreFailed + " failures storing local records!";
- Logger.warn(LOG_TAG, message + " Ignoring local store failures and continuing synchronizer session.");
- } else {
- Logger.trace(LOG_TAG, "No failures storing local records.");
- }
-
- super.onFirstFlowCompleted(recordsChannel);
- }
-
- @Override
- public void onSecondFlowCompleted(RecordsChannel recordsChannel) {
- // If a "reflow exception" was thrown, consider this synchronization failed.
- final ReflowIsNecessaryException reflowException = recordsChannel.getReflowException();
- if (reflowException != null) {
- final String message = "Reflow is necessary: " + reflowException;
- Logger.warn(LOG_TAG, message + " Aborting session.");
- delegate.onSynchronizeFailed(this, reflowException, message);
- return;
- }
-
- // Fetch failures always abort.
- int numLocalFetchFailed = recordsChannel.getFetchFailureCount();
- if (numLocalFetchFailed > 0) {
- final String message = "Got " + numLocalFetchFailed + " failures fetching local records!";
- Logger.warn(LOG_TAG, message + " Aborting session.");
- delegate.onSynchronizeFailed(this, new FetchFailedException(), message);
- return;
- }
- Logger.trace(LOG_TAG, "No failures fetching local records.");
-
- // Remote store failures abort!
- int numRemoteStoreFailed = recordsChannel.getStoreFailureCount();
- if (numRemoteStoreFailed > 0) {
- final String message = "Got " + numRemoteStoreFailed + " failures storing remote records!";
- Logger.warn(LOG_TAG, message + " Aborting session.");
- delegate.onSynchronizeFailed(this, new StoreFailedException(), message);
- return;
- }
- Logger.trace(LOG_TAG, "No failures storing remote records.");
-
- super.onSecondFlowCompleted(recordsChannel);
- }
-}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/synchronizer/SynchronizerSession.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/synchronizer/SynchronizerSession.java
@@ -1,40 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.synchronizer;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.mozilla.gecko.background.common.log.Logger;
+import org.mozilla.gecko.sync.ReflowIsNecessaryException;
import org.mozilla.gecko.sync.SyncException;
import org.mozilla.gecko.sync.synchronizer.StoreBatchTracker.Batch;
+import org.mozilla.gecko.sync.repositories.FetchFailedException;
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
import org.mozilla.gecko.sync.repositories.RepositorySession;
import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
+import org.mozilla.gecko.sync.repositories.StoreFailedException;
import org.mozilla.gecko.sync.repositories.delegates.DeferredRepositorySessionFinishDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate;
import android.content.Context;
/**
* I coordinate the moving parts of a sync started by
* {@link Synchronizer#synchronize}.
*
* I flow records twice: first from A to B, and then from B to A. I provide
* fine-grained feedback by calling my delegate's callback methods.
*
+ * I handle failure cases during flows as follows (in the order they will occur during a sync):
+ * <ul>
+ * <li>Remote fetch failures abort.</li>
+ * <li>Local store failures are ignored.</li>
+ * <li>Local fetch failures abort.</li>
+ * <li>Remote store failures abort.</li>
+ * </ul>
+ *
* Initialize me by creating me with a Synchronizer and a
* SynchronizerSessionDelegate. Kick things off by calling `init` with two
* RepositorySessionBundles, and then call `synchronize` in your `onInitialized`
* callback.
*
* I always call exactly one of my delegate's `onInitialized` or
* `onSessionError` callback methods from `init`.
*
@@ -302,16 +312,44 @@ public class SynchronizerSession impleme
/**
* Called after the first flow completes.
* <p>
* By default, any fetch and store failures are ignored.
* @param recordsChannel the <code>RecordsChannel</code> (for error testing).
*/
public void onFirstFlowCompleted(RecordsChannel recordsChannel) {
+ // If a "reflow exception" was thrown, consider this synchronization failed.
+ final ReflowIsNecessaryException reflowException = recordsChannel.getReflowException();
+ if (reflowException != null) {
+ final String message = "Reflow is necessary: " + reflowException;
+ Logger.warn(LOG_TAG, message + " Aborting session.");
+ delegate.onSynchronizeFailed(this, reflowException, message);
+ return;
+ }
+
+ // Fetch failures always abort.
+ int numRemoteFetchFailed = recordsChannel.getFetchFailureCount();
+ if (numRemoteFetchFailed > 0) {
+ final String message = "Got " + numRemoteFetchFailed + " failures fetching remote records!";
+ Logger.warn(LOG_TAG, message + " Aborting session.");
+ delegate.onSynchronizeFailed(this, new FetchFailedException(), message);
+ return;
+ }
+ Logger.trace(LOG_TAG, "No failures fetching remote records.");
+
+ // Local store failures are ignored.
+ int numLocalStoreFailed = recordsChannel.getStoreFailureCount();
+ if (numLocalStoreFailed > 0) {
+ final String message = "Got " + numLocalStoreFailed + " failures storing local records!";
+ Logger.warn(LOG_TAG, message + " Ignoring local store failures and continuing synchronizer session.");
+ } else {
+ Logger.trace(LOG_TAG, "No failures storing local records.");
+ }
+
Logger.trace(LOG_TAG, "First RecordsChannel onFlowCompleted.");
pendingATimestamp = sessionA.getLastFetchTimestamp();
storeEndBTimestamp = sessionB.getLastStoreTimestamp();
Logger.debug(LOG_TAG, "Fetch end is " + pendingATimestamp + ". Store end is " + storeEndBTimestamp + ". Starting next.");
numInboundRecords.set(recordsChannel.getFetchCount());
numInboundRecordsStored.set(recordsChannel.getStoreAcceptedCount());
numInboundRecordsFailed.set(recordsChannel.getStoreFailureCount());
numInboundRecordsReconciled.set(recordsChannel.getStoreReconciledCount());
@@ -321,16 +359,45 @@ public class SynchronizerSession impleme
/**
* Called after the second flow completes.
* <p>
* By default, any fetch and store failures are ignored.
* @param recordsChannel the <code>RecordsChannel</code> (for error testing).
*/
public void onSecondFlowCompleted(RecordsChannel recordsChannel) {
+ // If a "reflow exception" was thrown, consider this synchronization failed.
+ final ReflowIsNecessaryException reflowException = recordsChannel.getReflowException();
+ if (reflowException != null) {
+ final String message = "Reflow is necessary: " + reflowException;
+ Logger.warn(LOG_TAG, message + " Aborting session.");
+ delegate.onSynchronizeFailed(this, reflowException, message);
+ return;
+ }
+
+ // Fetch failures always abort.
+ int numLocalFetchFailed = recordsChannel.getFetchFailureCount();
+ if (numLocalFetchFailed > 0) {
+ final String message = "Got " + numLocalFetchFailed + " failures fetching local records!";
+ Logger.warn(LOG_TAG, message + " Aborting session.");
+ delegate.onSynchronizeFailed(this, new FetchFailedException(), message);
+ return;
+ }
+ Logger.trace(LOG_TAG, "No failures fetching local records.");
+
+ // Remote store failures abort!
+ int numRemoteStoreFailed = recordsChannel.getStoreFailureCount();
+ if (numRemoteStoreFailed > 0) {
+ final String message = "Got " + numRemoteStoreFailed + " failures storing remote records!";
+ Logger.warn(LOG_TAG, message + " Aborting session.");
+ delegate.onSynchronizeFailed(this, new StoreFailedException(), message);
+ return;
+ }
+ Logger.trace(LOG_TAG, "No failures storing remote records.");
+
Logger.trace(LOG_TAG, "Second RecordsChannel onFlowCompleted.");
pendingBTimestamp = sessionB.getLastFetchTimestamp();
storeEndATimestamp = sessionA.getLastStoreTimestamp();
Logger.debug(LOG_TAG, "Fetch end is " + pendingBTimestamp + ". Store end is " + storeEndATimestamp + ". Finishing.");
numOutboundRecords.set(recordsChannel.getFetchCount());
numOutboundRecordsStored.set(recordsChannel.getStoreAcceptedCount());
numOutboundRecordsFailed.set(recordsChannel.getStoreFailureCount());
outboundBatches.set(recordsChannel.getStoreBatches());
--- a/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestServer15RepositorySession.java
+++ b/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestServer15RepositorySession.java
@@ -23,17 +23,16 @@ import org.mozilla.gecko.sync.net.BaseRe
import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
import org.mozilla.gecko.sync.net.SyncStorageResponse;
import org.mozilla.gecko.sync.repositories.FetchFailedException;
import org.mozilla.gecko.sync.repositories.NonPersistentRepositoryStateProvider;
import org.mozilla.gecko.sync.repositories.Server15Repository;
import org.mozilla.gecko.sync.repositories.StoreFailedException;
import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
import org.mozilla.gecko.sync.repositories.domain.BookmarkRecordFactory;
-import org.mozilla.gecko.sync.synchronizer.ServerLocalSynchronizer;
import org.mozilla.gecko.sync.synchronizer.Synchronizer;
import org.simpleframework.http.ContentType;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@@ -115,23 +114,23 @@ public class TestServer15RepositorySessi
final Server15Repository remote = new Server15Repository(
COLLECTION, SystemClock.elapsedRealtime() + TimeUnit.MINUTES.toMillis(30),
getCollectionURL(COLLECTION), authHeaderProvider, infoCollections, infoConfiguration,
new NonPersistentRepositoryStateProvider());
KeyBundle collectionKey = new KeyBundle(TEST_USERNAME, SYNC_KEY);
Crypto5MiddlewareRepository cryptoRepo = new Crypto5MiddlewareRepository(remote, collectionKey);
cryptoRepo.recordFactory = new BookmarkRecordFactory();
- final Synchronizer synchronizer = new ServerLocalSynchronizer();
+ final Synchronizer synchronizer = new Synchronizer();
synchronizer.repositoryA = cryptoRepo;
synchronizer.repositoryB = local;
data.startHTTPServer(server);
try {
- Exception e = TestServerLocalSynchronizer.doSynchronize(synchronizer);
+ Exception e = TestSynchronizer.doSynchronize(synchronizer);
return e;
} finally {
data.stopHTTPServer();
}
}
protected String getCollectionURL(String collection) {
return LOCAL_BASE_URL + "/storage/" + collection;
deleted file mode 100644
--- a/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestServerLocalSynchronizer.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.android.sync.test;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mozilla.android.sync.test.SynchronizerHelpers.BatchFailStoreWBORepository;
-import org.mozilla.android.sync.test.SynchronizerHelpers.BeginErrorWBORepository;
-import org.mozilla.android.sync.test.SynchronizerHelpers.BeginFailedException;
-import org.mozilla.android.sync.test.SynchronizerHelpers.FailFetchWBORepository;
-import org.mozilla.android.sync.test.SynchronizerHelpers.FinishErrorWBORepository;
-import org.mozilla.android.sync.test.SynchronizerHelpers.FinishFailedException;
-import org.mozilla.android.sync.test.SynchronizerHelpers.SerialFailStoreWBORepository;
-import org.mozilla.android.sync.test.SynchronizerHelpers.TrackingWBORepository;
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.testhelpers.TestRunner;
-import org.mozilla.gecko.background.testhelpers.WBORepository;
-import org.mozilla.gecko.background.testhelpers.WaitHelper;
-import org.mozilla.gecko.sync.repositories.FetchFailedException;
-import org.mozilla.gecko.sync.repositories.StoreFailedException;
-import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
-import org.mozilla.gecko.sync.synchronizer.ServerLocalSynchronizer;
-import org.mozilla.gecko.sync.synchronizer.Synchronizer;
-import org.mozilla.gecko.sync.synchronizer.SynchronizerDelegate;
-
-import java.util.ArrayList;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-@RunWith(TestRunner.class)
-public class TestServerLocalSynchronizer {
- public static final String LOG_TAG = "TestServLocSync";
-
- protected Synchronizer getSynchronizer(WBORepository remote, WBORepository local) {
- 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("inboundFail2", "bookmarks", 1, false),
- new BookmarkRecord("inboundFail3", "bookmarks", 1, false),
- };
- BookmarkRecord[] outbounds = new BookmarkRecord[] {
- new BookmarkRecord("outboundFail1", "bookmarks", 1, false),
- new BookmarkRecord("outboundFail2", "bookmarks", 1, false),
- new BookmarkRecord("outboundFail3", "bookmarks", 1, false),
- new BookmarkRecord("outboundFail4", "bookmarks", 1, false),
- new BookmarkRecord("outboundFail5", "bookmarks", 1, false),
- new BookmarkRecord("outboundFail6", "bookmarks", 1, false),
- };
- for (BookmarkRecord inbound : inbounds) {
- remote.wbos.put(inbound.guid, inbound);
- }
- for (BookmarkRecord outbound : outbounds) {
- local.wbos.put(outbound.guid, outbound);
- }
-
- final Synchronizer synchronizer = new ServerLocalSynchronizer();
- synchronizer.repositoryA = remote;
- synchronizer.repositoryB = local;
- return synchronizer;
- }
-
- protected static Exception doSynchronize(final Synchronizer synchronizer) {
- final ArrayList<Exception> a = new ArrayList<Exception>();
-
- WaitHelper.getTestWaiter().performWait(new Runnable() {
- @Override
- public void run() {
- synchronizer.synchronize(null, new SynchronizerDelegate() {
- @Override
- public void onSynchronized(Synchronizer synchronizer) {
- Logger.trace(LOG_TAG, "Got onSynchronized.");
- a.add(null);
- WaitHelper.getTestWaiter().performNotify();
- }
-
- @Override
- public void onSynchronizeFailed(Synchronizer synchronizer, Exception lastException, String reason) {
- Logger.trace(LOG_TAG, "Got onSynchronizedFailed.");
- a.add(lastException);
- WaitHelper.getTestWaiter().performNotify();
- }
- });
- }
- });
-
- assertEquals(1, a.size()); // Should not be called multiple times!
- return a.get(0);
- }
-
- @Test
- public void testNoErrors() {
- WBORepository remote = new TrackingWBORepository();
- WBORepository local = new TrackingWBORepository();
-
- Synchronizer synchronizer = getSynchronizer(remote, local);
- assertNull(doSynchronize(synchronizer));
-
- assertEquals(12, local.wbos.size());
- assertEquals(12, remote.wbos.size());
- }
-
- @Test
- public void testLocalFetchErrors() {
- WBORepository remote = new TrackingWBORepository();
- WBORepository local = new FailFetchWBORepository(SynchronizerHelpers.FailMode.FETCH);
-
- Synchronizer synchronizer = getSynchronizer(remote, local);
- Exception e = doSynchronize(synchronizer);
- assertNotNull(e);
- assertEquals(FetchFailedException.class, e.getClass());
-
- // Neither session gets finished successfully, so all records are dropped.
- assertEquals(6, local.wbos.size());
- assertEquals(6, remote.wbos.size());
- }
-
- @Test
- public void testRemoteFetchErrors() {
- WBORepository remote = new FailFetchWBORepository(SynchronizerHelpers.FailMode.FETCH);
- WBORepository local = new TrackingWBORepository();
-
- Synchronizer synchronizer = getSynchronizer(remote, local);
- Exception e = doSynchronize(synchronizer);
- assertNotNull(e);
- assertEquals(FetchFailedException.class, e.getClass());
-
- // Neither session gets finished successfully, so all records are dropped.
- assertEquals(6, local.wbos.size());
- assertEquals(6, remote.wbos.size());
- }
-
- @Test
- public void testLocalSerialStoreErrorsAreIgnored() {
- WBORepository remote = new TrackingWBORepository();
- WBORepository local = new SerialFailStoreWBORepository(SynchronizerHelpers.FailMode.FETCH);
-
- Synchronizer synchronizer = getSynchronizer(remote, local);
- assertNull(doSynchronize(synchronizer));
-
- assertEquals(9, local.wbos.size());
- assertEquals(12, remote.wbos.size());
- }
-
- @Test
- public void testLocalBatchStoreErrorsAreIgnored() {
- final int BATCH_SIZE = 3;
-
- Synchronizer synchronizer = getSynchronizer(new TrackingWBORepository(), new BatchFailStoreWBORepository(BATCH_SIZE));
-
- Exception e = doSynchronize(synchronizer);
- assertNull(e);
- }
-
- @Test
- public void testRemoteSerialStoreErrorsAreNotIgnored() throws Exception {
- Synchronizer synchronizer = getSynchronizer(new SerialFailStoreWBORepository(SynchronizerHelpers.FailMode.STORE), new TrackingWBORepository()); // Tracking so we don't send incoming records back.
-
- Exception e = doSynchronize(synchronizer);
- assertNotNull(e);
- assertEquals(StoreFailedException.class, e.getClass());
- }
-
- @Test
- public void testRemoteBatchStoreErrorsAreNotIgnoredManyBatches() throws Exception {
- final int BATCH_SIZE = 3;
-
- Synchronizer synchronizer = getSynchronizer(new BatchFailStoreWBORepository(BATCH_SIZE), new TrackingWBORepository()); // Tracking so we don't send incoming records back.
-
- Exception e = doSynchronize(synchronizer);
- assertNotNull(e);
- assertEquals(StoreFailedException.class, e.getClass());
- }
-
- @Test
- public void testRemoteBatchStoreErrorsAreNotIgnoredOneBigBatch() throws Exception {
- final int BATCH_SIZE = 20;
-
- Synchronizer synchronizer = getSynchronizer(new BatchFailStoreWBORepository(BATCH_SIZE), new TrackingWBORepository()); // Tracking so we don't send incoming records back.
-
- Exception e = doSynchronize(synchronizer);
- assertNotNull(e);
- assertEquals(StoreFailedException.class, e.getClass());
- }
-
- @Test
- public void testSessionRemoteBeginError() {
- Synchronizer synchronizer = getSynchronizer(new BeginErrorWBORepository(), new TrackingWBORepository());
- Exception e = doSynchronize(synchronizer);
- assertNotNull(e);
- assertEquals(BeginFailedException.class, e.getClass());
- }
-
- @Test
- public void testSessionLocalBeginError() {
- Synchronizer synchronizer = getSynchronizer(new TrackingWBORepository(), new BeginErrorWBORepository());
- Exception e = doSynchronize(synchronizer);
- assertNotNull(e);
- assertEquals(BeginFailedException.class, e.getClass());
- }
-
- @Test
- public void testSessionRemoteFinishError() {
- Synchronizer synchronizer = getSynchronizer(new FinishErrorWBORepository(), new TrackingWBORepository());
- Exception e = doSynchronize(synchronizer);
- assertNotNull(e);
- assertEquals(FinishFailedException.class, e.getClass());
- }
-
- @Test
- public void testSessionLocalFinishError() {
- Synchronizer synchronizer = getSynchronizer(new TrackingWBORepository(), new FinishErrorWBORepository());
- Exception e = doSynchronize(synchronizer);
- assertNotNull(e);
- assertEquals(FinishFailedException.class, e.getClass());
- }
-
- @Test
- public void testSessionBothBeginError() {
- Synchronizer synchronizer = getSynchronizer(new BeginErrorWBORepository(), new BeginErrorWBORepository());
- Exception e = doSynchronize(synchronizer);
- assertNotNull(e);
- assertEquals(BeginFailedException.class, e.getClass());
- }
-
- @Test
- public void testSessionBothFinishError() {
- Synchronizer synchronizer = getSynchronizer(new FinishErrorWBORepository(), new FinishErrorWBORepository());
- Exception e = doSynchronize(synchronizer);
- assertNotNull(e);
- assertEquals(FinishFailedException.class, e.getClass());
- }
-}
--- a/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestSynchronizer.java
+++ b/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestSynchronizer.java
@@ -9,23 +9,26 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.android.sync.test.SynchronizerHelpers.TrackingWBORepository;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.background.testhelpers.WBORepository;
import org.mozilla.gecko.background.testhelpers.WaitHelper;
+import org.mozilla.gecko.sync.repositories.FetchFailedException;
import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
+import org.mozilla.gecko.sync.repositories.StoreFailedException;
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 java.util.ArrayList;
import java.util.Date;
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;
@@ -385,9 +388,210 @@ public class TestSynchronizer {
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);
}
+
+ static Exception doSynchronize(final Synchronizer synchronizer) {
+ final ArrayList<Exception> a = new ArrayList<Exception>();
+
+ WaitHelper.getTestWaiter().performWait(new Runnable() {
+ @Override
+ public void run() {
+ synchronizer.synchronize(null, new SynchronizerDelegate() {
+ @Override
+ public void onSynchronized(Synchronizer synchronizer) {
+ Logger.trace(LOG_TAG, "Got onSynchronized.");
+ a.add(null);
+ WaitHelper.getTestWaiter().performNotify();
+ }
+
+ @Override
+ public void onSynchronizeFailed(Synchronizer synchronizer, Exception lastException, String reason) {
+ Logger.trace(LOG_TAG, "Got onSynchronizedFailed.");
+ a.add(lastException);
+ WaitHelper.getTestWaiter().performNotify();
+ }
+ });
+ }
+ });
+
+ assertEquals(1, a.size()); // Should not be called multiple times!
+ return a.get(0);
+ }
+
+ private Synchronizer getSynchronizer(WBORepository remote, WBORepository local) {
+ 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("inboundFail2", "bookmarks", 1, false),
+ new BookmarkRecord("inboundFail3", "bookmarks", 1, false),
+ };
+ BookmarkRecord[] outbounds = new BookmarkRecord[] {
+ new BookmarkRecord("outboundFail1", "bookmarks", 1, false),
+ new BookmarkRecord("outboundFail2", "bookmarks", 1, false),
+ new BookmarkRecord("outboundFail3", "bookmarks", 1, false),
+ new BookmarkRecord("outboundFail4", "bookmarks", 1, false),
+ new BookmarkRecord("outboundFail5", "bookmarks", 1, false),
+ new BookmarkRecord("outboundFail6", "bookmarks", 1, false),
+ };
+ for (BookmarkRecord inbound : inbounds) {
+ remote.wbos.put(inbound.guid, inbound);
+ }
+ for (BookmarkRecord outbound : outbounds) {
+ local.wbos.put(outbound.guid, outbound);
+ }
+
+ final Synchronizer synchronizer = new Synchronizer();
+ synchronizer.repositoryA = remote;
+ synchronizer.repositoryB = local;
+ return synchronizer;
+ }
+
+ @Test
+ public void testNoErrors() {
+ WBORepository remote = new TrackingWBORepository();
+ WBORepository local = new TrackingWBORepository();
+
+ Synchronizer synchronizer = getSynchronizer(remote, local);
+ assertNull(doSynchronize(synchronizer));
+
+ assertEquals(12, local.wbos.size());
+ assertEquals(12, remote.wbos.size());
+ }
+
+ @Test
+ public void testLocalFetchErrors() {
+ WBORepository remote = new TrackingWBORepository();
+ WBORepository local = new SynchronizerHelpers.FailFetchWBORepository(SynchronizerHelpers.FailMode.FETCH);
+
+ Synchronizer synchronizer = getSynchronizer(remote, local);
+ Exception e = doSynchronize(synchronizer);
+ assertNotNull(e);
+ assertEquals(FetchFailedException.class, e.getClass());
+
+ // Neither session gets finished successfully, so all records are dropped.
+ assertEquals(6, local.wbos.size());
+ assertEquals(6, remote.wbos.size());
+ }
+
+ @Test
+ public void testRemoteFetchErrors() {
+ WBORepository remote = new SynchronizerHelpers.FailFetchWBORepository(SynchronizerHelpers.FailMode.FETCH);
+ WBORepository local = new TrackingWBORepository();
+
+ Synchronizer synchronizer = getSynchronizer(remote, local);
+ Exception e = doSynchronize(synchronizer);
+ assertNotNull(e);
+ assertEquals(FetchFailedException.class, e.getClass());
+
+ // Neither session gets finished successfully, so all records are dropped.
+ assertEquals(6, local.wbos.size());
+ assertEquals(6, remote.wbos.size());
+ }
+
+ @Test
+ public void testLocalSerialStoreErrorsAreIgnored() {
+ WBORepository remote = new TrackingWBORepository();
+ WBORepository local = new SynchronizerHelpers.SerialFailStoreWBORepository(SynchronizerHelpers.FailMode.FETCH);
+
+ Synchronizer synchronizer = getSynchronizer(remote, local);
+ assertNull(doSynchronize(synchronizer));
+
+ assertEquals(9, local.wbos.size());
+ assertEquals(12, remote.wbos.size());
+ }
+
+ @Test
+ public void testLocalBatchStoreErrorsAreIgnored() {
+ final int BATCH_SIZE = 3;
+
+ Synchronizer synchronizer = getSynchronizer(new TrackingWBORepository(), new SynchronizerHelpers.BatchFailStoreWBORepository(BATCH_SIZE));
+
+ Exception e = doSynchronize(synchronizer);
+ assertNull(e);
+ }
+
+ @Test
+ public void testRemoteSerialStoreErrorsAreNotIgnored() throws Exception {
+ Synchronizer synchronizer = getSynchronizer(new SynchronizerHelpers.SerialFailStoreWBORepository(SynchronizerHelpers.FailMode.STORE), new TrackingWBORepository()); // Tracking so we don't send incoming records back.
+
+ Exception e = doSynchronize(synchronizer);
+ assertNotNull(e);
+ assertEquals(StoreFailedException.class, e.getClass());
+ }
+
+ @Test
+ public void testRemoteBatchStoreErrorsAreNotIgnoredManyBatches() throws Exception {
+ final int BATCH_SIZE = 3;
+
+ Synchronizer synchronizer = getSynchronizer(new SynchronizerHelpers.BatchFailStoreWBORepository(BATCH_SIZE), new TrackingWBORepository()); // Tracking so we don't send incoming records back.
+
+ Exception e = doSynchronize(synchronizer);
+ assertNotNull(e);
+ assertEquals(StoreFailedException.class, e.getClass());
+ }
+
+ @Test
+ public void testRemoteBatchStoreErrorsAreNotIgnoredOneBigBatch() throws Exception {
+ final int BATCH_SIZE = 20;
+
+ Synchronizer synchronizer = getSynchronizer(new SynchronizerHelpers.BatchFailStoreWBORepository(BATCH_SIZE), new TrackingWBORepository()); // Tracking so we don't send incoming records back.
+
+ Exception e = doSynchronize(synchronizer);
+ assertNotNull(e);
+ assertEquals(StoreFailedException.class, e.getClass());
+ }
+
+ @Test
+ public void testSessionRemoteBeginError() {
+ Synchronizer synchronizer = getSynchronizer(new SynchronizerHelpers.BeginErrorWBORepository(), new TrackingWBORepository());
+ Exception e = doSynchronize(synchronizer);
+ assertNotNull(e);
+ assertEquals(SynchronizerHelpers.BeginFailedException.class, e.getClass());
+ }
+
+ @Test
+ public void testSessionLocalBeginError() {
+ Synchronizer synchronizer = getSynchronizer(new TrackingWBORepository(), new SynchronizerHelpers.BeginErrorWBORepository());
+ Exception e = doSynchronize(synchronizer);
+ assertNotNull(e);
+ assertEquals(SynchronizerHelpers.BeginFailedException.class, e.getClass());
+ }
+
+ @Test
+ public void testSessionRemoteFinishError() {
+ Synchronizer synchronizer = getSynchronizer(new SynchronizerHelpers.FinishErrorWBORepository(), new TrackingWBORepository());
+ Exception e = doSynchronize(synchronizer);
+ assertNotNull(e);
+ assertEquals(SynchronizerHelpers.FinishFailedException.class, e.getClass());
+ }
+
+ @Test
+ public void testSessionLocalFinishError() {
+ Synchronizer synchronizer = getSynchronizer(new TrackingWBORepository(), new SynchronizerHelpers.FinishErrorWBORepository());
+ Exception e = doSynchronize(synchronizer);
+ assertNotNull(e);
+ assertEquals(SynchronizerHelpers.FinishFailedException.class, e.getClass());
+ }
+
+ @Test
+ public void testSessionBothBeginError() {
+ Synchronizer synchronizer = getSynchronizer(new SynchronizerHelpers.BeginErrorWBORepository(), new SynchronizerHelpers.BeginErrorWBORepository());
+ Exception e = doSynchronize(synchronizer);
+ assertNotNull(e);
+ assertEquals(SynchronizerHelpers.BeginFailedException.class, e.getClass());
+ }
+
+ @Test
+ public void testSessionBothFinishError() {
+ Synchronizer synchronizer = getSynchronizer(new SynchronizerHelpers.FinishErrorWBORepository(), new SynchronizerHelpers.FinishErrorWBORepository());
+ Exception e = doSynchronize(synchronizer);
+ assertNotNull(e);
+ assertEquals(SynchronizerHelpers.FinishFailedException.class, e.getClass());
+ }
}