Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 2 | http://creativecommons.org/publicdomain/zero/1.0/ */ |
michael@0 | 3 | |
michael@0 | 4 | package org.mozilla.gecko.background.sync; |
michael@0 | 5 | |
michael@0 | 6 | import java.io.IOException; |
michael@0 | 7 | |
michael@0 | 8 | import org.json.simple.parser.ParseException; |
michael@0 | 9 | import org.mozilla.gecko.background.helpers.AndroidSyncTestCase; |
michael@0 | 10 | import org.mozilla.gecko.background.testhelpers.BaseMockServerSyncStage; |
michael@0 | 11 | import org.mozilla.gecko.background.testhelpers.DefaultGlobalSessionCallback; |
michael@0 | 12 | import org.mozilla.gecko.background.testhelpers.MockRecord; |
michael@0 | 13 | import org.mozilla.gecko.background.testhelpers.MockSharedPreferences; |
michael@0 | 14 | import org.mozilla.gecko.background.testhelpers.WBORepository; |
michael@0 | 15 | import org.mozilla.gecko.background.testhelpers.WaitHelper; |
michael@0 | 16 | import org.mozilla.gecko.sync.EngineSettings; |
michael@0 | 17 | import org.mozilla.gecko.sync.GlobalSession; |
michael@0 | 18 | import org.mozilla.gecko.sync.MetaGlobalException; |
michael@0 | 19 | import org.mozilla.gecko.sync.NonObjectJSONException; |
michael@0 | 20 | import org.mozilla.gecko.sync.SyncConfiguration; |
michael@0 | 21 | import org.mozilla.gecko.sync.SyncConfigurationException; |
michael@0 | 22 | import org.mozilla.gecko.sync.SynchronizerConfiguration; |
michael@0 | 23 | import org.mozilla.gecko.sync.crypto.CryptoException; |
michael@0 | 24 | import org.mozilla.gecko.sync.crypto.KeyBundle; |
michael@0 | 25 | import org.mozilla.gecko.sync.delegates.GlobalSessionCallback; |
michael@0 | 26 | import org.mozilla.gecko.sync.net.AuthHeaderProvider; |
michael@0 | 27 | import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider; |
michael@0 | 28 | import org.mozilla.gecko.sync.repositories.domain.Record; |
michael@0 | 29 | import org.mozilla.gecko.sync.stage.NoSuchStageException; |
michael@0 | 30 | import org.mozilla.gecko.sync.synchronizer.Synchronizer; |
michael@0 | 31 | |
michael@0 | 32 | import android.content.SharedPreferences; |
michael@0 | 33 | |
michael@0 | 34 | /** |
michael@0 | 35 | * Test the on-device side effects of reset operations on a stage. |
michael@0 | 36 | * |
michael@0 | 37 | * See also "TestResetCommands" in the unit test suite. |
michael@0 | 38 | */ |
michael@0 | 39 | public class TestResetting extends AndroidSyncTestCase { |
michael@0 | 40 | private static final String TEST_USERNAME = "johndoe"; |
michael@0 | 41 | private static final String TEST_PASSWORD = "password"; |
michael@0 | 42 | private static final String TEST_SYNC_KEY = "abcdeabcdeabcdeabcdeabcdea"; |
michael@0 | 43 | |
michael@0 | 44 | @Override |
michael@0 | 45 | public void setUp() { |
michael@0 | 46 | assertTrue(WaitHelper.getTestWaiter().isIdle()); |
michael@0 | 47 | } |
michael@0 | 48 | |
michael@0 | 49 | /** |
michael@0 | 50 | * Set up a mock stage that synchronizes two mock repositories. Apply various |
michael@0 | 51 | * reset/sync/wipe permutations and check state. |
michael@0 | 52 | */ |
michael@0 | 53 | public void testResetAndWipeStage() throws Exception { |
michael@0 | 54 | |
michael@0 | 55 | final long startTime = System.currentTimeMillis(); |
michael@0 | 56 | final GlobalSessionCallback callback = createGlobalSessionCallback(); |
michael@0 | 57 | final GlobalSession session = createDefaultGlobalSession(callback); |
michael@0 | 58 | |
michael@0 | 59 | final ExecutableMockServerSyncStage stage = new ExecutableMockServerSyncStage() { |
michael@0 | 60 | @Override |
michael@0 | 61 | public void onSynchronized(Synchronizer synchronizer) { |
michael@0 | 62 | try { |
michael@0 | 63 | assertTrue(startTime <= synchronizer.bundleA.getTimestamp()); |
michael@0 | 64 | assertTrue(startTime <= synchronizer.bundleB.getTimestamp()); |
michael@0 | 65 | |
michael@0 | 66 | // Call up to allow the usual persistence etc. to happen. |
michael@0 | 67 | super.onSynchronized(synchronizer); |
michael@0 | 68 | } catch (Throwable e) { |
michael@0 | 69 | performNotify(e); |
michael@0 | 70 | return; |
michael@0 | 71 | } |
michael@0 | 72 | performNotify(); |
michael@0 | 73 | } |
michael@0 | 74 | }; |
michael@0 | 75 | |
michael@0 | 76 | final boolean bumpTimestamps = true; |
michael@0 | 77 | WBORepository local = new WBORepository(bumpTimestamps); |
michael@0 | 78 | WBORepository remote = new WBORepository(bumpTimestamps); |
michael@0 | 79 | |
michael@0 | 80 | stage.name = "mock"; |
michael@0 | 81 | stage.collection = "mock"; |
michael@0 | 82 | stage.local = local; |
michael@0 | 83 | stage.remote = remote; |
michael@0 | 84 | |
michael@0 | 85 | stage.executeSynchronously(session); |
michael@0 | 86 | |
michael@0 | 87 | // Verify the persisted values. |
michael@0 | 88 | assertConfigTimestampsGreaterThan(stage.leakConfig(), startTime, startTime); |
michael@0 | 89 | |
michael@0 | 90 | // Reset. |
michael@0 | 91 | stage.resetLocal(session); |
michael@0 | 92 | |
michael@0 | 93 | // Verify that they're gone. |
michael@0 | 94 | assertConfigTimestampsEqual(stage.leakConfig(), 0, 0); |
michael@0 | 95 | |
michael@0 | 96 | // Now sync data, ensure that timestamps come back. |
michael@0 | 97 | final long afterReset = System.currentTimeMillis(); |
michael@0 | 98 | final String recordGUID = "abcdefghijkl"; |
michael@0 | 99 | local.wbos.put(recordGUID, new MockRecord(recordGUID, "mock", startTime, false)); |
michael@0 | 100 | |
michael@0 | 101 | // Sync again with data and verify timestamps and data. |
michael@0 | 102 | stage.executeSynchronously(session); |
michael@0 | 103 | |
michael@0 | 104 | assertConfigTimestampsGreaterThan(stage.leakConfig(), afterReset, afterReset); |
michael@0 | 105 | assertEquals(1, remote.wbos.size()); |
michael@0 | 106 | assertEquals(1, local.wbos.size()); |
michael@0 | 107 | |
michael@0 | 108 | Record remoteRecord = remote.wbos.get(recordGUID); |
michael@0 | 109 | assertNotNull(remoteRecord); |
michael@0 | 110 | assertNotNull(local.wbos.get(recordGUID)); |
michael@0 | 111 | assertEquals(recordGUID, remoteRecord.guid); |
michael@0 | 112 | assertTrue(afterReset <= remoteRecord.lastModified); |
michael@0 | 113 | |
michael@0 | 114 | // Reset doesn't clear data. |
michael@0 | 115 | stage.resetLocal(session); |
michael@0 | 116 | assertConfigTimestampsEqual(stage.leakConfig(), 0, 0); |
michael@0 | 117 | assertEquals(1, remote.wbos.size()); |
michael@0 | 118 | assertEquals(1, local.wbos.size()); |
michael@0 | 119 | remoteRecord = remote.wbos.get(recordGUID); |
michael@0 | 120 | assertNotNull(remoteRecord); |
michael@0 | 121 | assertNotNull(local.wbos.get(recordGUID)); |
michael@0 | 122 | |
michael@0 | 123 | // Wipe does. Recover from reset... |
michael@0 | 124 | final long beforeWipe = System.currentTimeMillis(); |
michael@0 | 125 | stage.executeSynchronously(session); |
michael@0 | 126 | assertEquals(1, remote.wbos.size()); |
michael@0 | 127 | assertEquals(1, local.wbos.size()); |
michael@0 | 128 | assertConfigTimestampsGreaterThan(stage.leakConfig(), beforeWipe, beforeWipe); |
michael@0 | 129 | |
michael@0 | 130 | // ... then wipe. |
michael@0 | 131 | stage.wipeLocal(session); |
michael@0 | 132 | assertConfigTimestampsEqual(stage.leakConfig(), 0, 0); |
michael@0 | 133 | assertEquals(1, remote.wbos.size()); // We don't wipe the server. |
michael@0 | 134 | assertEquals(0, local.wbos.size()); // We do wipe local. |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | /** |
michael@0 | 138 | * A stage that joins two Repositories with no wrapping. |
michael@0 | 139 | */ |
michael@0 | 140 | public class ExecutableMockServerSyncStage extends BaseMockServerSyncStage { |
michael@0 | 141 | /** |
michael@0 | 142 | * Run this stage synchronously. |
michael@0 | 143 | */ |
michael@0 | 144 | public void executeSynchronously(final GlobalSession session) { |
michael@0 | 145 | final BaseMockServerSyncStage self = this; |
michael@0 | 146 | performWait(new Runnable() { |
michael@0 | 147 | @Override |
michael@0 | 148 | public void run() { |
michael@0 | 149 | try { |
michael@0 | 150 | self.execute(session); |
michael@0 | 151 | } catch (NoSuchStageException e) { |
michael@0 | 152 | performNotify(e); |
michael@0 | 153 | } |
michael@0 | 154 | } |
michael@0 | 155 | }); |
michael@0 | 156 | } |
michael@0 | 157 | } |
michael@0 | 158 | |
michael@0 | 159 | private GlobalSession createDefaultGlobalSession(final GlobalSessionCallback callback) throws SyncConfigurationException, IllegalArgumentException, NonObjectJSONException, IOException, ParseException, CryptoException { |
michael@0 | 160 | |
michael@0 | 161 | final KeyBundle keyBundle = new KeyBundle(TEST_USERNAME, TEST_SYNC_KEY); |
michael@0 | 162 | final AuthHeaderProvider authHeaderProvider = new BasicAuthHeaderProvider(TEST_USERNAME, TEST_PASSWORD); |
michael@0 | 163 | final SharedPreferences prefs = new MockSharedPreferences(); |
michael@0 | 164 | final SyncConfiguration config = new SyncConfiguration(TEST_USERNAME, authHeaderProvider, prefs); |
michael@0 | 165 | config.syncKeyBundle = keyBundle; |
michael@0 | 166 | return new GlobalSession(config, callback, getApplicationContext(), null, callback) { |
michael@0 | 167 | @Override |
michael@0 | 168 | public boolean isEngineRemotelyEnabled(String engineName, |
michael@0 | 169 | EngineSettings engineSettings) |
michael@0 | 170 | throws MetaGlobalException { |
michael@0 | 171 | return true; |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | @Override |
michael@0 | 175 | public void advance() { |
michael@0 | 176 | // So we don't proceed and run other stages. |
michael@0 | 177 | } |
michael@0 | 178 | }; |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | private static GlobalSessionCallback createGlobalSessionCallback() { |
michael@0 | 182 | return new DefaultGlobalSessionCallback() { |
michael@0 | 183 | |
michael@0 | 184 | @Override |
michael@0 | 185 | public void handleAborted(GlobalSession globalSession, String reason) { |
michael@0 | 186 | performNotify(new Exception("Aborted")); |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | @Override |
michael@0 | 190 | public void handleError(GlobalSession globalSession, Exception ex) { |
michael@0 | 191 | performNotify(ex); |
michael@0 | 192 | } |
michael@0 | 193 | }; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | private static void assertConfigTimestampsGreaterThan(SynchronizerConfiguration config, long local, long remote) { |
michael@0 | 197 | assertTrue(local <= config.localBundle.getTimestamp()); |
michael@0 | 198 | assertTrue(remote <= config.remoteBundle.getTimestamp()); |
michael@0 | 199 | } |
michael@0 | 200 | |
michael@0 | 201 | private static void assertConfigTimestampsEqual(SynchronizerConfiguration config, long local, long remote) { |
michael@0 | 202 | assertEquals(local, config.localBundle.getTimestamp()); |
michael@0 | 203 | assertEquals(remote, config.remoteBundle.getTimestamp()); |
michael@0 | 204 | } |
michael@0 | 205 | } |