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.io.UnsupportedEncodingException; michael@0: import java.security.NoSuchAlgorithmException; michael@0: import java.util.concurrent.TimeUnit; 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.sync.ExtendedJSONObject; 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.setup.Constants; 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.accounts.AccountManagerCallback; michael@0: import android.accounts.AccountManagerFuture; michael@0: import android.content.Context; michael@0: import android.content.Intent; michael@0: import android.content.SharedPreferences; michael@0: import android.test.InstrumentationTestCase; michael@0: michael@0: /** michael@0: * We can use performWait and performNotify here if we michael@0: * are careful about threading issues with AsyncTask. We need to michael@0: * take some care to both create and run certain tasks on the main thread -- michael@0: * moving the object allocation out of the UI thread causes failures! michael@0: *

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