/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ package org.mozilla.gecko.sync.config.test; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import org.mozilla.android.sync.test.AndroidSyncTestCase; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.GlobalConstants; import org.mozilla.gecko.sync.SyncConstants; import org.mozilla.gecko.sync.SyncConfiguration; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.config.AccountPickler; import org.mozilla.gecko.sync.setup.Constants; 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.ContentResolver; import android.content.Context; import android.content.SharedPreferences; public class TestAccountPickler extends AndroidSyncTestCase { public static final String TEST_FILENAME = "test.json"; public static final String TEST_ACCOUNTTYPE = SyncConstants.ACCOUNTTYPE_SYNC; // Test account names must start with TEST_USERNAME in order to be recognized // as test accounts and deleted in tearDown. public static final String TEST_USERNAME = "testAccount@mozilla.com"; public static final String TEST_USERNAME2 = TEST_USERNAME + "2"; public static final String TEST_SYNCKEY = "testSyncKey"; public static final String TEST_PASSWORD = "testPassword"; public static final String TEST_SERVER_URL = "test.server.url/"; public static final String TEST_CLUSTER_URL = "test.cluster.url/"; public static final String TEST_CLIENT_NAME = "testClientName"; public static final String TEST_CLIENT_GUID = "testClientGuid"; public static final String TEST_PRODUCT = GlobalConstants.BROWSER_INTENT_PACKAGE; public static final String TEST_PROFILE = "default"; public static final long TEST_VERSION = SyncConfiguration.CURRENT_PREFS_VERSION; protected SyncAccountParameters params; protected Context context; protected AccountManager accountManager; protected int numAccounts; public void setUp() { context = getApplicationContext(); accountManager = AccountManager.get(context); params = new SyncAccountParameters(context, accountManager, TEST_USERNAME, TEST_SYNCKEY, TEST_PASSWORD, TEST_SERVER_URL, TEST_CLUSTER_URL, TEST_CLIENT_NAME, TEST_CLIENT_GUID); numAccounts = accountManager.getAccountsByType(TEST_ACCOUNTTYPE).length; } public static List<Account> getTestAccounts(final AccountManager accountManager) { final List<Account> testAccounts = new ArrayList<Account>(); final Account[] accounts = accountManager.getAccountsByType(TEST_ACCOUNTTYPE); for (Account account : accounts) { if (account.name.startsWith(TEST_USERNAME)) { testAccounts.add(account); } } return testAccounts; } public void deleteTestAccounts() { for (Account account : getTestAccounts(accountManager)) { TestSyncAccounts.deleteAccount(this, accountManager, account); } } public void tearDown() { deleteTestAccounts(); assertEquals(numAccounts, accountManager.getAccountsByType(TEST_ACCOUNTTYPE).length); } public static void assertFileNotPresent(final Context context, final String filename) throws Exception { // Verify file is not present. FileInputStream fis = null; try { fis = context.openFileInput(TEST_FILENAME); fail("Should get FileNotFoundException."); } catch (FileNotFoundException e) { // Do nothing; file should not exist. } finally { if (fis != null) { fis.close(); } } } public void testPersist() throws Exception { context.deleteFile(TEST_FILENAME); assertFileNotPresent(context, TEST_FILENAME); AccountPickler.pickle(context, TEST_FILENAME, params, true); final String s = Utils.readFile(context, TEST_FILENAME); assertNotNull(s); final ExtendedJSONObject o = ExtendedJSONObject.parseJSONObject(s); assertEquals(TEST_USERNAME, o.getString(Constants.JSON_KEY_ACCOUNT)); assertEquals(TEST_PASSWORD, o.getString(Constants.JSON_KEY_PASSWORD)); assertEquals(TEST_SERVER_URL, o.getString(Constants.JSON_KEY_SERVER)); assertEquals(TEST_SYNCKEY, o.getString(Constants.JSON_KEY_SYNCKEY)); assertEquals(TEST_CLUSTER_URL, o.getString(Constants.JSON_KEY_CLUSTER)); assertEquals(TEST_CLIENT_NAME, o.getString(Constants.JSON_KEY_CLIENT_NAME)); assertEquals(TEST_CLIENT_GUID, o.getString(Constants.JSON_KEY_CLIENT_GUID)); assertEquals(new Boolean(true), o.get(Constants.JSON_KEY_SYNC_AUTOMATICALLY)); assertEquals(new Long(AccountPickler.VERSION), o.getLong(Constants.JSON_KEY_VERSION)); assertTrue(o.containsKey(Constants.JSON_KEY_TIMESTAMP)); } public void testDeletePickle() throws Exception { AccountPickler.pickle(context, TEST_FILENAME, params, false); // Verify file is present. final String s = Utils.readFile(context, TEST_FILENAME); assertNotNull(s); assertTrue(s.length() > 0); AccountPickler.deletePickle(context, TEST_FILENAME); assertFileNotPresent(context, TEST_FILENAME); } public Account deleteAccountsAndUnpickle(final Context context, final AccountManager accountManager, final String filename) { deleteTestAccounts(); assertEquals(0, getTestAccounts(accountManager).size()); return AccountPickler.unpickle(context, filename); } public void testUnpickleSuccess() throws Exception { AccountPickler.pickle(context, TEST_FILENAME, params, true); // Make sure we have no accounts hanging around. final Account account = deleteAccountsAndUnpickle(context, accountManager, TEST_FILENAME); assertNotNull(account); try { assertEquals(1, getTestAccounts(accountManager).size()); assertTrue(ContentResolver.getSyncAutomatically(account, BrowserContract.AUTHORITY)); assertEquals(account.name, TEST_USERNAME); // Verify Account parameters are in place. Not testing clusterURL since it's stored in // shared prefs and it's less critical. final String password = accountManager.getPassword(account); final String serverURL = accountManager.getUserData(account, Constants.OPTION_SERVER); final String syncKey = accountManager.getUserData(account, Constants.OPTION_SYNCKEY); assertEquals(TEST_PASSWORD, password); assertEquals(TEST_SERVER_URL, serverURL); assertEquals(TEST_SYNCKEY, syncKey); // Verify shared prefs parameters are in place. final SharedPreferences prefs = Utils.getSharedPreferences(context, TEST_PRODUCT, TEST_USERNAME, TEST_SERVER_URL, TEST_PROFILE, TEST_VERSION); final String clusterURL = prefs.getString(SyncConfiguration.PREF_CLUSTER_URL, null); final String clientName = prefs.getString(SyncConfiguration.PREF_CLIENT_NAME, null); final String clientGuid = prefs.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null); assertEquals(TEST_CLUSTER_URL, clusterURL); assertEquals(TEST_CLIENT_NAME, clientName); assertEquals(TEST_CLIENT_GUID, clientGuid); } finally { TestSyncAccounts.deleteAccount(this, accountManager, account); } } public void testUnpickleNoAutomatic() throws Exception { AccountPickler.pickle(context, TEST_FILENAME, params, false); // Make sure we have no accounts hanging around. final Account account = deleteAccountsAndUnpickle(context, accountManager, TEST_FILENAME); assertNotNull(account); try { assertEquals(1, getTestAccounts(accountManager).size()); assertFalse(ContentResolver.getSyncAutomatically(account, BrowserContract.AUTHORITY)); } finally { TestSyncAccounts.deleteAccount(this, accountManager, account); } } public void testUnpickleNoFile() { // Just in case file is hanging around. context.deleteFile(TEST_FILENAME); final Account account = deleteAccountsAndUnpickle(context, accountManager, TEST_FILENAME); assertNull(account); } public void testUnpickleIncompleteUserData() throws Exception { final FileOutputStream fos = context.openFileOutput(TEST_FILENAME, Context.MODE_PRIVATE); final PrintStream ps = (new PrintStream(fos)); ps.print("{}"); // Valid JSON, just missing everything. ps.close(); fos.close(); final Account account = deleteAccountsAndUnpickle(context, accountManager, TEST_FILENAME); assertNull(account); } public void testUnpickleMalformedFile() throws Exception { final FileOutputStream fos = context.openFileOutput(TEST_FILENAME, Context.MODE_PRIVATE); final PrintStream ps = (new PrintStream(fos)); ps.print("{1:!}"); // Not valid JSON. ps.close(); fos.close(); final Account account = deleteAccountsAndUnpickle(context, accountManager, TEST_FILENAME); assertNull(account); } public void testUnpickleAccountAlreadyExists() { AccountPickler.pickle(context, TEST_FILENAME, params, false); // Make sure we have no test accounts hanging around. final Account account = deleteAccountsAndUnpickle(context, accountManager, TEST_FILENAME); assertNotNull(account); assertEquals(TEST_USERNAME, account.name); // Now replace file with new username. params = new SyncAccountParameters(context, accountManager, TEST_USERNAME2, TEST_SYNCKEY, TEST_PASSWORD, TEST_SERVER_URL, null, TEST_CLIENT_NAME, TEST_CLIENT_GUID); AccountPickler.pickle(context, TEST_FILENAME, params, false); // Checking if sync accounts exist could try to unpickle. That unpickle // would load an account with a different username, so we can check that // nothing was unpickled by verifying that the username has not changed. assertTrue(SyncAccounts.syncAccountsExist(context)); for (Account a : getTestAccounts(accountManager)) { assertEquals(TEST_USERNAME, a.name); } } }