|
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.util.Map; |
|
7 |
|
8 import org.mozilla.gecko.background.common.GlobalConstants; |
|
9 import org.mozilla.gecko.background.helpers.AndroidSyncTestCase; |
|
10 import org.mozilla.gecko.background.testhelpers.MockSharedPreferences; |
|
11 import org.mozilla.gecko.sync.ExtendedJSONObject; |
|
12 import org.mozilla.gecko.sync.PrefsBackoffHandler; |
|
13 import org.mozilla.gecko.sync.SyncConfiguration; |
|
14 import org.mozilla.gecko.sync.SyncConstants; |
|
15 import org.mozilla.gecko.sync.Utils; |
|
16 import org.mozilla.gecko.sync.config.ConfigurationMigrator; |
|
17 import org.mozilla.gecko.sync.setup.SyncAccounts; |
|
18 import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters; |
|
19 |
|
20 import android.accounts.Account; |
|
21 import android.accounts.AccountManager; |
|
22 import android.content.Context; |
|
23 import android.content.SharedPreferences; |
|
24 import android.content.SharedPreferences.Editor; |
|
25 |
|
26 public class TestConfigurationMigrator extends AndroidSyncTestCase { |
|
27 /** |
|
28 * A migrator that makes public certain protected static functions for testing. |
|
29 */ |
|
30 protected static class PublicMigrator extends ConfigurationMigrator { |
|
31 public static int upgradeGlobals0to1(final SharedPreferences from, final SharedPreferences to) throws Exception { |
|
32 return ConfigurationMigrator.upgradeGlobals0to1(from, to); |
|
33 } |
|
34 |
|
35 public static int downgradeGlobals1to0(final SharedPreferences from, final SharedPreferences to) throws Exception { |
|
36 return ConfigurationMigrator.downgradeGlobals1to0(from, to); |
|
37 } |
|
38 |
|
39 public static int upgradeShared0to1(final SharedPreferences from, final SharedPreferences to) { |
|
40 return ConfigurationMigrator.upgradeShared0to1(from, to); |
|
41 } |
|
42 |
|
43 public static int downgradeShared1to0(final SharedPreferences from, final SharedPreferences to) { |
|
44 return ConfigurationMigrator.downgradeShared1to0(from, to); |
|
45 } |
|
46 |
|
47 public static int upgradeAndroidAccount0to1(final AccountManager accountManager, final Account account, final SharedPreferences to) throws Exception { |
|
48 return ConfigurationMigrator.upgradeAndroidAccount0to1(accountManager, account, to); |
|
49 } |
|
50 |
|
51 public static int downgradeAndroidAccount1to0(final SharedPreferences from, final AccountManager accountManager, final Account account) throws Exception { |
|
52 return ConfigurationMigrator.downgradeAndroidAccount1to0(from, accountManager, account); |
|
53 } |
|
54 }; |
|
55 |
|
56 public static final String TEST_USERNAME = "test@mozilla.com"; |
|
57 public static final String TEST_SYNCKEY = "testSyncKey"; |
|
58 public static final String TEST_PASSWORD = "testPassword"; |
|
59 public static final String TEST_SERVERURL = null; |
|
60 public static final String TEST_PROFILE = "default"; |
|
61 public static final String TEST_PRODUCT = GlobalConstants.BROWSER_INTENT_PACKAGE; |
|
62 |
|
63 public static final String TEST_GUID = "testGuid"; |
|
64 public static final String TEST_CLIENT_NAME = "test's Nightly on test"; |
|
65 public static final long TEST_NUM_CLIENTS = 2; |
|
66 |
|
67 protected static void putJSON(final Editor editor, final String name, final String JSON) throws Exception { |
|
68 editor.putString(name, ExtendedJSONObject.parseJSONObject(JSON).toJSONString()); |
|
69 } |
|
70 |
|
71 /** |
|
72 * Write a complete set of unversioned account prefs suitable for testing forward migration. |
|
73 * @throws Exception |
|
74 */ |
|
75 public void populateAccountSharedPrefs(final SharedPreferences to) throws Exception { |
|
76 final Editor editor = to.edit(); |
|
77 |
|
78 putJSON(editor, "forms.remote", "{\"timestamp\":1340402010180}"); |
|
79 putJSON(editor, "forms.local", "{\"timestamp\":1340402018565}"); |
|
80 editor.putString("forms.syncID", "JKAkk-wUEUpX"); |
|
81 |
|
82 putJSON(editor, "tabs.remote", "{\"timestamp\":1340401964300}"); |
|
83 putJSON(editor, "tabs.local", "{\"timestamp\":1340401970533}"); |
|
84 editor.putString("tabs.syncID", "604bXkw7dnUq"); |
|
85 |
|
86 putJSON(editor, "passwords.remote", "{\"timestamp\":1340401965150}"); |
|
87 putJSON(editor, "passwords.local", "{\"timestamp\":1340402005243}"); |
|
88 editor.putString("passwords.syncID", "VkTH0QiVj6dD"); |
|
89 |
|
90 putJSON(editor, "history.remote", "{\"timestamp\":1340402003640}"); |
|
91 putJSON(editor, "history.local", "{\"timestamp\":1340402015381}"); |
|
92 editor.putString("history.syncID", "fs1241n-JyWh"); |
|
93 |
|
94 putJSON(editor, "bookmarks.remote", "{\"timestamp\":1340402003370}"); |
|
95 putJSON(editor, "bookmarks.local", "{\"timestamp\":1340402008397}"); |
|
96 editor.putString("bookmarks.syncID", "P8gG8ERuJ4H1"); |
|
97 |
|
98 editor.putLong("metaGlobalLastModified", 1340401961960L); |
|
99 putJSON(editor, "metaGlobalServerResponseBody", "{\"ttl\":31536000,\"id\":\"global\",\"payload\":\"{\\\"storageVersion\\\":5,\\\"syncID\\\":\\\"Z2RopSDg-0bE\\\",\\\"engines\\\":{\\\"history\\\":{\\\"syncID\\\":\\\"fs1241n-JyWh\\\",\\\"version\\\":1},\\\"bookmarks\\\":{\\\"syncID\\\":\\\"P8gG8ERuJ4H1\\\",\\\"version\\\":2},\\\"passwords\\\":{\\\"syncID\\\":\\\"VkTH0QiVj6dD\\\",\\\"version\\\":1},\\\"prefs\\\":{\\\"syncID\\\":\\\"4lESgyoYPXYI\\\",\\\"version\\\":2},\\\"addons\\\":{\\\"syncID\\\":\\\"yCkJKkH-okoS\\\",\\\"version\\\":1},\\\"forms\\\":{\\\"syncID\\\":\\\"JKAkk-wUEUpX\\\",\\\"version\\\":1},\\\"clients\\\":{\\\"syncID\\\":\\\"KfANCdkZNOFJ\\\",\\\"version\\\":1},\\\"tabs\\\":{\\\"syncID\\\":\\\"604bXkw7dnUq\\\",\\\"version\\\":1}}}\"}"); |
|
100 |
|
101 editor.putLong("crypto5KeysLastModified", 1340401962760L); |
|
102 putJSON(editor, "crypto5KeysServerResponseBody", "{\"ttl\":31536000,\"id\":\"keys\",\"payload\":\"{\\\"ciphertext\\\":\\\"+ZH6AaMhnKOWS7OzpdMfT5X2C7AYgax5JRd2HY4BHAFNPDv8\\\\\\/TwQIJgFDuNjASo0WEujjdkFot39qeQ24RLAz4D11rG\\\\\\/FZwo8FEUB9aSfec1N6sao6KzWkSamdqiJSRjpsUKexp2it1HvwqRDEBH\\\\\\/lgue11axv51u1MAV3ZfX2fdzVIiGTqF1YJAvENZtol3pyEh2HI4FZlv+oLW250nV4w1vAfDNGLVbbjXbdR+kec=\\\",\\\"IV\\\":\\\"bHqF\\\\\\/4PshKt2GQ\\\\\\/njGj2Jw==\\\",\\\"hmac\\\":\\\"f97c20d5c0a141f62a1571a108de1bad4b854b29c8d4b2b0d36da73421e4becc\\\"}\"}"); |
|
103 |
|
104 editor.putString("syncID", "Z2RopSDg-0bE"); |
|
105 editor.putString("clusterURL", "https://scl2-sync3.services.mozilla.com/"); |
|
106 putJSON(editor, "enabledEngineNames", "{\"history\":0,\"bookmarks\":0,\"passwords\":0,\"prefs\":0,\"addons\":0,\"forms\":0,\"clients\":0,\"tabs\":0}"); |
|
107 |
|
108 editor.putLong("serverClientsTimestamp", 1340401963950L); |
|
109 editor.putLong("serverClientRecordTimestamp", 1340401963950L); |
|
110 |
|
111 editor.commit(); |
|
112 } |
|
113 |
|
114 /** |
|
115 * Write a complete set of unversioned global prefs suitable for testing forward migration. |
|
116 * @throws Exception |
|
117 */ |
|
118 public void populateGlobalSharedPrefs(final SharedPreferences to) throws Exception { |
|
119 final Editor editor = to.edit(); |
|
120 |
|
121 editor.putLong("earliestnextsync", 1340402318649L); |
|
122 editor.putBoolean("clusterurlisstale", false); |
|
123 |
|
124 editor.commit(); |
|
125 } |
|
126 |
|
127 /** |
|
128 * Write a complete set of unversioned Account data suitable for testing forward migration. |
|
129 * @throws Exception |
|
130 */ |
|
131 public void populateAccountData(final AccountManager accountManager, final Account account) throws Exception { |
|
132 accountManager.setUserData(account, "account.guid", TEST_GUID); |
|
133 accountManager.setUserData(account, "account.clientName", TEST_CLIENT_NAME); |
|
134 accountManager.setUserData(account, "account.numClients", Long.valueOf(TEST_NUM_CLIENTS).toString()); |
|
135 } |
|
136 |
|
137 public void testMigrateGlobals0and1() throws Exception { |
|
138 final SharedPreferences v0a = new MockSharedPreferences(); |
|
139 final SharedPreferences v1a = new MockSharedPreferences(); |
|
140 final SharedPreferences v0b = new MockSharedPreferences(); |
|
141 final SharedPreferences v1b = new MockSharedPreferences(); |
|
142 |
|
143 populateGlobalSharedPrefs(v0a); |
|
144 |
|
145 final int NUM_GLOBALS = 2; |
|
146 assertEquals(NUM_GLOBALS, v0a.getAll().size()); |
|
147 assertEquals(NUM_GLOBALS, PublicMigrator.upgradeGlobals0to1(v0a, v1a)); |
|
148 assertEquals(NUM_GLOBALS, PublicMigrator.downgradeGlobals1to0(v1a, v0b)); |
|
149 assertEquals(NUM_GLOBALS, PublicMigrator.upgradeGlobals0to1(v0b, v1b)); |
|
150 assertEquals(v0a.getAll(), v0b.getAll()); |
|
151 assertEquals(v1a.getAll(), v1b.getAll()); |
|
152 } |
|
153 |
|
154 public void testMigrateShared0and1() throws Exception { |
|
155 final SharedPreferences v0a = new MockSharedPreferences(); |
|
156 final SharedPreferences v1a = new MockSharedPreferences(); |
|
157 final SharedPreferences v0b = new MockSharedPreferences(); |
|
158 final SharedPreferences v1b = new MockSharedPreferences(); |
|
159 |
|
160 populateAccountSharedPrefs(v0a); |
|
161 |
|
162 final int NUM_GLOBALS = 24; |
|
163 assertEquals(NUM_GLOBALS, v0a.getAll().size()); |
|
164 assertEquals(NUM_GLOBALS, PublicMigrator.upgradeShared0to1(v0a, v1a)); |
|
165 assertEquals(NUM_GLOBALS, PublicMigrator.downgradeShared1to0(v1a, v0b)); |
|
166 assertEquals(NUM_GLOBALS, PublicMigrator.upgradeShared0to1(v0b, v1b)); |
|
167 assertEquals(v0a.getAll(), v0b.getAll()); |
|
168 assertEquals(v1a.getAll(), v1b.getAll()); |
|
169 } |
|
170 |
|
171 public void testMigrateAccount0and1() throws Exception { |
|
172 final Context context = getApplicationContext(); |
|
173 final AccountManager accountManager = AccountManager.get(context); |
|
174 final SyncAccountParameters syncAccount = new SyncAccountParameters(context, null, |
|
175 TEST_USERNAME, TEST_SYNCKEY, TEST_PASSWORD, null); |
|
176 |
|
177 Account account = null; |
|
178 try { |
|
179 account = SyncAccounts.createSyncAccount(syncAccount, false); |
|
180 populateAccountData(accountManager, account); |
|
181 |
|
182 final int NUM_ACCOUNTS = 3; |
|
183 final SharedPreferences a = new MockSharedPreferences(); |
|
184 final SharedPreferences b = new MockSharedPreferences(); |
|
185 |
|
186 assertEquals(NUM_ACCOUNTS, PublicMigrator.upgradeAndroidAccount0to1(accountManager, account, a)); |
|
187 assertEquals(NUM_ACCOUNTS, a.getAll().size()); |
|
188 assertEquals(NUM_ACCOUNTS, PublicMigrator.downgradeAndroidAccount1to0(a, accountManager, account)); |
|
189 |
|
190 TestSyncAccounts.deleteAccount(this, accountManager, account); |
|
191 account = SyncAccounts.createSyncAccount(syncAccount, false); |
|
192 |
|
193 assertEquals(NUM_ACCOUNTS, PublicMigrator.downgradeAndroidAccount1to0(a, accountManager, account)); |
|
194 assertEquals(NUM_ACCOUNTS, PublicMigrator.upgradeAndroidAccount0to1(accountManager, account, b)); |
|
195 assertEquals(a.getAll(), b.getAll()); |
|
196 } finally { |
|
197 if (account != null) { |
|
198 TestSyncAccounts.deleteAccount(this, accountManager, account); |
|
199 } |
|
200 } |
|
201 } |
|
202 |
|
203 public void testMigrate0to1() throws Exception { |
|
204 final Context context = getApplicationContext(); |
|
205 |
|
206 final String ACCOUNT_SHARED_PREFS_NAME = "sync.prefs.3qyu5zoqpuu4zhdiv5l2qthsiter3vop"; |
|
207 final String GLOBAL_SHARED_PREFS_NAME = "sync.prefs.global"; |
|
208 |
|
209 final String path = Utils.getPrefsPath(TEST_PRODUCT, TEST_USERNAME, TEST_SERVERURL, TEST_PROFILE, 0); |
|
210 assertEquals(ACCOUNT_SHARED_PREFS_NAME, path); |
|
211 final SharedPreferences accountPrefs = context.getSharedPreferences(ACCOUNT_SHARED_PREFS_NAME, Utils.SHARED_PREFERENCES_MODE); |
|
212 final SharedPreferences globalPrefs = context.getSharedPreferences(GLOBAL_SHARED_PREFS_NAME, Utils.SHARED_PREFERENCES_MODE); |
|
213 |
|
214 accountPrefs.edit().clear().commit(); |
|
215 globalPrefs.edit().clear().commit(); |
|
216 // Clear prefs we're about to write into. |
|
217 final SharedPreferences existingPrefs = Utils.getSharedPreferences(context, TEST_PRODUCT, TEST_USERNAME, TEST_SERVERURL, TEST_PROFILE, 1); |
|
218 existingPrefs.edit().clear().commit(); |
|
219 |
|
220 final AccountManager accountManager = AccountManager.get(context); |
|
221 final SyncAccountParameters syncAccount = new SyncAccountParameters(context, null, |
|
222 TEST_USERNAME, TEST_SYNCKEY, TEST_PASSWORD, null); |
|
223 |
|
224 Account account = null; |
|
225 try { |
|
226 account = SyncAccounts.createSyncAccount(syncAccount, false); // Wipes prefs. |
|
227 |
|
228 populateAccountSharedPrefs(accountPrefs); |
|
229 populateGlobalSharedPrefs(globalPrefs); |
|
230 populateAccountData(accountManager, account); |
|
231 |
|
232 ConfigurationMigrator.upgrade0to1(context, accountManager, account, TEST_PRODUCT, TEST_USERNAME, TEST_SERVERURL, TEST_PROFILE); |
|
233 } finally { |
|
234 if (account != null) { |
|
235 TestSyncAccounts.deleteAccount(this, accountManager, account); |
|
236 } |
|
237 } |
|
238 |
|
239 Map<String, ?> origAccountPrefs = accountPrefs.getAll(); |
|
240 Map<String, ?> origGlobalPrefs = globalPrefs.getAll(); |
|
241 assertFalse(origAccountPrefs.isEmpty()); |
|
242 assertFalse(origGlobalPrefs.isEmpty()); |
|
243 |
|
244 final SharedPreferences newPrefs = Utils.getSharedPreferences(context, TEST_PRODUCT, TEST_USERNAME, TEST_SERVERURL, TEST_PROFILE, 1); |
|
245 |
|
246 // Some global stuff. |
|
247 assertEquals(false, newPrefs.getBoolean(SyncConfiguration.PREF_CLUSTER_URL_IS_STALE, true)); |
|
248 assertEquals(1340402318649L, newPrefs.getLong(PrefsBackoffHandler.PREF_EARLIEST_NEXT + SyncConstants.BACKOFF_PREF_SUFFIX_11, 111)); |
|
249 // Some per-Sync account stuff. |
|
250 assertEquals("{\"timestamp\":1340402003370}", newPrefs.getString("bookmarks.remote", null)); |
|
251 assertEquals("{\"timestamp\":1340402008397}", newPrefs.getString("bookmarks.local", null)); |
|
252 assertEquals("P8gG8ERuJ4H1", newPrefs.getString("bookmarks.syncID", null)); |
|
253 assertEquals(1340401961960L, newPrefs.getLong("metaGlobalLastModified", 0)); |
|
254 // Some per-Android account stuff. |
|
255 assertEquals(TEST_GUID, newPrefs.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null)); |
|
256 assertEquals(TEST_CLIENT_NAME, newPrefs.getString(SyncConfiguration.PREF_CLIENT_NAME, null)); |
|
257 assertEquals(TEST_NUM_CLIENTS, newPrefs.getLong(SyncConfiguration.PREF_NUM_CLIENTS, -1L)); |
|
258 |
|
259 // Now try to downgrade. |
|
260 accountPrefs.edit().clear().commit(); |
|
261 globalPrefs.edit().clear().commit(); |
|
262 |
|
263 account = null; |
|
264 try { |
|
265 account = SyncAccounts.createSyncAccount(syncAccount, false); |
|
266 |
|
267 ConfigurationMigrator.downgrade1to0(context, accountManager, account, TEST_PRODUCT, TEST_USERNAME, TEST_SERVERURL, TEST_PROFILE); |
|
268 |
|
269 final String V0_PREF_ACCOUNT_GUID = "account.guid"; |
|
270 final String V0_PREF_CLIENT_NAME = "account.clientName"; |
|
271 final String V0_PREF_NUM_CLIENTS = "account.numClients"; |
|
272 |
|
273 assertEquals(TEST_GUID, accountManager.getUserData(account, V0_PREF_ACCOUNT_GUID)); |
|
274 assertEquals(TEST_CLIENT_NAME, accountManager.getUserData(account, V0_PREF_CLIENT_NAME)); |
|
275 assertEquals(Long.valueOf(TEST_NUM_CLIENTS).toString(), accountManager.getUserData(account, V0_PREF_NUM_CLIENTS)); |
|
276 } finally { |
|
277 if (account != null) { |
|
278 TestSyncAccounts.deleteAccount(this, accountManager, account); |
|
279 } |
|
280 } |
|
281 |
|
282 // Check re-constituted prefs against old prefs. |
|
283 assertEquals(origAccountPrefs, accountPrefs.getAll()); |
|
284 assertEquals(origGlobalPrefs, globalPrefs.getAll()); |
|
285 } |
|
286 } |