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