michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: package org.mozilla.gecko.background.sync; michael@0: michael@0: import java.util.Map; michael@0: michael@0: import org.mozilla.gecko.background.common.GlobalConstants; michael@0: import org.mozilla.gecko.background.helpers.AndroidSyncTestCase; michael@0: import org.mozilla.gecko.background.testhelpers.MockSharedPreferences; michael@0: import org.mozilla.gecko.sync.ExtendedJSONObject; michael@0: import org.mozilla.gecko.sync.PrefsBackoffHandler; michael@0: import org.mozilla.gecko.sync.SyncConfiguration; michael@0: import org.mozilla.gecko.sync.SyncConstants; michael@0: import org.mozilla.gecko.sync.Utils; michael@0: import org.mozilla.gecko.sync.config.ConfigurationMigrator; michael@0: import org.mozilla.gecko.sync.setup.SyncAccounts; michael@0: import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters; michael@0: michael@0: import android.accounts.Account; michael@0: import android.accounts.AccountManager; michael@0: import android.content.Context; michael@0: import android.content.SharedPreferences; michael@0: import android.content.SharedPreferences.Editor; michael@0: michael@0: public class TestConfigurationMigrator extends AndroidSyncTestCase { michael@0: /** michael@0: * A migrator that makes public certain protected static functions for testing. michael@0: */ michael@0: protected static class PublicMigrator extends ConfigurationMigrator { michael@0: public static int upgradeGlobals0to1(final SharedPreferences from, final SharedPreferences to) throws Exception { michael@0: return ConfigurationMigrator.upgradeGlobals0to1(from, to); michael@0: } michael@0: michael@0: public static int downgradeGlobals1to0(final SharedPreferences from, final SharedPreferences to) throws Exception { michael@0: return ConfigurationMigrator.downgradeGlobals1to0(from, to); michael@0: } michael@0: michael@0: public static int upgradeShared0to1(final SharedPreferences from, final SharedPreferences to) { michael@0: return ConfigurationMigrator.upgradeShared0to1(from, to); michael@0: } michael@0: michael@0: public static int downgradeShared1to0(final SharedPreferences from, final SharedPreferences to) { michael@0: return ConfigurationMigrator.downgradeShared1to0(from, to); michael@0: } michael@0: michael@0: public static int upgradeAndroidAccount0to1(final AccountManager accountManager, final Account account, final SharedPreferences to) throws Exception { michael@0: return ConfigurationMigrator.upgradeAndroidAccount0to1(accountManager, account, to); michael@0: } michael@0: michael@0: public static int downgradeAndroidAccount1to0(final SharedPreferences from, final AccountManager accountManager, final Account account) throws Exception { michael@0: return ConfigurationMigrator.downgradeAndroidAccount1to0(from, accountManager, account); michael@0: } michael@0: }; michael@0: michael@0: public static final String TEST_USERNAME = "test@mozilla.com"; michael@0: public static final String TEST_SYNCKEY = "testSyncKey"; michael@0: public static final String TEST_PASSWORD = "testPassword"; michael@0: public static final String TEST_SERVERURL = null; michael@0: public static final String TEST_PROFILE = "default"; michael@0: public static final String TEST_PRODUCT = GlobalConstants.BROWSER_INTENT_PACKAGE; michael@0: michael@0: public static final String TEST_GUID = "testGuid"; michael@0: public static final String TEST_CLIENT_NAME = "test's Nightly on test"; michael@0: public static final long TEST_NUM_CLIENTS = 2; michael@0: michael@0: protected static void putJSON(final Editor editor, final String name, final String JSON) throws Exception { michael@0: editor.putString(name, ExtendedJSONObject.parseJSONObject(JSON).toJSONString()); michael@0: } michael@0: michael@0: /** michael@0: * Write a complete set of unversioned account prefs suitable for testing forward migration. michael@0: * @throws Exception michael@0: */ michael@0: public void populateAccountSharedPrefs(final SharedPreferences to) throws Exception { michael@0: final Editor editor = to.edit(); michael@0: michael@0: putJSON(editor, "forms.remote", "{\"timestamp\":1340402010180}"); michael@0: putJSON(editor, "forms.local", "{\"timestamp\":1340402018565}"); michael@0: editor.putString("forms.syncID", "JKAkk-wUEUpX"); michael@0: michael@0: putJSON(editor, "tabs.remote", "{\"timestamp\":1340401964300}"); michael@0: putJSON(editor, "tabs.local", "{\"timestamp\":1340401970533}"); michael@0: editor.putString("tabs.syncID", "604bXkw7dnUq"); michael@0: michael@0: putJSON(editor, "passwords.remote", "{\"timestamp\":1340401965150}"); michael@0: putJSON(editor, "passwords.local", "{\"timestamp\":1340402005243}"); michael@0: editor.putString("passwords.syncID", "VkTH0QiVj6dD"); michael@0: michael@0: putJSON(editor, "history.remote", "{\"timestamp\":1340402003640}"); michael@0: putJSON(editor, "history.local", "{\"timestamp\":1340402015381}"); michael@0: editor.putString("history.syncID", "fs1241n-JyWh"); michael@0: michael@0: putJSON(editor, "bookmarks.remote", "{\"timestamp\":1340402003370}"); michael@0: putJSON(editor, "bookmarks.local", "{\"timestamp\":1340402008397}"); michael@0: editor.putString("bookmarks.syncID", "P8gG8ERuJ4H1"); michael@0: michael@0: editor.putLong("metaGlobalLastModified", 1340401961960L); michael@0: 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}}}\"}"); michael@0: michael@0: editor.putLong("crypto5KeysLastModified", 1340401962760L); michael@0: putJSON(editor, "crypto5KeysServerResponseBody", "{\"ttl\":31536000,\"id\":\"keys\",\"payload\":\"{\\\"ciphertext\\\":\\\"+ZH6AaMhnKOWS7OzpdMfT5X2C7AYgax5JRd2HY4BHAFNPDv8\\\\\\/TwQIJgFDuNjASo0WEujjdkFot39qeQ24RLAz4D11rG\\\\\\/FZwo8FEUB9aSfec1N6sao6KzWkSamdqiJSRjpsUKexp2it1HvwqRDEBH\\\\\\/lgue11axv51u1MAV3ZfX2fdzVIiGTqF1YJAvENZtol3pyEh2HI4FZlv+oLW250nV4w1vAfDNGLVbbjXbdR+kec=\\\",\\\"IV\\\":\\\"bHqF\\\\\\/4PshKt2GQ\\\\\\/njGj2Jw==\\\",\\\"hmac\\\":\\\"f97c20d5c0a141f62a1571a108de1bad4b854b29c8d4b2b0d36da73421e4becc\\\"}\"}"); michael@0: michael@0: editor.putString("syncID", "Z2RopSDg-0bE"); michael@0: editor.putString("clusterURL", "https://scl2-sync3.services.mozilla.com/"); michael@0: putJSON(editor, "enabledEngineNames", "{\"history\":0,\"bookmarks\":0,\"passwords\":0,\"prefs\":0,\"addons\":0,\"forms\":0,\"clients\":0,\"tabs\":0}"); michael@0: michael@0: editor.putLong("serverClientsTimestamp", 1340401963950L); michael@0: editor.putLong("serverClientRecordTimestamp", 1340401963950L); michael@0: michael@0: editor.commit(); michael@0: } michael@0: michael@0: /** michael@0: * Write a complete set of unversioned global prefs suitable for testing forward migration. michael@0: * @throws Exception michael@0: */ michael@0: public void populateGlobalSharedPrefs(final SharedPreferences to) throws Exception { michael@0: final Editor editor = to.edit(); michael@0: michael@0: editor.putLong("earliestnextsync", 1340402318649L); michael@0: editor.putBoolean("clusterurlisstale", false); michael@0: michael@0: editor.commit(); michael@0: } michael@0: michael@0: /** michael@0: * Write a complete set of unversioned Account data suitable for testing forward migration. michael@0: * @throws Exception michael@0: */ michael@0: public void populateAccountData(final AccountManager accountManager, final Account account) throws Exception { michael@0: accountManager.setUserData(account, "account.guid", TEST_GUID); michael@0: accountManager.setUserData(account, "account.clientName", TEST_CLIENT_NAME); michael@0: accountManager.setUserData(account, "account.numClients", Long.valueOf(TEST_NUM_CLIENTS).toString()); michael@0: } michael@0: michael@0: public void testMigrateGlobals0and1() throws Exception { michael@0: final SharedPreferences v0a = new MockSharedPreferences(); michael@0: final SharedPreferences v1a = new MockSharedPreferences(); michael@0: final SharedPreferences v0b = new MockSharedPreferences(); michael@0: final SharedPreferences v1b = new MockSharedPreferences(); michael@0: michael@0: populateGlobalSharedPrefs(v0a); michael@0: michael@0: final int NUM_GLOBALS = 2; michael@0: assertEquals(NUM_GLOBALS, v0a.getAll().size()); michael@0: assertEquals(NUM_GLOBALS, PublicMigrator.upgradeGlobals0to1(v0a, v1a)); michael@0: assertEquals(NUM_GLOBALS, PublicMigrator.downgradeGlobals1to0(v1a, v0b)); michael@0: assertEquals(NUM_GLOBALS, PublicMigrator.upgradeGlobals0to1(v0b, v1b)); michael@0: assertEquals(v0a.getAll(), v0b.getAll()); michael@0: assertEquals(v1a.getAll(), v1b.getAll()); michael@0: } michael@0: michael@0: public void testMigrateShared0and1() throws Exception { michael@0: final SharedPreferences v0a = new MockSharedPreferences(); michael@0: final SharedPreferences v1a = new MockSharedPreferences(); michael@0: final SharedPreferences v0b = new MockSharedPreferences(); michael@0: final SharedPreferences v1b = new MockSharedPreferences(); michael@0: michael@0: populateAccountSharedPrefs(v0a); michael@0: michael@0: final int NUM_GLOBALS = 24; michael@0: assertEquals(NUM_GLOBALS, v0a.getAll().size()); michael@0: assertEquals(NUM_GLOBALS, PublicMigrator.upgradeShared0to1(v0a, v1a)); michael@0: assertEquals(NUM_GLOBALS, PublicMigrator.downgradeShared1to0(v1a, v0b)); michael@0: assertEquals(NUM_GLOBALS, PublicMigrator.upgradeShared0to1(v0b, v1b)); michael@0: assertEquals(v0a.getAll(), v0b.getAll()); michael@0: assertEquals(v1a.getAll(), v1b.getAll()); michael@0: } michael@0: michael@0: public void testMigrateAccount0and1() throws Exception { michael@0: final Context context = getApplicationContext(); michael@0: final AccountManager accountManager = AccountManager.get(context); michael@0: final SyncAccountParameters syncAccount = new SyncAccountParameters(context, null, michael@0: TEST_USERNAME, TEST_SYNCKEY, TEST_PASSWORD, null); michael@0: michael@0: Account account = null; michael@0: try { michael@0: account = SyncAccounts.createSyncAccount(syncAccount, false); michael@0: populateAccountData(accountManager, account); michael@0: michael@0: final int NUM_ACCOUNTS = 3; michael@0: final SharedPreferences a = new MockSharedPreferences(); michael@0: final SharedPreferences b = new MockSharedPreferences(); michael@0: michael@0: assertEquals(NUM_ACCOUNTS, PublicMigrator.upgradeAndroidAccount0to1(accountManager, account, a)); michael@0: assertEquals(NUM_ACCOUNTS, a.getAll().size()); michael@0: assertEquals(NUM_ACCOUNTS, PublicMigrator.downgradeAndroidAccount1to0(a, accountManager, account)); michael@0: michael@0: TestSyncAccounts.deleteAccount(this, accountManager, account); michael@0: account = SyncAccounts.createSyncAccount(syncAccount, false); michael@0: michael@0: assertEquals(NUM_ACCOUNTS, PublicMigrator.downgradeAndroidAccount1to0(a, accountManager, account)); michael@0: assertEquals(NUM_ACCOUNTS, PublicMigrator.upgradeAndroidAccount0to1(accountManager, account, b)); michael@0: assertEquals(a.getAll(), b.getAll()); michael@0: } finally { michael@0: if (account != null) { michael@0: TestSyncAccounts.deleteAccount(this, accountManager, account); michael@0: } michael@0: } michael@0: } michael@0: michael@0: public void testMigrate0to1() throws Exception { michael@0: final Context context = getApplicationContext(); michael@0: michael@0: final String ACCOUNT_SHARED_PREFS_NAME = "sync.prefs.3qyu5zoqpuu4zhdiv5l2qthsiter3vop"; michael@0: final String GLOBAL_SHARED_PREFS_NAME = "sync.prefs.global"; michael@0: michael@0: final String path = Utils.getPrefsPath(TEST_PRODUCT, TEST_USERNAME, TEST_SERVERURL, TEST_PROFILE, 0); michael@0: assertEquals(ACCOUNT_SHARED_PREFS_NAME, path); michael@0: final SharedPreferences accountPrefs = context.getSharedPreferences(ACCOUNT_SHARED_PREFS_NAME, Utils.SHARED_PREFERENCES_MODE); michael@0: final SharedPreferences globalPrefs = context.getSharedPreferences(GLOBAL_SHARED_PREFS_NAME, Utils.SHARED_PREFERENCES_MODE); michael@0: michael@0: accountPrefs.edit().clear().commit(); michael@0: globalPrefs.edit().clear().commit(); michael@0: // Clear prefs we're about to write into. michael@0: final SharedPreferences existingPrefs = Utils.getSharedPreferences(context, TEST_PRODUCT, TEST_USERNAME, TEST_SERVERURL, TEST_PROFILE, 1); michael@0: existingPrefs.edit().clear().commit(); michael@0: michael@0: final AccountManager accountManager = AccountManager.get(context); michael@0: final SyncAccountParameters syncAccount = new SyncAccountParameters(context, null, michael@0: TEST_USERNAME, TEST_SYNCKEY, TEST_PASSWORD, null); michael@0: michael@0: Account account = null; michael@0: try { michael@0: account = SyncAccounts.createSyncAccount(syncAccount, false); // Wipes prefs. michael@0: michael@0: populateAccountSharedPrefs(accountPrefs); michael@0: populateGlobalSharedPrefs(globalPrefs); michael@0: populateAccountData(accountManager, account); michael@0: michael@0: ConfigurationMigrator.upgrade0to1(context, accountManager, account, TEST_PRODUCT, TEST_USERNAME, TEST_SERVERURL, TEST_PROFILE); michael@0: } finally { michael@0: if (account != null) { michael@0: TestSyncAccounts.deleteAccount(this, accountManager, account); michael@0: } michael@0: } michael@0: michael@0: Map origAccountPrefs = accountPrefs.getAll(); michael@0: Map origGlobalPrefs = globalPrefs.getAll(); michael@0: assertFalse(origAccountPrefs.isEmpty()); michael@0: assertFalse(origGlobalPrefs.isEmpty()); michael@0: michael@0: final SharedPreferences newPrefs = Utils.getSharedPreferences(context, TEST_PRODUCT, TEST_USERNAME, TEST_SERVERURL, TEST_PROFILE, 1); michael@0: michael@0: // Some global stuff. michael@0: assertEquals(false, newPrefs.getBoolean(SyncConfiguration.PREF_CLUSTER_URL_IS_STALE, true)); michael@0: assertEquals(1340402318649L, newPrefs.getLong(PrefsBackoffHandler.PREF_EARLIEST_NEXT + SyncConstants.BACKOFF_PREF_SUFFIX_11, 111)); michael@0: // Some per-Sync account stuff. michael@0: assertEquals("{\"timestamp\":1340402003370}", newPrefs.getString("bookmarks.remote", null)); michael@0: assertEquals("{\"timestamp\":1340402008397}", newPrefs.getString("bookmarks.local", null)); michael@0: assertEquals("P8gG8ERuJ4H1", newPrefs.getString("bookmarks.syncID", null)); michael@0: assertEquals(1340401961960L, newPrefs.getLong("metaGlobalLastModified", 0)); michael@0: // Some per-Android account stuff. michael@0: assertEquals(TEST_GUID, newPrefs.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null)); michael@0: assertEquals(TEST_CLIENT_NAME, newPrefs.getString(SyncConfiguration.PREF_CLIENT_NAME, null)); michael@0: assertEquals(TEST_NUM_CLIENTS, newPrefs.getLong(SyncConfiguration.PREF_NUM_CLIENTS, -1L)); michael@0: michael@0: // Now try to downgrade. michael@0: accountPrefs.edit().clear().commit(); michael@0: globalPrefs.edit().clear().commit(); michael@0: michael@0: account = null; michael@0: try { michael@0: account = SyncAccounts.createSyncAccount(syncAccount, false); michael@0: michael@0: ConfigurationMigrator.downgrade1to0(context, accountManager, account, TEST_PRODUCT, TEST_USERNAME, TEST_SERVERURL, TEST_PROFILE); michael@0: michael@0: final String V0_PREF_ACCOUNT_GUID = "account.guid"; michael@0: final String V0_PREF_CLIENT_NAME = "account.clientName"; michael@0: final String V0_PREF_NUM_CLIENTS = "account.numClients"; michael@0: michael@0: assertEquals(TEST_GUID, accountManager.getUserData(account, V0_PREF_ACCOUNT_GUID)); michael@0: assertEquals(TEST_CLIENT_NAME, accountManager.getUserData(account, V0_PREF_CLIENT_NAME)); michael@0: assertEquals(Long.valueOf(TEST_NUM_CLIENTS).toString(), accountManager.getUserData(account, V0_PREF_NUM_CLIENTS)); michael@0: } finally { michael@0: if (account != null) { michael@0: TestSyncAccounts.deleteAccount(this, accountManager, account); michael@0: } michael@0: } michael@0: michael@0: // Check re-constituted prefs against old prefs. michael@0: assertEquals(origAccountPrefs, accountPrefs.getAll()); michael@0: assertEquals(origGlobalPrefs, globalPrefs.getAll()); michael@0: } michael@0: }