diff -r 000000000000 -r 6474c204b198 mobile/android/tests/background/junit3/src/sync/TestSyncAccounts.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mobile/android/tests/background/junit3/src/sync/TestSyncAccounts.java Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,343 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +package org.mozilla.gecko.background.sync; + +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.TimeUnit; + +import org.mozilla.gecko.background.common.GlobalConstants; +import org.mozilla.gecko.background.helpers.AndroidSyncTestCase; +import org.mozilla.gecko.sync.ExtendedJSONObject; +import org.mozilla.gecko.sync.SyncConfiguration; +import org.mozilla.gecko.sync.SyncConstants; +import org.mozilla.gecko.sync.Utils; +import org.mozilla.gecko.sync.setup.Constants; +import org.mozilla.gecko.sync.setup.SyncAccounts; +import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.test.InstrumentationTestCase; + +/** + * We can use performWait and performNotify here if we + * are careful about threading issues with AsyncTask. We need to + * take some care to both create and run certain tasks on the main thread -- + * moving the object allocation out of the UI thread causes failures! + *

+ * @see " + * http://stackoverflow.com/questions/2321829/android-asynctask-testing-problem-with-android-test-framework." + */ +public class TestSyncAccounts extends AndroidSyncTestCase { + private static final String TEST_USERNAME = "testAccount@mozilla.com"; + private static final String TEST_SYNCKEY = "testSyncKey"; + private static final String TEST_PASSWORD = "testPassword"; + private static final String TEST_SERVERURL = "test.server.url/"; + private static final String TEST_CLUSTERURL = "test.cluster.url/"; + + public static final String TEST_ACCOUNTTYPE = SyncConstants.ACCOUNTTYPE_SYNC; + + public static final String TEST_PRODUCT = GlobalConstants.BROWSER_INTENT_PACKAGE; + public static final String TEST_PROFILE = Constants.DEFAULT_PROFILE; + public static final long TEST_VERSION = SyncConfiguration.CURRENT_PREFS_VERSION; + + public static final String TEST_PREFERENCE = "testPreference"; + public static final String TEST_SYNC_ID = "testSyncID"; + + private Account account; + private Context context; + private AccountManager accountManager; + private SyncAccountParameters syncAccount; + + public void setUp() { + account = null; + context = getApplicationContext(); + accountManager = AccountManager.get(context); + syncAccount = new SyncAccountParameters(context, null, + TEST_USERNAME, TEST_SYNCKEY, TEST_PASSWORD, null); + } + + public static void deleteAccount(final InstrumentationTestCase test, final AccountManager accountManager, final Account account) { + performWait(new Runnable() { + @Override + public void run() { + try { + test.runTestOnUiThread(new Runnable() { + final AccountManagerCallback callback = new AccountManagerCallback() { + @Override + public void run(AccountManagerFuture future) { + try { + future.getResult(5L, TimeUnit.SECONDS); + } catch (Exception e) { + } + performNotify(); + } + }; + + @Override + public void run() { + accountManager.removeAccount(account, callback, null); + } + }); + } catch (Throwable e) { + performNotify(e); + } + } + }); + } + + public void tearDown() { + if (account == null) { + return; + } + deleteAccount(this, accountManager, account); + account = null; + } + + public void testSyncAccountParameters() { + assertEquals(TEST_USERNAME, syncAccount.username); + assertNull(syncAccount.accountManager); + assertNull(syncAccount.serverURL); + + try { + syncAccount = new SyncAccountParameters(context, null, + null, TEST_SYNCKEY, TEST_PASSWORD, TEST_SERVERURL); + } catch (IllegalArgumentException e) { + return; + } catch (Exception e) { + fail("Did not expect exception: " + e); + } + fail("Expected IllegalArgumentException."); + } + + public void testCreateAccount() { + int before = accountManager.getAccountsByType(TEST_ACCOUNTTYPE).length; + account = SyncAccounts.createSyncAccount(syncAccount, false); + int afterCreate = accountManager.getAccountsByType(TEST_ACCOUNTTYPE).length; + assertTrue(afterCreate > before); + deleteAccount(this, accountManager, account); + account = null; + int afterDelete = accountManager.getAccountsByType(TEST_ACCOUNTTYPE).length; + assertEquals(before, afterDelete); + } + + public void testCreateSecondAccount() { + int before = accountManager.getAccountsByType(TEST_ACCOUNTTYPE).length; + account = SyncAccounts.createSyncAccount(syncAccount, false); + int afterFirst = accountManager.getAccountsByType(TEST_ACCOUNTTYPE).length; + assertTrue(afterFirst > before); + + SyncAccountParameters secondSyncAccount = new SyncAccountParameters(context, null, + "second@username.com", TEST_SYNCKEY, TEST_PASSWORD, null); + + Account second = SyncAccounts.createSyncAccount(secondSyncAccount, false); + assertNotNull(second); + int afterSecond = accountManager.getAccountsByType(TEST_ACCOUNTTYPE).length; + assertTrue(afterSecond > afterFirst); + + deleteAccount(this, accountManager, second); + deleteAccount(this, accountManager, account); + account = null; + + int afterDelete = accountManager.getAccountsByType(TEST_ACCOUNTTYPE).length; + assertEquals(before, afterDelete); + } + + public void testCreateDuplicateAccount() { + int before = accountManager.getAccountsByType(TEST_ACCOUNTTYPE).length; + account = SyncAccounts.createSyncAccount(syncAccount, false); + int afterCreate = accountManager.getAccountsByType(TEST_ACCOUNTTYPE).length; + assertTrue(afterCreate > before); + + Account dupe = SyncAccounts.createSyncAccount(syncAccount, false); + assertNull(dupe); + } + + public void testClientRecord() throws NoSuchAlgorithmException, UnsupportedEncodingException { + final String TEST_NAME = "testName"; + final String TEST_GUID = "testGuid"; + syncAccount = new SyncAccountParameters(context, null, + TEST_USERNAME, TEST_SYNCKEY, TEST_PASSWORD, null, null, TEST_NAME, TEST_GUID); + account = SyncAccounts.createSyncAccount(syncAccount, false); + assertNotNull(account); + + SharedPreferences prefs = Utils.getSharedPreferences(context, TEST_PRODUCT, TEST_USERNAME, + SyncConstants.DEFAULT_AUTH_SERVER, TEST_PROFILE, TEST_VERSION); + + // Verify that client record is set. + assertEquals(TEST_GUID, prefs.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null)); + assertEquals(TEST_NAME, prefs.getString(SyncConfiguration.PREF_CLIENT_NAME, null)); + + // Let's verify that clusterURL is correctly not set. + String clusterURL = prefs.getString(SyncConfiguration.PREF_CLUSTER_URL, null); + assertNull(clusterURL); + } + + public void testClusterURL() throws NoSuchAlgorithmException, UnsupportedEncodingException { + syncAccount = new SyncAccountParameters(context, null, + TEST_USERNAME, TEST_SYNCKEY, TEST_PASSWORD, TEST_SERVERURL, TEST_CLUSTERURL, null, null); + account = SyncAccounts.createSyncAccount(syncAccount, false); + assertNotNull(account); + + SharedPreferences prefs = Utils.getSharedPreferences(context, TEST_PRODUCT, TEST_USERNAME, + TEST_SERVERURL, TEST_PROFILE, TEST_VERSION); + String clusterURL = prefs.getString(SyncConfiguration.PREF_CLUSTER_URL, null); + assertNotNull(clusterURL); + assertEquals(TEST_CLUSTERURL, clusterURL); + + // Let's verify that client name and GUID are not set. + assertNull(prefs.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null)); + assertNull(prefs.getString(SyncConfiguration.PREF_CLIENT_NAME, null)); + } + + /** + * Verify that creating an account wipes stale settings in Shared Preferences. + */ + public void testCreatingWipesSharedPrefs() throws Exception { + final String TEST_PREFERENCE = "testPreference"; + final String TEST_SYNC_ID = "testSyncID"; + + SharedPreferences prefs = Utils.getSharedPreferences(context, TEST_PRODUCT, TEST_USERNAME, + TEST_SERVERURL, TEST_PROFILE, TEST_VERSION); + prefs.edit().putString(SyncConfiguration.PREF_SYNC_ID, TEST_SYNC_ID).commit(); + prefs.edit().putString(TEST_PREFERENCE, TEST_SYNC_ID).commit(); + + syncAccount = new SyncAccountParameters(context, null, + TEST_USERNAME, TEST_SYNCKEY, TEST_PASSWORD, TEST_SERVERURL); + account = SyncAccounts.createSyncAccount(syncAccount, false); + assertNotNull(account); + + // All values deleted (known and unknown). + assertNull(prefs.getString(SyncConfiguration.PREF_SYNC_ID, null)); + assertNull(prefs.getString(TEST_SYNC_ID, null)); + } + + /** + * Verify that creating an account preserves settings in Shared Preferences when asked. + */ + public void testCreateSyncAccountWithExistingPreferences() throws Exception { + + SharedPreferences prefs = Utils.getSharedPreferences(context, TEST_PRODUCT, TEST_USERNAME, + TEST_SERVERURL, TEST_PROFILE, TEST_VERSION); + + prefs.edit().putString(SyncConfiguration.PREF_SYNC_ID, TEST_SYNC_ID).commit(); + prefs.edit().putString(TEST_PREFERENCE, TEST_SYNC_ID).commit(); + + assertNotNull(prefs.getString(TEST_PREFERENCE, null)); + assertNotNull(prefs.getString(SyncConfiguration.PREF_SYNC_ID, null)); + + syncAccount = new SyncAccountParameters(context, null, + TEST_USERNAME, TEST_SYNCKEY, TEST_PASSWORD, TEST_SERVERURL); + account = SyncAccounts.createSyncAccountPreservingExistingPreferences(syncAccount, false); + assertNotNull(account); + + // All values remain (known and unknown). + assertNotNull(prefs.getString(TEST_PREFERENCE, null)); + assertNotNull(prefs.getString(SyncConfiguration.PREF_SYNC_ID, null)); + } + + protected void assertParams(final SyncAccountParameters params) throws Exception { + assertNotNull(params); + assertEquals(context, params.context); + assertEquals(Utils.usernameFromAccount(TEST_USERNAME), params.username); + assertEquals(TEST_PASSWORD, params.password); + assertEquals(TEST_SERVERURL, params.serverURL); + assertEquals(TEST_SYNCKEY, params.syncKey); + } + + public void testBlockingFromAndroidAccountV0() throws Throwable { + syncAccount = new SyncAccountParameters(context, null, + TEST_USERNAME, TEST_SYNCKEY, TEST_PASSWORD, TEST_SERVERURL, TEST_CLUSTERURL, null, null); + try { + account = SyncAccounts.createSyncAccount(syncAccount); + assertNotNull(account); + + // Test fetching parameters multiple times. Historically, we needed to + // invalidate this token type every fetch; now we don't, but we'd like + // to ensure multiple fetches work. + SyncAccountParameters params = SyncAccounts.blockingFromAndroidAccountV0(context, accountManager, account); + assertParams(params); + + params = SyncAccounts.blockingFromAndroidAccountV0(context, accountManager, account); + assertParams(params); + + // Test this works on the main thread. + this.runTestOnUiThread(new Runnable() { + @Override + public void run() { + SyncAccountParameters params; + try { + params = SyncAccounts.blockingFromAndroidAccountV0(context, accountManager, account); + assertParams(params); + } catch (Exception e) { + fail("Fetching Sync account parameters failed on UI thread."); + } + } + }); + } finally { + if (account != null) { + deleteAccount(this, accountManager, account); + account = null; + } + } + } + + public void testMakeSyncAccountDeletedIntent() throws Throwable { + syncAccount = new SyncAccountParameters(context, null, + TEST_USERNAME, TEST_SYNCKEY, TEST_PASSWORD, TEST_SERVERURL, TEST_CLUSTERURL, null, null); + try { + account = SyncAccounts.createSyncAccount(syncAccount); + assertNotNull(account); + + Intent intent = SyncAccounts.makeSyncAccountDeletedIntent(context, accountManager, account); + assertEquals(SyncConstants.SYNC_ACCOUNT_DELETED_ACTION, intent.getAction()); + assertEquals(SyncConstants.SYNC_ACCOUNT_DELETED_INTENT_VERSION, intent.getLongExtra(Constants.JSON_KEY_VERSION, 0)); + assertEquals(TEST_USERNAME, intent.getStringExtra(Constants.JSON_KEY_ACCOUNT)); + assertTrue(Math.abs(intent.getLongExtra(Constants.JSON_KEY_TIMESTAMP, 0) - System.currentTimeMillis()) < 1000); + + String payload = intent.getStringExtra(Constants.JSON_KEY_PAYLOAD); + assertNotNull(payload); + + SyncAccountParameters params = new SyncAccountParameters(context, accountManager, ExtendedJSONObject.parseJSONObject(payload)); + // Can't use assertParams because Sync key is deleted. + assertNotNull(params); + assertEquals(context, params.context); + assertEquals(Utils.usernameFromAccount(TEST_USERNAME), params.username); + assertEquals(TEST_PASSWORD, params.password); + assertEquals(TEST_SERVERURL, params.serverURL); + assertEquals("", params.syncKey); + } finally { + if (account != null) { + deleteAccount(this, accountManager, account); + account = null; + } + } + } + + public void testBlockingPrefsFromAndroidAccountV0() throws Exception { + // Create test account with prefs. We use a different username to avoid a + // timing issue, where the delayed clean-up of the account created by the + // previous test deletes the preferences for this account. + SharedPreferences prefs = Utils.getSharedPreferences(context, TEST_PRODUCT, + TEST_USERNAME + "2", TEST_SERVERURL, TEST_PROFILE, TEST_VERSION); + prefs.edit().putString(TEST_PREFERENCE, TEST_SYNC_ID).commit(); + + syncAccount = new SyncAccountParameters(context, null, + TEST_USERNAME + "2", TEST_SYNCKEY, TEST_PASSWORD, TEST_SERVERURL); + account = SyncAccounts.createSyncAccountPreservingExistingPreferences(syncAccount, false); + assertNotNull(account); + + // Fetch account and check prefs. + SharedPreferences sharedPreferences = SyncAccounts.blockingPrefsFromAndroidAccountV0(context, accountManager, + account, TEST_PRODUCT, TEST_PROFILE, TEST_VERSION); + assertNotNull(sharedPreferences); + assertEquals(TEST_SYNC_ID, sharedPreferences.getString(TEST_PREFERENCE, null)); + } +}