|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 package org.mozilla.gecko.background.sync; |
|
5 |
|
6 import java.io.IOException; |
|
7 |
|
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; |
|
31 |
|
32 import android.content.SharedPreferences; |
|
33 |
|
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"; |
|
43 |
|
44 @Override |
|
45 public void setUp() { |
|
46 assertTrue(WaitHelper.getTestWaiter().isIdle()); |
|
47 } |
|
48 |
|
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 { |
|
54 |
|
55 final long startTime = System.currentTimeMillis(); |
|
56 final GlobalSessionCallback callback = createGlobalSessionCallback(); |
|
57 final GlobalSession session = createDefaultGlobalSession(callback); |
|
58 |
|
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()); |
|
65 |
|
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 }; |
|
75 |
|
76 final boolean bumpTimestamps = true; |
|
77 WBORepository local = new WBORepository(bumpTimestamps); |
|
78 WBORepository remote = new WBORepository(bumpTimestamps); |
|
79 |
|
80 stage.name = "mock"; |
|
81 stage.collection = "mock"; |
|
82 stage.local = local; |
|
83 stage.remote = remote; |
|
84 |
|
85 stage.executeSynchronously(session); |
|
86 |
|
87 // Verify the persisted values. |
|
88 assertConfigTimestampsGreaterThan(stage.leakConfig(), startTime, startTime); |
|
89 |
|
90 // Reset. |
|
91 stage.resetLocal(session); |
|
92 |
|
93 // Verify that they're gone. |
|
94 assertConfigTimestampsEqual(stage.leakConfig(), 0, 0); |
|
95 |
|
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)); |
|
100 |
|
101 // Sync again with data and verify timestamps and data. |
|
102 stage.executeSynchronously(session); |
|
103 |
|
104 assertConfigTimestampsGreaterThan(stage.leakConfig(), afterReset, afterReset); |
|
105 assertEquals(1, remote.wbos.size()); |
|
106 assertEquals(1, local.wbos.size()); |
|
107 |
|
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); |
|
113 |
|
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)); |
|
122 |
|
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); |
|
129 |
|
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 } |
|
136 |
|
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 } |
|
158 |
|
159 private GlobalSession createDefaultGlobalSession(final GlobalSessionCallback callback) throws SyncConfigurationException, IllegalArgumentException, NonObjectJSONException, IOException, ParseException, CryptoException { |
|
160 |
|
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 } |
|
173 |
|
174 @Override |
|
175 public void advance() { |
|
176 // So we don't proceed and run other stages. |
|
177 } |
|
178 }; |
|
179 } |
|
180 |
|
181 private static GlobalSessionCallback createGlobalSessionCallback() { |
|
182 return new DefaultGlobalSessionCallback() { |
|
183 |
|
184 @Override |
|
185 public void handleAborted(GlobalSession globalSession, String reason) { |
|
186 performNotify(new Exception("Aborted")); |
|
187 } |
|
188 |
|
189 @Override |
|
190 public void handleError(GlobalSession globalSession, Exception ex) { |
|
191 performNotify(ex); |
|
192 } |
|
193 }; |
|
194 } |
|
195 |
|
196 private static void assertConfigTimestampsGreaterThan(SynchronizerConfiguration config, long local, long remote) { |
|
197 assertTrue(local <= config.localBundle.getTimestamp()); |
|
198 assertTrue(remote <= config.remoteBundle.getTimestamp()); |
|
199 } |
|
200 |
|
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 } |