// Copyright 2016 The Chromium Authors
// 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;
import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static java.util.function.Function.identity;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupManager;
import android.content.SharedPreferences;
import android.os.ParcelFileDescriptor;
import android.util.Pair;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.LooperMode;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.Callback;
import org.chromium.base.ContextUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.browser.firstrun.FirstRunStatus;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.init.AsyncInitTaskRunner;
import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
import org.chromium.chrome.browser.signin.services.SigninManager;
import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
import org.chromium.components.prefs.PrefService;
import org.chromium.components.signin.SigninFeatures;
import org.chromium.components.signin.base.CoreAccountInfo;
import org.chromium.components.signin.identitymanager.ConsentLevel;
import org.chromium.components.signin.identitymanager.IdentityManager;
import org.chromium.components.signin.test.util.FakeAccountManagerFacade;
import org.chromium.components.sync.internal.SyncPrefNames;
import org.chromium.components.user_prefs.UserPrefs;
import org.chromium.components.user_prefs.UserPrefsJni;
import org.chromium.content_public.common.ContentProcessInfo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
/** Unit tests for {@link org.chromium.chrome.browser.ChromeBackupAgent}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(
manifest = Config.NONE,
shadows = {
ChromeBackupAgentTest.BackupManagerShadow.class,
})
@LooperMode(LooperMode.Mode.INSTRUMENTATION_TEST)
public class ChromeBackupAgentTest {
@Rule public TemporaryFolder mTempDir = new TemporaryFolder();
/** Shadow to allow counting of dataChanged calls. */
@Implements(BackupManager.class)
public static class BackupManagerShadow {
private static int sDataChangedCalls;
public static int getDataChangedCalls() {
return sDataChangedCalls;
}
public static void clearDataChangedCalls() {
sDataChangedCalls = 0;
}
@Implementation
public void dataChanged() {
sDataChangedCalls++;
}
}
@Rule public JniMocker mocker = new JniMocker();
@Rule
public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
@Mock private ChromeBackupAgentImpl.Natives mChromeBackupAgentJniMock;
@Mock private IdentityManager mIdentityManagerMock;
@Mock private UserPrefs.Natives mUserPrefsJniMock;
@Mock private PrefService mPrefService;
@Mock private Profile mProfile;
@Mock private SigninManager mSigninManager;
private ChromeBackupAgentImpl mAgent;
private AsyncInitTaskRunner mTaskRunner;
private boolean mIsAccountManaged;
private final CoreAccountInfo mAccountInfo =
CoreAccountInfo.createFromEmailAndGaiaId(
"user1", FakeAccountManagerFacade.toGaiaId("user1"));
private static final String SHARED_PREF_NOT_BACKED_UP = "shared_pref_not_backed_up";
private static final String NATIVE_PREF_NOT_BACKED_UP = "native_pref_not_backed_up";
private static final String ACCOUNT_SETTINGS_PREF_VALUE = "account_settings_pref_value";
private static final int BACKUP_BOOL_PREF_COUNT =
ChromeBackupAgentImpl.BACKUP_NATIVE_SYNC_TYPE_BOOL_PREFS.length
+ ChromeBackupAgentImpl.BACKUP_ANDROID_BOOL_PREFS.length;
// The 3 additional preferences are: SELECTED_TYPES_PER_ACCOUNT, the syncing account and the
// signed-in account.
private static final int BACKUP_PREF_COUNT = BACKUP_BOOL_PREF_COUNT + 3;
// Number of preferences that default to true in the test, see setUpPrefsToBackup().
private static final int DEFAULT_TRUE_BOOL_PREF_COUNT = 2;
// Mutable map containing boolean preferences names and their values for the fake backup.
private HashMap<String, Boolean> mNativeBoolPrefBackupValues =
new HashMap(
Arrays.stream(ChromeBackupAgentImpl.BACKUP_NATIVE_SYNC_TYPE_BOOL_PREFS)
.collect(Collectors.toMap(identity(), pref -> false)));
// Sets up default values for native and android prefs to be backed up.
private void setUpPrefsToBackup(SharedPreferences prefs) {
when(mChromeBackupAgentJniMock.getSerializedDict(
mPrefService, SyncPrefNames.SELECTED_TYPES_PER_ACCOUNT))
.thenReturn(ACCOUNT_SETTINGS_PREF_VALUE);
// Other boolean prefs in BACKUP_NATIVE_SYNC_TYPE_BOOL_PREFS are false by default.
when(mPrefService.getBoolean(SyncPrefNames.SYNC_PASSWORDS)).thenReturn(true);
SharedPreferences.Editor editor = prefs.edit();
// In production some of these prefs can't be present in SharedPreferences at the same time,
// but ChromeBackupAgentImpl is agnostic to that. The focus of these tests is making sure
// that all the allowlisted prefs are backed up, and none other.
editor.putBoolean(ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE, true);
editor.putBoolean(ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED, false);
editor.putBoolean(ChromePreferenceKeys.FIRST_RUN_LIGHTWEIGHT_FLOW_COMPLETE, false);
editor.putBoolean(ChromePreferenceKeys.PRIVACY_METRICS_REPORTING_PERMITTED_BY_USER, false);
editor.putBoolean(
ChromePreferenceKeys.PRIVACY_METRICS_REPORTING_PERMITTED_BY_POLICY, false);
editor.putBoolean(SHARED_PREF_NOT_BACKED_UP, false);
doReturn(mAccountInfo).when(mIdentityManagerMock).getPrimaryAccountInfo(anyInt());
editor.apply();
}
@Before
public void setUp() {
// Create the agent to test; override fetching the task runner, and spy on the agent to
// allow us to validate calls to these methods.
mAgent =
spy(
new ChromeBackupAgentImpl() {
@Override
AsyncInitTaskRunner createAsyncInitTaskRunner(CountDownLatch latch) {
latch.countDown();
return mTaskRunner;
}
});
MockitoAnnotations.initMocks(this);
ProfileManager.setLastUsedProfileForTesting(mProfile);
mocker.mock(ChromeBackupAgentImplJni.TEST_HOOKS, mChromeBackupAgentJniMock);
mocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsJniMock);
when(mUserPrefsJniMock.get(mProfile)).thenReturn(mPrefService);
IdentityServicesProvider identityServicesProvider = mock(IdentityServicesProvider.class);
IdentityServicesProvider.setInstanceForTests(identityServicesProvider);
when(identityServicesProvider.getIdentityManager(any())).thenReturn(mIdentityManagerMock);
when(mIdentityManagerMock.getPrimaryAccountInfo(ConsentLevel.SYNC)).thenReturn(null);
when(identityServicesProvider.getSigninManager(any())).thenReturn(mSigninManager);
doAnswer(
(invocation) -> {
Runnable runnable = invocation.getArgument(0);
runnable.run();
return null;
})
.when(mSigninManager)
.runAfterOperationInProgress(any());
doAnswer(
(invocation) -> {
SigninManager.SignInCallback callback = invocation.getArgument(2);
callback.onSignInComplete();
callback.onPrefsCommitted();
return null;
})
.when(mSigninManager)
.signin(eq(mAccountInfo), anyInt(), any());
doAnswer(
(invocation) -> {
Callback<Boolean> callback = invocation.getArgument(1);
callback.onResult(mIsAccountManaged);
return null;
})
.when(mSigninManager)
.isAccountManaged(eq(mAccountInfo), any());
// Mock initializing the browser
doReturn(true).when(mAgent).initializeBrowser();
// Mock the AsyncTaskRunner.
mTaskRunner = mock(AsyncInitTaskRunner.class);
ContentProcessInfo.setInChildProcess(false);
}
/**
* Test method for {@link ChromeBackupAgent#onBackup} testing first backup with a syncing
* account.
*/
@Test
public void testOnBackup_firstBackup_syncing() throws IOException, ClassNotFoundException {
// Mock the backup data.
BackupDataOutput backupData = mock(BackupDataOutput.class);
// Set up some preferences to back up.
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
setUpPrefsToBackup(prefs);
File stateFile = mTempDir.newFile();
try (ParcelFileDescriptor newState =
ParcelFileDescriptor.open(stateFile, ParcelFileDescriptor.MODE_WRITE_ONLY)) {
// Run the test function.
mAgent.onBackup(null, backupData, newState);
}
// Check that the right things were written to the backup
verify(backupData).writeEntityHeader("native." + SyncPrefNames.SYNC_PASSWORDS, 1);
byte[] accountSettingsPrefBytes =
ApiCompatibilityUtils.getBytesUtf8(ACCOUNT_SETTINGS_PREF_VALUE);
verify(backupData)
.writeEntityHeader(
"NativeJsonDict." + SyncPrefNames.SELECTED_TYPES_PER_ACCOUNT,
accountSettingsPrefBytes.length);
verify(backupData)
.writeEntityHeader(
"AndroidDefault." + ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE, 1);
verify(backupData)
.writeEntityHeader(
"AndroidDefault." + ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED, 1);
verify(backupData)
.writeEntityHeader(
"AndroidDefault."
+ ChromePreferenceKeys.FIRST_RUN_LIGHTWEIGHT_FLOW_COMPLETE,
1);
verify(backupData)
.writeEntityHeader(
"AndroidDefault."
+ ChromePreferenceKeys.PRIVACY_METRICS_REPORTING_PERMITTED_BY_USER,
1);
verify(backupData, times(DEFAULT_TRUE_BOOL_PREF_COUNT)).writeEntityData(new byte[] {1}, 1);
verify(backupData, times(BACKUP_BOOL_PREF_COUNT - DEFAULT_TRUE_BOOL_PREF_COUNT))
.writeEntityData(new byte[] {0}, 1);
byte[] unameBytes = ApiCompatibilityUtils.getBytesUtf8(mAccountInfo.getEmail());
verify(backupData)
.writeEntityHeader(
"AndroidDefault." + ChromeBackupAgentImpl.SYNCING_ACCOUNT_KEY,
unameBytes.length);
verify(backupData).writeEntityData(unameBytes, unameBytes.length);
byte[] uidBytes = ApiCompatibilityUtils.getBytesUtf8(mAccountInfo.getGaiaId());
verify(backupData)
.writeEntityHeader(
"AndroidDefault." + ChromeBackupAgentImpl.SIGNED_IN_ACCOUNT_ID_KEY,
uidBytes.length);
verify(backupData).writeEntityData(uidBytes, uidBytes.length);
verify(backupData, times(0))
.writeEntityHeader(eq("AndroidDefault." + SHARED_PREF_NOT_BACKED_UP), anyInt());
// Check that the state was saved correctly.
try (ObjectInputStream newStateStream =
new ObjectInputStream(new FileInputStream(stateFile))) {
ArrayList<String> names = (ArrayList<String>) newStateStream.readObject();
assertThat(names.size(), equalTo(BACKUP_PREF_COUNT));
assertThat(names, hasItem("native." + SyncPrefNames.SYNC_PASSWORDS));
assertThat(
names, hasItem("NativeJsonDict." + SyncPrefNames.SELECTED_TYPES_PER_ACCOUNT));
assertThat(
names,
hasItem("AndroidDefault." + ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE));
assertThat(
names,
hasItem(
"AndroidDefault."
+ ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED));
assertThat(
names,
hasItem(
"AndroidDefault."
+ ChromePreferenceKeys.FIRST_RUN_LIGHTWEIGHT_FLOW_COMPLETE));
assertThat(
names,
hasItem(
"AndroidDefault."
+ ChromePreferenceKeys
.PRIVACY_METRICS_REPORTING_PERMITTED_BY_USER));
assertThat(
names, hasItem("AndroidDefault." + ChromeBackupAgentImpl.SYNCING_ACCOUNT_KEY));
assertThat(
names,
hasItem("AndroidDefault." + ChromeBackupAgentImpl.SIGNED_IN_ACCOUNT_ID_KEY));
ArrayList<byte[]> values = (ArrayList<byte[]>) newStateStream.readObject();
assertThat(values.size(), equalTo(BACKUP_PREF_COUNT));
assertThat(values, hasItem(unameBytes));
assertThat(values, hasItem(uidBytes));
assertThat(values, hasItem(accountSettingsPrefBytes));
assertThat(values, hasItem(new byte[] {0}));
assertThat(values, hasItem(new byte[] {1}));
// Make sure that there are no extra objects.
assertThat(newStateStream.available(), equalTo(0));
}
}
/**
* Test method for {@link ChromeBackupAgent#onBackup} testing first backup with a signed-in only
* user.
*/
@Test
public void testOnBackup_firstBackup_signedInNotSyncing()
throws IOException, ClassNotFoundException {
// Mock the backup data.
BackupDataOutput backupData = mock(BackupDataOutput.class);
// Set up some preferences to back up.
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
setUpPrefsToBackup(prefs);
doReturn(null).when(mIdentityManagerMock).getPrimaryAccountInfo(ConsentLevel.SYNC);
File stateFile = mTempDir.newFile();
try (ParcelFileDescriptor newState =
ParcelFileDescriptor.open(stateFile, ParcelFileDescriptor.MODE_WRITE_ONLY)) {
// Run the test function.
mAgent.onBackup(null, backupData, newState);
}
// Check that the right things were written to the backup
verify(backupData).writeEntityHeader("native." + SyncPrefNames.SYNC_PASSWORDS, 1);
byte[] accountSettingsPrefBytes =
ApiCompatibilityUtils.getBytesUtf8(ACCOUNT_SETTINGS_PREF_VALUE);
verify(backupData)
.writeEntityHeader(
"NativeJsonDict." + SyncPrefNames.SELECTED_TYPES_PER_ACCOUNT,
accountSettingsPrefBytes.length);
verify(backupData)
.writeEntityHeader(
"AndroidDefault." + ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE, 1);
verify(backupData)
.writeEntityHeader(
"AndroidDefault." + ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED, 1);
verify(backupData)
.writeEntityHeader(
"AndroidDefault."
+ ChromePreferenceKeys.FIRST_RUN_LIGHTWEIGHT_FLOW_COMPLETE,
1);
verify(backupData)
.writeEntityHeader(
"AndroidDefault."
+ ChromePreferenceKeys.PRIVACY_METRICS_REPORTING_PERMITTED_BY_USER,
1);
verify(backupData, times(DEFAULT_TRUE_BOOL_PREF_COUNT)).writeEntityData(new byte[] {1}, 1);
verify(backupData, times(BACKUP_BOOL_PREF_COUNT - DEFAULT_TRUE_BOOL_PREF_COUNT))
.writeEntityData(new byte[] {0}, 1);
byte[] unameBytes = ApiCompatibilityUtils.getBytesUtf8(mAccountInfo.getEmail());
verify(backupData, times(0))
.writeEntityHeader(
"AndroidDefault." + ChromeBackupAgentImpl.SYNCING_ACCOUNT_KEY,
unameBytes.length);
byte[] uidBytes = ApiCompatibilityUtils.getBytesUtf8(mAccountInfo.getGaiaId());
verify(backupData)
.writeEntityHeader(
"AndroidDefault." + ChromeBackupAgentImpl.SIGNED_IN_ACCOUNT_ID_KEY,
uidBytes.length);
verify(backupData).writeEntityData(uidBytes, uidBytes.length);
verify(backupData, times(0))
.writeEntityHeader(eq("AndroidDefault." + SHARED_PREF_NOT_BACKED_UP), anyInt());
// Check that the state was saved correctly
try (ObjectInputStream newStateStream =
new ObjectInputStream(new FileInputStream(stateFile))) {
ArrayList<String> names = (ArrayList<String>) newStateStream.readObject();
assertThat(names.size(), equalTo(BACKUP_PREF_COUNT));
assertThat(names, hasItem("native." + SyncPrefNames.SYNC_PASSWORDS));
assertThat(
names, hasItem("NativeJsonDict." + SyncPrefNames.SELECTED_TYPES_PER_ACCOUNT));
assertThat(
names,
hasItem("AndroidDefault." + ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE));
assertThat(
names,
hasItem(
"AndroidDefault."
+ ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED));
assertThat(
names,
hasItem(
"AndroidDefault."
+ ChromePreferenceKeys.FIRST_RUN_LIGHTWEIGHT_FLOW_COMPLETE));
assertThat(
names,
hasItem(
"AndroidDefault."
+ ChromePreferenceKeys
.PRIVACY_METRICS_REPORTING_PERMITTED_BY_USER));
assertThat(
names, hasItem("AndroidDefault." + ChromeBackupAgentImpl.SYNCING_ACCOUNT_KEY));
assertThat(
names,
hasItem("AndroidDefault." + ChromeBackupAgentImpl.SIGNED_IN_ACCOUNT_ID_KEY));
ArrayList<byte[]> values = (ArrayList<byte[]>) newStateStream.readObject();
assertThat(values.size(), equalTo(BACKUP_PREF_COUNT));
assertThat(values, not(hasItem(unameBytes)));
assertThat(values, hasItem(uidBytes));
assertThat(values, hasItem(accountSettingsPrefBytes));
assertThat(values, hasItem(new byte[] {0}));
assertThat(values, hasItem(new byte[] {1}));
// Make sure that there are no extra objects.
assertThat(newStateStream.available(), equalTo(0));
}
}
/** Test method for {@link ChromeBackupAgent#onBackup} a second backup with the same data */
@Test
@SuppressWarnings("unchecked")
public void testOnBackup_duplicateBackup()
throws FileNotFoundException, IOException, ClassNotFoundException {
// Mock the backup data.
BackupDataOutput backupData = mock(BackupDataOutput.class);
// Set up some preferences to back up.
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
setUpPrefsToBackup(prefs);
File stateFile1 = mTempDir.newFile();
try (ParcelFileDescriptor newState =
ParcelFileDescriptor.open(stateFile1, ParcelFileDescriptor.MODE_WRITE_ONLY)) {
// Do a first backup.
mAgent.onBackup(null, backupData, newState);
}
// Minimal check on first backup, this isn't the test here.
verify(backupData, times(BACKUP_PREF_COUNT)).writeEntityHeader(anyString(), anyInt());
verify(backupData, times(BACKUP_PREF_COUNT)).writeEntityData(any(byte[].class), anyInt());
File stateFile2 = mTempDir.newFile();
try (ParcelFileDescriptor oldState =
ParcelFileDescriptor.open(stateFile1, ParcelFileDescriptor.MODE_READ_ONLY);
ParcelFileDescriptor newState =
ParcelFileDescriptor.open(
stateFile2, ParcelFileDescriptor.MODE_WRITE_ONLY)) {
// Try a second backup without changing any data
mAgent.onBackup(oldState, backupData, newState);
}
// Check that the second backup didn't write anything.
verifyNoMoreInteractions(backupData);
// The two state files should contain identical data.
try (ObjectInputStream oldStateStream =
new ObjectInputStream(new FileInputStream(stateFile1));
ObjectInputStream newStateStream =
new ObjectInputStream(new FileInputStream(stateFile2))) {
ArrayList<String> oldNames = (ArrayList<String>) oldStateStream.readObject();
ArrayList<byte[]> oldValues = (ArrayList<byte[]>) oldStateStream.readObject();
ArrayList<String> newNames = (ArrayList<String>) newStateStream.readObject();
ArrayList<byte[]> newValues = (ArrayList<byte[]>) newStateStream.readObject();
assertThat(newNames, equalTo(oldNames));
assertTrue(Arrays.deepEquals(newValues.toArray(), oldValues.toArray()));
assertThat(newStateStream.available(), equalTo(0));
}
}
/** Test method for {@link ChromeBackupAgent#onBackup} a second backup with different data */
@Test
@SuppressWarnings("unchecked")
public void testOnBackup_dataChanged()
throws FileNotFoundException, IOException, ClassNotFoundException {
// Mock the backup data.
BackupDataOutput backupData = mock(BackupDataOutput.class);
// Set up some preferences to back up.
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
setUpPrefsToBackup(prefs);
// Create a state file.
File stateFile1 = mTempDir.newFile();
try (ParcelFileDescriptor newState =
ParcelFileDescriptor.open(stateFile1, ParcelFileDescriptor.MODE_WRITE_ONLY)) {
// Do a first backup.
mAgent.onBackup(null, backupData, newState);
}
// Minimal check on first backup, this isn't the test here.
verify(backupData, times(BACKUP_PREF_COUNT)).writeEntityHeader(anyString(), anyInt());
verify(backupData, times(BACKUP_PREF_COUNT)).writeEntityData(any(byte[].class), anyInt());
// Change some data.
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(ChromePreferenceKeys.PRIVACY_METRICS_REPORTING_PERMITTED_BY_USER, true);
editor.apply();
reset(backupData);
File stateFile2 = mTempDir.newFile();
try (ParcelFileDescriptor oldState =
ParcelFileDescriptor.open(
stateFile1, ParcelFileDescriptor.MODE_WRITE_ONLY);
ParcelFileDescriptor newState =
ParcelFileDescriptor.open(
stateFile2, ParcelFileDescriptor.MODE_WRITE_ONLY)) {
// Do a second backup.
mAgent.onBackup(oldState, backupData, newState);
}
// Check that the second backup wrote something.
verify(backupData, times(BACKUP_PREF_COUNT)).writeEntityHeader(anyString(), anyInt());
verify(backupData, times(BACKUP_PREF_COUNT)).writeEntityData(any(byte[].class), anyInt());
// the two state files should contain different data (although the names are unchanged).
try (ObjectInputStream oldStateStream =
new ObjectInputStream(new FileInputStream(stateFile1));
ObjectInputStream newStateStream =
new ObjectInputStream(new FileInputStream(stateFile2))) {
ArrayList<String> oldNames = (ArrayList<String>) oldStateStream.readObject();
ArrayList<byte[]> oldValues = (ArrayList<byte[]>) oldStateStream.readObject();
ArrayList<String> newNames = (ArrayList<String>) newStateStream.readObject();
ArrayList<byte[]> newValues = (ArrayList<byte[]>) newStateStream.readObject();
assertThat(newNames, equalTo(oldNames));
assertFalse(Arrays.deepEquals(newValues.toArray(), oldValues.toArray()));
assertThat(newStateStream.available(), equalTo(0));
}
}
/** Test method for {@link ChromeBackupAgent#onBackup} when browser startup fails */
@Test
public void testOnBackup_browserStartupFails() throws IOException {
BackupDataOutput backupData = mock(BackupDataOutput.class);
ParcelFileDescriptor mockState = mock(ParcelFileDescriptor.class);
doReturn(false).when(mAgent).initializeBrowser();
BackupManagerShadow.clearDataChangedCalls();
mAgent.onBackup(null, backupData, mockState);
assertThat(BackupManagerShadow.getDataChangedCalls(), equalTo(1));
verifyNoMoreInteractions(backupData);
verifyNoMoreInteractions(mockState);
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
assertThat(prefs.getInt(ChromeBackupAgentImpl.BACKUP_FAILURE_COUNT, 0), equalTo(1));
// Check that the backup agent gives up retrying after too many failures
prefs.edit()
.putInt(
ChromeBackupAgentImpl.BACKUP_FAILURE_COUNT,
ChromeBackupAgentImpl.MAX_BACKUP_FAILURES)
.apply();
mAgent.onBackup(null, backupData, mockState);
assertThat(BackupManagerShadow.getDataChangedCalls(), equalTo(1));
// Check that a successful backup resets the failure count
doReturn(true).when(mAgent).initializeBrowser();
// Set up some preferences to back up.
setUpPrefsToBackup(prefs);
// A successful backup needs a real state file, or lots more mocking.
try (ParcelFileDescriptor newState =
ParcelFileDescriptor.open(
mTempDir.newFile(), ParcelFileDescriptor.MODE_WRITE_ONLY)) {
mAgent.onBackup(null, backupData, newState);
}
assertThat(prefs.getInt(ChromeBackupAgentImpl.BACKUP_FAILURE_COUNT, 0), equalTo(0));
verify(backupData, times(DEFAULT_TRUE_BOOL_PREF_COUNT)).writeEntityData(new byte[] {1}, 1);
}
private BackupDataInput createMockBackupData(
boolean hasSyncingUser, boolean hasSignedInUser, boolean hasAccountSettings)
throws IOException {
// Mock the backup data
BackupDataInput backupData = mock(BackupDataInput.class);
String syncingUserEmail = hasSyncingUser ? mAccountInfo.getEmail() : "";
String signedInUserGaiaId = hasSignedInUser ? mAccountInfo.getGaiaId() : "";
ArrayList<Pair<String, byte[]>> keysAndValues =
new ArrayList(
Arrays.asList(
new Pair<>("native." + NATIVE_PREF_NOT_BACKED_UP, new byte[] {1}),
new Pair<>(
"AndroidDefault."
+ ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE,
new byte[] {1}),
new Pair<>("AndroidDefault.junk", new byte[] {23, 42}),
new Pair<>(
"AndroidDefault."
+ ChromeBackupAgentImpl.SYNCING_ACCOUNT_KEY,
ApiCompatibilityUtils.getBytesUtf8(syncingUserEmail)),
new Pair<>(
"AndroidDefault."
+ ChromeBackupAgentImpl.SIGNED_IN_ACCOUNT_ID_KEY,
ApiCompatibilityUtils.getBytesUtf8(signedInUserGaiaId))));
for (Map.Entry<String, Boolean> entry : mNativeBoolPrefBackupValues.entrySet()) {
byte[] value = entry.getValue() ? new byte[] {1} : new byte[] {0};
keysAndValues.add(new Pair<>("native." + entry.getKey(), value));
}
if (hasAccountSettings) {
keysAndValues.add(
new Pair<>(
"NativeJsonDict." + SyncPrefNames.SELECTED_TYPES_PER_ACCOUNT,
ApiCompatibilityUtils.getBytesUtf8(ACCOUNT_SETTINGS_PREF_VALUE)));
}
when(backupData.getKey())
.thenAnswer(
new Answer<String>() {
private int mPos;
@Override
public String answer(InvocationOnMock invocation) {
return keysAndValues.get(mPos++).first;
}
});
when(backupData.getDataSize())
.thenAnswer(
new Answer<Integer>() {
private int mPos;
@Override
public Integer answer(InvocationOnMock invocation) {
return keysAndValues.get(mPos++).second.length;
}
});
when(backupData.readEntityData(any(byte[].class), anyInt(), anyInt()))
.thenAnswer(
new Answer<Integer>() {
private int mPos;
@Override
public Integer answer(InvocationOnMock invocation) {
byte[] buffer = invocation.getArgument(0);
for (int i = 0; i < keysAndValues.get(mPos).second.length; i++) {
buffer[i] = keysAndValues.get(mPos).second[i];
}
return keysAndValues.get(mPos++).second.length;
}
});
when(backupData.readNextHeader())
.thenAnswer(
new Answer<Boolean>() {
private int mPos;
@Override
public Boolean answer(InvocationOnMock invocation) {
return mPos++ < keysAndValues.size();
}
});
return backupData;
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains the previously
* syncing user only.
*/
@Test
@DisableFeatures({
SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP,
ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS
})
public void testOnRestore_withSyncUser_signInRestoreDisabled_replaceSyncBySigninDisabled()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ true,
/* withSignedInUser= */ false,
/* withAccountSettings= */ true);
verify(mTaskRunner)
.startBackgroundTasks(
/* allocateChildConnection= */ false, /* initVariationSeed= */ true);
verifyRestoreFinishWithSigninAndSync();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(false);
// Verify that bool prefs are not migrated to account settings, since flags are disabled.
verifyBoolPrefsMigratedToAccountSettings(false);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains the previously
* syncing user only.
*/
@Test
@DisableFeatures({SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP})
@EnableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
public void testOnRestore_withSyncUser_signInRestoreDisabled_syncToSigninEnabled()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ true,
/* withSignedInUser= */ false,
/* withAccountSettings= */ true);
verifyRestoreFinishWithSignin();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(false);
// Verify that bool prefs are not migrated to account settings, since account settings
// restore flag is disabled.
verifyBoolPrefsMigratedToAccountSettings(false);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. the backup contains the previously
* signed-in user only.
*/
@Test
@EnableFeatures({SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP})
@DisableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
public void testOnRestore_withSignInUser_signInRestoreEnabled_replaceSyncBySigninDisabled()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ false,
/* withSignedInUser= */ true,
/* withAccountSettings= */ true);
verifyRestoreFinishWithSignin();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(true);
// Verify that bool prefs are not migrated to account settings, since the UNO flag is
// disabled.
verifyBoolPrefsMigratedToAccountSettings(false);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. the backup contains the previously
* signed-in user only, and does not contain account settings backup.
*/
@Test
@EnableFeatures({SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP})
@DisableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
public void
testOnRestore_withSignInUser_signInRestoreEnabled_replaceSyncBySigninDisabled_noAccountSettings()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ false,
/* withSignedInUser= */ true,
/* withAccountSettings= */ false);
verifyRestoreFinishWithSignin();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(false);
// Verify that bool prefs are not migrated to account settings, since the UNO flag is
// disabled.
verifyBoolPrefsMigratedToAccountSettings(false);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains the previously
* signed-in user only.
*/
@Test
@EnableFeatures({
SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP,
ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS
})
public void testOnRestore_withSignInUser_signInRestoreEnabled_syncToSigninEnabled()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ false,
/* withSignedInUser= */ true,
/* withAccountSettings= */ true);
verifyRestoreFinishWithSignin();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(true);
// Verify that bool prefs are not migrated to account settings, since the backed-up user
// is not previously syncing, and there's an existing account settings backup, even if the
// flags are enabled.
verifyBoolPrefsMigratedToAccountSettings(false);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains the previously
* signed-in user only, and does not contain account settings backup.
*/
@Test
@EnableFeatures({
SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP,
ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS
})
public void
testOnRestore_withSignInUser_signInRestoreEnabled_syncToSigninEnabled_noAccountSettings()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ false,
/* withSignedInUser= */ true,
/* withAccountSettings= */ false);
verifyRestoreFinishWithSignin();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(false);
// Verify that bool prefs are migrated to account settings, since the backed-up user
// is not previously syncing, but there's no existing account settings backup.
verifyBoolPrefsMigratedToAccountSettings(true);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains the previously
* signed-in user only.
*/
@Test
@EnableFeatures({
SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP,
ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS
})
public void testOnRestore_withSignInUser() throws IOException {
mIsAccountManaged = true;
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ false,
/* withSignedInUser= */ true,
/* withAccountSettings= */ true);
verifyRestoreFinishWithSignin();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(true);
// Verify that bool prefs are not migrated to account settings, since the backed-up user
// is not previously syncing, and there's an existing account settings backup.
verifyBoolPrefsMigratedToAccountSettings(false);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains the previously
* signed-in user only.
*/
@Test
@EnableFeatures({
SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP,
ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS,
})
public void testOnRestore_withSignInUser_notManaged() throws IOException {
mIsAccountManaged = false;
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ false,
/* withSignedInUser= */ true,
/* withAccountSettings= */ true);
verifyRestoreFinishWithSignin();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(true);
// Verify that bool prefs are not migrated to account settings, since the backed-up user
// is not previously syncing, and there's an existing account settings backup.
verifyBoolPrefsMigratedToAccountSettings(false);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains a record for the
* previously signed-in user and another for the syncing user.
*/
@Test
@EnableFeatures({SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP})
@DisableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
public void
testOnRestore_withSignInAndSyncUser_signInRestoreEnabled_replaceSyncBySigninDisabled()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ true,
/* withSignedInUser= */ true,
/* withAccountSettings= */ true);
verifyRestoreFinishWithSigninAndSync();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(true);
// Verify that bool prefs are not migrated to account settings, since we are not converting
// previously syncing user to sign-in.
verifyBoolPrefsMigratedToAccountSettings(false);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains a record for the
* previously signed-in user and another for the syncing user.
*/
@Test
@EnableFeatures({
SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP,
ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS
})
public void testOnRestore_withSignInAndSyncUser_signInRestoreEnabled_syncToSigninEnabled()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ true,
/* withSignedInUser= */ true,
/* withAccountSettings= */ true);
// Verify sign-in restoration.
verifyRestoreFinishWithSignin();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(true);
// Verify that global prefs are migrated to account settings, given that flags are enabled,
// and the backed-up account was a syncing one.
verifyBoolPrefsMigratedToAccountSettings(true);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains a record for the
* previously signed-in user and another for the syncing user, and no account settings.
*/
@Test
@EnableFeatures({
SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP,
ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS
})
public void
testOnRestore_withSignInAndSyncUser_signInRestoreEnabled_syncToSigninEnabled_noAccountSettings()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ true,
/* withSignedInUser= */ true,
/* withAccountSettings= */ false);
// Verify sign-in restoration.
verifyRestoreFinishWithSignin();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(false);
// Verify that global prefs are migrated to account settings, given that flags are enabled,
// and the backed-up account was a syncing one.
verifyBoolPrefsMigratedToAccountSettings(true);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains a record for the
* previously signed-in user and another for the syncing user.
*/
@Test
@DisableFeatures({
SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP,
ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS
})
public void
testOnRestore_withSignInAndSyncUser_signInRestoreDisabled_replaceSyncBySigninDisabled()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ true,
/* withSignedInUser= */ true,
/* withAccountSettings= */ true);
verifyRestoreFinishWithSigninAndSync();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(false);
// Verify that bool prefs are not migrated to account settings since flags are disabled.
verifyBoolPrefsMigratedToAccountSettings(false);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains a record for the
* previously signed-in user and another for the syncing user.
*/
@Test
@DisableFeatures({SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP})
@EnableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
public void testOnRestore_withSignInAndSyncUser_signInRestoreDisabled_syncToSigninEnabled()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ true,
/* withSignedInUser= */ true,
/* withAccountSettings= */ true);
verifyRestoreFinishWithSignin();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(false);
// Verify that bool prefs are not migrated to account settings, since account settings
// restore flag is disabled.
verifyBoolPrefsMigratedToAccountSettings(false);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains a record for the
* previously syncing user only.
*/
@Test
@EnableFeatures({SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP})
@DisableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
public void testOnRestore_withSyncUser_signInRestoreEnabled_replaceSyncBySigninDisabled()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ true,
/* withSignedInUser= */ false,
/* withAccountSettings= */ true);
verifyRestoreFinishWithSigninAndSync();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(true);
// Verify that bool prefs are not migrated to account settings, since syncing user is not
// converted to sign-in only mode.
verifyBoolPrefsMigratedToAccountSettings(false);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains a record for the
* previously syncing user, and a record for account settings.
*/
@Test
@EnableFeatures({
SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP,
ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS
})
public void
testOnRestore_withSyncUserAndAccountSettings_signInRestoreEnabled_syncToSigninEnabled()
throws IOException {
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ true,
/* withSignedInUser= */ false,
/* withAccountSettings= */ true);
verifyRestoreFinishWithSignin();
verifySyncTypeBoolPrefsRestored(true);
InOrder inOrder = inOrder(mChromeBackupAgentJniMock, mPrefService);
inOrder.verify(mPrefService, times(mNativeBoolPrefBackupValues.size()))
.setBoolean(anyString(), anyBoolean());
inOrder.verify(mChromeBackupAgentJniMock, times(1))
.setDict(
mPrefService,
SyncPrefNames.SELECTED_TYPES_PER_ACCOUNT,
ACCOUNT_SETTINGS_PREF_VALUE);
// Verify that global prefs are migrated to account settings, after the account setting
// backup is restored.
// The migration is done since the corresponding flags are enabled, and the backed-up
// account was a syncing one.
inOrder.verify(mChromeBackupAgentJniMock, times(1)).commitPendingPrefWrites(mPrefService);
inOrder.verifyNoMoreInteractions();
}
/**
* Test method for {@link ChromeBackupAgent#onRestore}. The backup contains a record for the
* previously syncing user only, and the backup value for SYNC_KEEP_EVERYTHING_SYNCED is true.
*/
@Test
@EnableFeatures({
SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP,
ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS
})
public void testOnRestore_withSyncUser_syncEverything_signInRestoreEnabled_syncToSigninEnabled()
throws IOException {
mNativeBoolPrefBackupValues.put(SyncPrefNames.SYNC_KEEP_EVERYTHING_SYNCED, true);
executeNormalRestoreAndCheckPrefs(
/* withSyncingUser= */ true,
/* withSignedInUser= */ false,
/* withAccountSettings= */ true);
verifyRestoreFinishWithSignin();
verifySyncTypeBoolPrefsRestored(true);
verifyAccountSettingsBackupRestored(true);
// Verify that global prefs are migrated to account settings, given that flags are enabled,
// and the backed-up account was a syncing one.
verifyBoolPrefsMigratedToAccountSettings(true);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore} with valid signed-in user but no syncing
* user. With signed-in user restore disabled, the restore should fail since no valid syncing
* user was recorded.
*/
@Test
@DisableFeatures({
ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS,
SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP
})
public void testOnRestore_failure_signinUserOnly_signedInRestoreDisabled() throws IOException {
BackupDataInput backupData =
createMockBackupData(
/* hasSyncingUser= */ false,
/* hasSignedInUser= */ true,
/* hasAccountSettings= */ true);
mAccountManagerTestRule.addAccount(mAccountInfo.getEmail());
try (ParcelFileDescriptor newState =
ParcelFileDescriptor.open(
mTempDir.newFile(), ParcelFileDescriptor.MODE_WRITE_ONLY)) {
// Do a restore.
mAgent.onRestore(backupData, 0, newState);
}
// Verify that the restore is not done since no valid account can be signed-in.
// The sign-in account was recorded, but the sign-in account restore flag is disabled. Given
// that no syncing account has been recorded, no account can be restored here so the restore
// should be skipped.
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
assertFalse(prefs.contains(ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE));
verify(mPrefService, never()).setBoolean(any(), anyBoolean());
verify(mTaskRunner)
.startBackgroundTasks(
/* allocateChildConnection= */ false, /* initVariationSeed= */ true);
// Verify that no sign-in or prefs restoration is done.
verifyRestoreFinishWithoutSignin();
verifySyncTypeBoolPrefsRestored(false);
verifyAccountSettingsBackupRestored(false);
verifyBoolPrefsMigratedToAccountSettings(false);
}
/**
* Test method for {@link ChromeBackupAgent#onRestore} for a user that doesn't exist on the
* device. Since the recorded signed-in account is not present on the device and can't be
* signed-in, the restore should be skipped.
*/
@Test
@EnableFeatures({SigninFeatures.RESTORE_SIGNED_IN_ACCOUNT_AND_SETTINGS_FROM_BACKUP})
@DisableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
public void testOnRestore_badUser_signedInRestoreEnabled() throws IOException {
BackupDataInput backupData =
createMockBackupData(
/* hasSyncingUser= */ true,
/* hasSignedInUser= */ true,
/* hasAccountSettings= */ true);
try (ParcelFileDescriptor newState =
ParcelFileDescriptor.open(
mTempDir.newFile(), ParcelFileDescriptor.MODE_WRITE_ONLY)) {
// Do a restore.
mAgent.onRestore(backupData, 0, newState);
}
// Verify that the restore is not done since no valid account can be signed-in.
// The signed-in & syncing account is recorded in the backup, but is not present on the
// device, so the sign-in can't be done.
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
assertFalse(prefs.contains(ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE));
verify(mPrefService, never()).setBoolean(any(), anyBoolean());
verify(mTaskRunner)
.startBackgroundTasks(
/* allocateChildConnection= */ false, /* initVariationSeed= */ true);
// Verify that no sign-in or prefs restoration is done.
verifyRestoreFinishWithoutSignin();
verifySyncTypeBoolPrefsRestored(false);
verifyAccountSettingsBackupRestored(false);
verifyBoolPrefsMigratedToAccountSettings(false);
}
/** Test method for {@link ChromeBackupAgent#onRestore} for browser startup failure */
@Test
public void testOnRestore_browserStartupFails() throws IOException {
BackupDataInput backupData =
createMockBackupData(
/* hasSyncingUser= */ true,
/* hasSignedInUser= */ true,
/* hasAccountSettings= */ true);
doReturn(false).when(mAgent).initializeBrowser();
try (ParcelFileDescriptor newState =
ParcelFileDescriptor.open(
mTempDir.newFile(), ParcelFileDescriptor.MODE_WRITE_ONLY)) {
// Do a restore.
mAgent.onRestore(backupData, 0, newState);
}
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
assertFalse(prefs.contains(ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE));
// Test that the status of the restore has been recorded.
assertThat(
ChromeBackupAgentImpl.getRestoreStatus(),
equalTo(ChromeBackupAgentImpl.RestoreStatus.BROWSER_STARTUP_FAILED));
}
/** Test method for {@link ChromeBackupAgent#onRestore} for browser startup failure */
@Test
public void testOnRestore_afterFirstRun() throws IOException {
BackupDataInput backupData =
createMockBackupData(
/* hasSyncingUser= */ true,
/* hasSignedInUser= */ true,
/* hasAccountSettings= */ true);
FirstRunStatus.setFirstRunFlowComplete(true);
try (ParcelFileDescriptor newState =
ParcelFileDescriptor.open(
mTempDir.newFile(), ParcelFileDescriptor.MODE_WRITE_ONLY)) {
// Do a restore.
mAgent.onRestore(backupData, 0, newState);
}
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
assertTrue(prefs.contains(ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE));
// Test that the status of the restore has been recorded.
assertThat(
ChromeBackupAgentImpl.getRestoreStatus(),
equalTo(ChromeBackupAgentImpl.RestoreStatus.RESTORE_AFTER_FIRST_RUN));
}
/** Test of {@link ChromeBackupAgent#getRestoreStatus} */
@Test
public void testGetRestoreStatus() {
// Test default value
assertThat(
ChromeBackupAgentImpl.getRestoreStatus(),
equalTo(ChromeBackupAgentImpl.RestoreStatus.NO_RESTORE));
// Test that the value can be changed
ChromeBackupAgentImpl.setRestoreStatus(
ChromeBackupAgentImpl.RestoreStatus.RESTORE_AFTER_FIRST_RUN);
assertThat(
ChromeBackupAgentImpl.getRestoreStatus(),
equalTo(ChromeBackupAgentImpl.RestoreStatus.RESTORE_AFTER_FIRST_RUN));
// Prove that the value equalTo held in the app preferences (and not, for example, in a
// static).
ContextUtils.getAppSharedPreferences().edit().clear().apply();
assertThat(
ChromeBackupAgentImpl.getRestoreStatus(),
equalTo(ChromeBackupAgentImpl.RestoreStatus.NO_RESTORE));
// Test that ChromeBackupAgentImpl.setRestoreStatus really looks at the argument.
ChromeBackupAgentImpl.setRestoreStatus(
ChromeBackupAgentImpl.RestoreStatus.BROWSER_STARTUP_FAILED);
assertThat(
ChromeBackupAgentImpl.getRestoreStatus(),
equalTo(ChromeBackupAgentImpl.RestoreStatus.BROWSER_STARTUP_FAILED));
// Test the remaining values are implemented
ChromeBackupAgentImpl.setRestoreStatus(ChromeBackupAgentImpl.RestoreStatus.NOT_SIGNED_IN);
assertThat(
ChromeBackupAgentImpl.getRestoreStatus(),
equalTo(ChromeBackupAgentImpl.RestoreStatus.NOT_SIGNED_IN));
ChromeBackupAgentImpl.setRestoreStatus(
ChromeBackupAgentImpl.RestoreStatus.RESTORE_COMPLETED);
assertThat(
ChromeBackupAgentImpl.getRestoreStatus(),
equalTo(ChromeBackupAgentImpl.RestoreStatus.RESTORE_COMPLETED));
ChromeBackupAgentImpl.setRestoreStatus(
ChromeBackupAgentImpl.RestoreStatus.SIGNIN_TIMED_OUT);
assertThat(
ChromeBackupAgentImpl.getRestoreStatus(),
equalTo(ChromeBackupAgentImpl.RestoreStatus.SIGNIN_TIMED_OUT));
}
/**
* Test normal browser startup. This is not tested by the other tests, since, until recently,
* it was not possible to mock ChromeBrowserInitializer, so initializeBrowser is mocked.
*
* TODO (aberent) remove mocking of initializeBrowser in the other tests.
*/
@Test
public void testInitializeBrowser_normal() {
ChromeBackupAgentImpl agent = new ChromeBackupAgentImpl();
ChromeBrowserInitializer initializer = mock(ChromeBrowserInitializer.class);
ChromeBrowserInitializer.setForTesting(initializer);
assertTrue(agent.initializeBrowser());
}
/**
* Test that browser startup fails when in a child process. This is important because of
* https://crbug.com/718166
*/
@Test
public void testInitializeBrowser_childProcess() {
ContentProcessInfo.setInChildProcess(true);
ChromeBackupAgentImpl agent = new ChromeBackupAgentImpl();
ChromeBrowserInitializer initializer = mock(ChromeBrowserInitializer.class);
ChromeBrowserInitializer.setForTesting(initializer);
assertFalse(agent.initializeBrowser());
verifyNoMoreInteractions(initializer);
}
private void executeNormalRestoreAndCheckPrefs(
boolean withSyncingUser, boolean withSignedInUser, boolean withAccountSettings)
throws IOException {
BackupDataInput backupData =
createMockBackupData(
/* hasSyncingUser= */ withSyncingUser,
/* hasSignedInUser= */ withSignedInUser,
/* hasAccountSettings= */ withAccountSettings);
mAccountManagerTestRule.addAccount(mAccountInfo.getEmail());
try (ParcelFileDescriptor newState =
ParcelFileDescriptor.open(
mTempDir.newFile(), ParcelFileDescriptor.MODE_WRITE_ONLY)) {
mAgent.onRestore(backupData, 0, newState);
}
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
assertTrue(prefs.getBoolean(ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE, false));
assertFalse(prefs.contains("junk"));
assertFalse(prefs.contains(ChromeBackupAgentImpl.SIGNED_IN_ACCOUNT_ID_KEY));
assertFalse(prefs.contains(SyncPrefNames.SELECTED_TYPES_PER_ACCOUNT));
verify(mPrefService, never()).setBoolean(eq(NATIVE_PREF_NOT_BACKED_UP), anyBoolean());
}
private void verifyRestoreFinishWithSignin() {
// Verify that the restore is marked as completed.
assertThat(
ChromeBackupAgentImpl.getRestoreStatus(),
equalTo(ChromeBackupAgentImpl.RestoreStatus.RESTORE_COMPLETED));
// Verify that the account is not recorded to trigger the sign-in & sync flow later.
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
assertFalse(prefs.contains(ChromePreferenceKeys.BACKUP_FLOW_SIGNIN_ACCOUNT_NAME));
// Verify that sign-in without sync is triggered for the given account.
verify(mSigninManager, timeout(CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL))
.signin(eq(mAccountInfo), anyInt(), any());
if (mIsAccountManaged) {
verify(mSigninManager).setUserAcceptedAccountManagement(true);
} else {
verify(mSigninManager, never()).setUserAcceptedAccountManagement(anyBoolean());
}
}
private void verifySyncTypeBoolPrefsRestored(boolean isRestored) {
for (Map.Entry<String, Boolean> entry : mNativeBoolPrefBackupValues.entrySet()) {
if (isRestored) {
verify(mPrefService, times(1)).setBoolean(entry.getKey(), entry.getValue());
} else {
verify(mPrefService, never()).setBoolean(eq(entry.getKey()), anyBoolean());
}
}
}
private void verifyAccountSettingsBackupRestored(boolean isRestored) {
if (isRestored) {
verify(mChromeBackupAgentJniMock, times(1))
.setDict(
mPrefService,
SyncPrefNames.SELECTED_TYPES_PER_ACCOUNT,
ACCOUNT_SETTINGS_PREF_VALUE);
} else {
verify(mChromeBackupAgentJniMock, never())
.setDict(any(), eq(SyncPrefNames.SELECTED_TYPES_PER_ACCOUNT), anyString());
}
}
private void verifyBoolPrefsMigratedToAccountSettings(boolean isMigrated) {
if (isMigrated) {
verify(mChromeBackupAgentJniMock, times(1))
.migrateGlobalDataTypePrefsToAccount(mPrefService, mAccountInfo.getGaiaId());
} else {
verify(mChromeBackupAgentJniMock, never())
.migrateGlobalDataTypePrefsToAccount(any(), anyString());
}
}
private void verifyRestoreFinishWithSigninAndSync() {
// Verify that the restore is marked as completed.
assertThat(
ChromeBackupAgentImpl.getRestoreStatus(),
equalTo(ChromeBackupAgentImpl.RestoreStatus.RESTORE_COMPLETED));
// Verify that the account is recorded to trigger the sign-in & sync flow later.
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
assertTrue(prefs.contains(ChromePreferenceKeys.BACKUP_FLOW_SIGNIN_ACCOUNT_NAME));
assertThat(
prefs.getString(ChromePreferenceKeys.BACKUP_FLOW_SIGNIN_ACCOUNT_NAME, ""),
equalTo(mAccountInfo.getEmail()));
// Verify that the sign-in is not triggered immediately.
verify(mSigninManager, never()).signin(any(CoreAccountInfo.class), anyInt(), any());
}
private void verifyRestoreFinishWithoutSignin() {
// Verify that the status of the restore has been recorded.
assertThat(
ChromeBackupAgentImpl.getRestoreStatus(),
equalTo(ChromeBackupAgentImpl.RestoreStatus.NOT_SIGNED_IN));
// Verify that the sign-in is not triggered immediately.
verify(mSigninManager, never()).signin(any(CoreAccountInfo.class), anyInt(), any());
// Verify that the account is not recorded to trigger the sign-in & sync flow later.
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
assertFalse(prefs.contains(ChromePreferenceKeys.BACKUP_FLOW_SIGNIN_ACCOUNT_NAME));
}
}