/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.android.sync.test;
import java.io.IOException;
import org.json.simple.parser.ParseException;
import org.mozilla.android.sync.test.helpers.BaseMockServerSyncStage;
import org.mozilla.android.sync.test.helpers.DefaultGlobalSessionCallback;
import org.mozilla.android.sync.test.helpers.MockRecord;
import org.mozilla.android.sync.test.helpers.WBORepository;
import org.mozilla.android.sync.test.helpers.WaitHelper;
import org.mozilla.gecko.sync.EngineSettings;
import org.mozilla.gecko.sync.GlobalSession;
import org.mozilla.gecko.sync.MetaGlobalException;
import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.SyncConfiguration;
import org.mozilla.gecko.sync.SyncConfigurationException;
import org.mozilla.gecko.sync.SynchronizerConfiguration;
import org.mozilla.gecko.sync.crypto.CryptoException;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
import org.mozilla.gecko.sync.repositories.domain.Record;
import org.mozilla.gecko.sync.stage.NoSuchStageException;
import org.mozilla.gecko.sync.synchronizer.Synchronizer;
/**
* Test the on-device side effects of reset operations on a stage.
*
* See also "TestResetCommands" in the unit test suite.
*/
public class TestResetting extends AndroidSyncTestCase {
private static final String TEST_USERNAME = "johndoe";
private static final String TEST_PASSWORD = "password";
private static final String TEST_SYNC_KEY = "abcdeabcdeabcdeabcdeabcdea";
@Override
public void setUp() {
assertTrue(WaitHelper.getTestWaiter().isIdle());
}
/**
* Set up a mock stage that synchronizes two mock repositories. Apply various
* reset/sync/wipe permutations and check state.
*/
public void testResetAndWipeStage() throws Exception {
final long startTime = System.currentTimeMillis();
final GlobalSessionCallback callback = createGlobalSessionCallback();
final GlobalSession session = createDefaultGlobalSession(callback);
final ExecutableMockServerSyncStage stage = new ExecutableMockServerSyncStage(session) {
@Override
public void onSynchronized(Synchronizer synchronizer) {
try {
assertTrue(startTime <= synchronizer.bundleA.getTimestamp());
assertTrue(startTime <= synchronizer.bundleB.getTimestamp());
// Call up to allow the usual persistence etc. to happen.
super.onSynchronized(synchronizer);
} catch (Throwable e) {
performNotify(e);
return;
}
performNotify();
}
};
final boolean bumpTimestamps = true;
WBORepository local = new WBORepository(bumpTimestamps);
WBORepository remote = new WBORepository(bumpTimestamps);
stage.name = "mock";
stage.collection = "mock";
stage.local = local;
stage.remote = remote;
stage.executeSynchronously(session);
// Verify the persisted values.
assertConfigTimestampsGreaterThan(stage.leakConfig(), startTime, startTime);
// Reset.
stage.resetLocal();
// Verify that they're gone.
assertConfigTimestampsEqual(stage.leakConfig(), 0, 0);
// Now sync data, ensure that timestamps come back.
final long afterReset = System.currentTimeMillis();
final String recordGUID = "abcdefghijkl";
local.wbos.put(recordGUID, new MockRecord(recordGUID, "mock", startTime, false));
// Sync again with data and verify timestamps and data.
stage.executeSynchronously(session);
assertConfigTimestampsGreaterThan(stage.leakConfig(), afterReset, afterReset);
assertEquals(1, remote.wbos.size());
assertEquals(1, local.wbos.size());
Record remoteRecord = remote.wbos.get(recordGUID);
assertNotNull(remoteRecord);
assertNotNull(local.wbos.get(recordGUID));
assertEquals(recordGUID, remoteRecord.guid);
assertTrue(afterReset <= remoteRecord.lastModified);
// Reset doesn't clear data.
stage.resetLocal();
assertConfigTimestampsEqual(stage.leakConfig(), 0, 0);
assertEquals(1, remote.wbos.size());
assertEquals(1, local.wbos.size());
remoteRecord = remote.wbos.get(recordGUID);
assertNotNull(remoteRecord);
assertNotNull(local.wbos.get(recordGUID));
// Wipe does. Recover from reset...
final long beforeWipe = System.currentTimeMillis();
stage.executeSynchronously(session);
assertEquals(1, remote.wbos.size());
assertEquals(1, local.wbos.size());
assertConfigTimestampsGreaterThan(stage.leakConfig(), beforeWipe, beforeWipe);
// ... then wipe.
stage.wipeLocal();
assertConfigTimestampsEqual(stage.leakConfig(), 0, 0);
assertEquals(1, remote.wbos.size()); // We don't wipe the server.
assertEquals(0, local.wbos.size()); // We do wipe local.
}
/**
* A stage that joins two Repositories with no wrapping.
*/
public class ExecutableMockServerSyncStage extends BaseMockServerSyncStage {
public ExecutableMockServerSyncStage(GlobalSession session) {
super(session);
}
/**
* Run this stage synchronously.
*/
public void executeSynchronously(final GlobalSession session) {
final BaseMockServerSyncStage self = this;
performWait(new Runnable() {
@Override
public void run() {
try {
self.execute();
} catch (NoSuchStageException e) {
performNotify(e);
}
}
});
}
}
private GlobalSession createDefaultGlobalSession(final GlobalSessionCallback callback) throws SyncConfigurationException, IllegalArgumentException, NonObjectJSONException, IOException, ParseException, CryptoException {
return new GlobalSession(
SyncConfiguration.DEFAULT_USER_API,
null,
TEST_USERNAME, TEST_PASSWORD, null,
new KeyBundle(TEST_USERNAME, TEST_SYNC_KEY),
callback, getApplicationContext(), null, null) {
@Override
public boolean engineIsEnabled(String engineName,
EngineSettings engineSettings)
throws MetaGlobalException {
return true;
}
@Override
public void advance() {
// So we don't proceed and run other stages.
}
};
}
private static GlobalSessionCallback createGlobalSessionCallback() {
return new DefaultGlobalSessionCallback() {
@Override
public void handleAborted(GlobalSession globalSession, String reason) {
performNotify(new Exception("Aborted"));
}
@Override
public void handleError(GlobalSession globalSession, Exception ex) {
performNotify(ex);
}
};
}
private static void assertConfigTimestampsGreaterThan(SynchronizerConfiguration config, long local, long remote) {
assertTrue(local <= config.localBundle.getTimestamp());
assertTrue(remote <= config.remoteBundle.getTimestamp());
}
private static void assertConfigTimestampsEqual(SynchronizerConfiguration config, long local, long remote) {
assertEquals(local, config.localBundle.getTimestamp());
assertEquals(remote, config.remoteBundle.getTimestamp());
}
}