| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.chrome.browser.sync; |
| |
| import android.accounts.Account; |
| import android.content.Context; |
| import android.support.test.InstrumentationRegistry; |
| |
| import org.junit.Assert; |
| import org.junit.runner.Description; |
| import org.junit.runners.model.Statement; |
| |
| import org.chromium.base.ThreadUtils; |
| import org.chromium.chrome.browser.ChromeActivity; |
| import org.chromium.chrome.browser.identity.UniqueIdentificationGenerator; |
| import org.chromium.chrome.browser.identity.UniqueIdentificationGeneratorFactory; |
| import org.chromium.chrome.browser.identity.UuidBasedUniqueIdentificationGenerator; |
| import org.chromium.chrome.browser.signin.SigninManager; |
| import org.chromium.chrome.browser.signin.SignoutReason; |
| import org.chromium.chrome.test.ChromeActivityTestRule; |
| import org.chromium.chrome.test.util.ApplicationTestUtils; |
| import org.chromium.chrome.test.util.browser.signin.SigninTestUtil; |
| import org.chromium.chrome.test.util.browser.sync.SyncTestUtil; |
| import org.chromium.components.sync.AndroidSyncSettings; |
| import org.chromium.components.sync.ModelType; |
| import org.chromium.components.sync.test.util.MockSyncContentResolverDelegate; |
| import org.chromium.content_public.browser.test.util.Criteria; |
| import org.chromium.content_public.browser.test.util.CriteriaHelper; |
| |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.Semaphore; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * TestRule for common functionality between sync tests. |
| */ |
| public class SyncTestRule extends ChromeActivityTestRule<ChromeActivity> { |
| private static final String TAG = "SyncTestBase"; |
| |
| private static final String CLIENT_ID = "Client_ID"; |
| |
| private static final Set<Integer> USER_SELECTABLE_TYPES = |
| new HashSet<Integer>(Arrays.asList(new Integer[] { |
| ModelType.AUTOFILL, ModelType.BOOKMARKS, ModelType.PASSWORDS, |
| ModelType.PREFERENCES, ModelType.PROXY_TABS, ModelType.TYPED_URLS, |
| })); |
| |
| public abstract static class DataCriteria<T> extends Criteria { |
| public DataCriteria() { |
| super("Sync data criteria not met."); |
| } |
| |
| public abstract boolean isSatisfied(List<T> data); |
| |
| public abstract List<T> getData() throws Exception; |
| |
| @Override |
| public boolean isSatisfied() { |
| try { |
| return isSatisfied(getData()); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| } |
| |
| private Context mContext; |
| private FakeServerHelper mFakeServerHelper; |
| private ProfileSyncService mProfileSyncService; |
| private MockSyncContentResolverDelegate mSyncContentResolver; |
| |
| public SyncTestRule() { |
| super(ChromeActivity.class); |
| } |
| |
| /**Getters for Test variables */ |
| public Context getTargetContext() { |
| return mContext; |
| } |
| |
| public FakeServerHelper getFakeServerHelper() { |
| return mFakeServerHelper; |
| } |
| |
| public ProfileSyncService getProfileSyncService() { |
| return mProfileSyncService; |
| } |
| |
| public MockSyncContentResolverDelegate getSyncContentResolver() { |
| return mSyncContentResolver; |
| } |
| |
| public void startMainActivityForSyncTest() throws Exception { |
| // Start the activity by opening about:blank. This URL is ideal because it is not synced as |
| // a typed URL. If another URL is used, it could interfere with test data. |
| startMainActivityOnBlankPage(); |
| } |
| |
| private void setUpMockAndroidSyncSettings() { |
| mSyncContentResolver = new MockSyncContentResolverDelegate(); |
| mSyncContentResolver.setMasterSyncAutomatically(true); |
| AndroidSyncSettings.overrideForTests(mSyncContentResolver, null); |
| } |
| |
| public Account setUpTestAccount() { |
| Account account = SigninTestUtil.addTestAccount(); |
| Assert.assertFalse(SyncTestUtil.isSyncRequested()); |
| return account; |
| } |
| |
| public Account setUpTestAccountAndSignIn() { |
| Account account = setUpTestAccount(); |
| signIn(account); |
| return account; |
| } |
| |
| public void startSync() { |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| mProfileSyncService.requestStart(); |
| } |
| }); |
| } |
| |
| public void startSyncAndWait() { |
| startSync(); |
| SyncTestUtil.waitForSyncActive(); |
| } |
| |
| public void stopSync() { |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| mProfileSyncService.requestStop(); |
| } |
| }); |
| InstrumentationRegistry.getInstrumentation().waitForIdleSync(); |
| } |
| |
| public void signIn(final Account account) { |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| SigninManager.get().signIn(account, null, null); |
| } |
| }); |
| SyncTestUtil.waitForSyncActive(); |
| SyncTestUtil.triggerSyncAndWaitForCompletion(); |
| Assert.assertEquals(account, SigninTestUtil.getCurrentAccount()); |
| } |
| |
| public void signOut() throws InterruptedException { |
| final Semaphore s = new Semaphore(0); |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| SigninManager.get().signOut(SignoutReason.SIGNOUT_TEST, new Runnable() { |
| @Override |
| public void run() { |
| s.release(); |
| } |
| }); |
| } |
| }); |
| Assert.assertTrue(s.tryAcquire(SyncTestUtil.TIMEOUT_MS, TimeUnit.MILLISECONDS)); |
| Assert.assertNull(SigninTestUtil.getCurrentAccount()); |
| Assert.assertFalse(SyncTestUtil.isSyncRequested()); |
| } |
| |
| public void clearServerData() { |
| mFakeServerHelper.clearServerData(); |
| SyncTestUtil.triggerSync(); |
| CriteriaHelper.pollUiThread(new Criteria("Timed out waiting for sync to stop.") { |
| @Override |
| public boolean isSatisfied() { |
| return !ProfileSyncService.get().isSyncRequested(); |
| } |
| }, SyncTestUtil.TIMEOUT_MS, SyncTestUtil.INTERVAL_MS); |
| } |
| |
| /* |
| * Enables the |modelType| Sync data type, which must be in USER_SELECTABLE_TYPES. |
| */ |
| public void enableDataType(final int modelType) { |
| ThreadUtils.runOnUiThreadBlocking(() -> { |
| Set<Integer> chosenTypes = mProfileSyncService.getChosenDataTypes(); |
| chosenTypes.add(modelType); |
| mProfileSyncService.setChosenDataTypes(false, chosenTypes); |
| }); |
| } |
| |
| /* |
| * Disables the |modelType| Sync data type, which must be in USER_SELECTABLE_TYPES. |
| */ |
| public void disableDataType(final int modelType) { |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| Set<Integer> chosenTypes = mProfileSyncService.getChosenDataTypes(); |
| chosenTypes.remove(modelType); |
| mProfileSyncService.setChosenDataTypes(false, chosenTypes); |
| } |
| }); |
| } |
| |
| public void pollInstrumentationThread(Criteria criteria) { |
| CriteriaHelper.pollInstrumentationThread( |
| criteria, SyncTestUtil.TIMEOUT_MS, SyncTestUtil.INTERVAL_MS); |
| } |
| |
| @Override |
| public Statement apply(final Statement statement, final Description desc) { |
| final Statement base = super.apply(new Statement() { |
| @Override |
| public void evaluate() throws Throwable { |
| startMainActivityForSyncTest(); |
| mContext = InstrumentationRegistry.getTargetContext(); |
| |
| setUpMockAndroidSyncSettings(); |
| |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| // Ensure SyncController is registered with the new AndroidSyncSettings. |
| AndroidSyncSettings.registerObserver(SyncController.get(mContext)); |
| mFakeServerHelper = FakeServerHelper.get(); |
| } |
| }); |
| FakeServerHelper.useFakeServer(mContext); |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| mProfileSyncService = ProfileSyncService.get(); |
| } |
| }); |
| |
| UniqueIdentificationGeneratorFactory.registerGenerator( |
| UuidBasedUniqueIdentificationGenerator.GENERATOR_ID, |
| new UniqueIdentificationGenerator() { |
| @Override |
| public String getUniqueId(String salt) { |
| return CLIENT_ID; |
| } |
| }, |
| true); |
| statement.evaluate(); |
| } |
| }, desc); |
| return new Statement() { |
| @Override |
| public void evaluate() throws Throwable { |
| ruleSetUp(); |
| base.evaluate(); |
| ruleTearDown(); |
| } |
| }; |
| } |
| |
| private void ruleSetUp() throws Throwable { |
| // This must be called before doing anything with the SharedPreferences because |
| // ChromeActivityTestRule's setUp normally wipes them clean between runs. |
| // Setting a SharedPreference here via the SigninTestUtil.setUpAuthForTest() call below |
| // causes Android to cache them before the files get cleared, meaning that a data clear |
| // is useless and test runs influence each other. |
| ApplicationTestUtils.clearAppData(InstrumentationRegistry.getTargetContext()); |
| |
| // This must be called before super.setUp() in order for test authentication to work. |
| SigninTestUtil.setUpAuthForTest(InstrumentationRegistry.getInstrumentation()); |
| } |
| |
| private void ruleTearDown() throws Exception { |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| mProfileSyncService.requestStop(); |
| FakeServerHelper.deleteFakeServer(); |
| } |
| }); |
| SigninTestUtil.resetSigninState(); |
| SigninTestUtil.tearDownAuthForTest(); |
| } |
| } |