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