blob: 38c4803e27035053fe2793832e59412540760aa8 [file] [log] [blame]
// Copyright 2018 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.signin;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.UserManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
import org.robolectric.annotation.LooperMode;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowLooper;
import org.chromium.base.Callback;
import org.chromium.base.Promise;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Features;
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.bookmarks.BookmarkModel;
import org.chromium.chrome.browser.bookmarks.FakeBookmarkModel;
import org.chromium.chrome.browser.browsing_data.BrowsingDataBridge;
import org.chromium.chrome.browser.browsing_data.BrowsingDataBridgeJni;
import org.chromium.chrome.browser.browsing_data.BrowsingDataType;
import org.chromium.chrome.browser.browsing_data.TimePeriod;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.password_manager.PasswordManagerUtilBridge;
import org.chromium.chrome.browser.password_manager.PasswordManagerUtilBridgeJni;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.signin.services.SigninManager;
import org.chromium.chrome.browser.signin.services.SigninPreferencesManager;
import org.chromium.components.externalauth.ExternalAuthUtils;
import org.chromium.components.prefs.PrefService;
import org.chromium.components.signin.AccountManagerFacade;
import org.chromium.components.signin.AccountManagerFacadeProvider;
import org.chromium.components.signin.AccountUtils;
import org.chromium.components.signin.SigninFeatureMap;
import org.chromium.components.signin.SigninFeatures;
import org.chromium.components.signin.base.AccountInfo;
import org.chromium.components.signin.base.CoreAccountId;
import org.chromium.components.signin.base.CoreAccountInfo;
import org.chromium.components.signin.identitymanager.AccountInfoServiceProvider;
import org.chromium.components.signin.identitymanager.AccountTrackerService;
import org.chromium.components.signin.identitymanager.ConsentLevel;
import org.chromium.components.signin.identitymanager.IdentityManager;
import org.chromium.components.signin.identitymanager.IdentityManagerJni;
import org.chromium.components.signin.identitymanager.IdentityMutator;
import org.chromium.components.signin.identitymanager.PrimaryAccountChangeEvent;
import org.chromium.components.signin.identitymanager.PrimaryAccountError;
import org.chromium.components.signin.metrics.SigninAccessPoint;
import org.chromium.components.signin.metrics.SignoutReason;
import org.chromium.components.signin.test.util.FakeAccountManagerFacade;
import org.chromium.components.sync.SyncService;
import org.chromium.components.sync.UserSelectableType;
import org.chromium.components.user_prefs.UserPrefs;
import org.chromium.components.user_prefs.UserPrefsJni;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/** Tests for {@link SigninManagerImpl}. */
@RunWith(BaseRobolectricTestRunner.class)
@LooperMode(LooperMode.Mode.LEGACY)
@DisableFeatures(SigninFeatures.ENTERPRISE_POLICY_ON_SIGNIN)
public class SigninManagerImplTest {
private static final long NATIVE_SIGNIN_MANAGER = 10001L;
private static final long NATIVE_IDENTITY_MANAGER = 10002L;
private static final AccountInfo ACCOUNT_INFO =
new AccountInfo.Builder(
"user@domain.com", FakeAccountManagerFacade.toGaiaId("user@domain.com"))
.fullName("full name")
.givenName("given name")
.build();
@Rule public final TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
@Rule public final JniMocker mocker = new JniMocker();
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.LENIENT);
@Mock private SigninManagerImpl.Natives mNativeMock;
@Mock private IdentityManager.Natives mIdentityManagerNativeMock;
@Mock private BrowsingDataBridge.Natives mBrowsingDataBridgeNativeMock;
@Mock private PasswordManagerUtilBridge.Natives mPasswordManagerUtilBridgeNativeMock;
@Mock private UserPrefs.Natives mUserPrefsNativeMock;
@Mock private PrefService mPrefService;
@Mock private AccountTrackerService mAccountTrackerService;
@Mock private IdentityMutator mIdentityMutator;
@Mock private ExternalAuthUtils mExternalAuthUtils;
@Mock private SyncService mSyncService;
@Mock private Profile mProfile;
@Mock private SigninManager.SignInStateObserver mSignInStateObserver;
private final IdentityManager mIdentityManager =
IdentityManager.create(NATIVE_IDENTITY_MANAGER, null /* OAuth2TokenService */);
private final FakeAccountManagerFacade mFakeAccountManagerFacade =
new FakeAccountManagerFacade();
private SigninManagerImpl mSigninManager;
@Before
public void setUp() {
mocker.mock(SigninManagerImplJni.TEST_HOOKS, mNativeMock);
mocker.mock(IdentityManagerJni.TEST_HOOKS, mIdentityManagerNativeMock);
mocker.mock(BrowsingDataBridgeJni.TEST_HOOKS, mBrowsingDataBridgeNativeMock);
mocker.mock(PasswordManagerUtilBridgeJni.TEST_HOOKS, mPasswordManagerUtilBridgeNativeMock);
mocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsNativeMock);
when(mUserPrefsNativeMock.get(mProfile)).thenReturn(mPrefService);
ExternalAuthUtils.setInstanceForTesting(mExternalAuthUtils);
BookmarkModel.setInstanceForTesting(FakeBookmarkModel.createModel());
when(mNativeMock.isSigninAllowedByPolicy(NATIVE_SIGNIN_MANAGER)).thenReturn(true);
// Pretend Google Play services are available as it is required for the sign-in
when(mExternalAuthUtils.isGooglePlayServicesMissing(any())).thenReturn(false);
doAnswer(
invocation -> {
Runnable runnable = invocation.getArgument(0);
runnable.run();
return null;
})
.when(mAccountTrackerService)
.legacySeedAccountsIfNeeded(any(Runnable.class));
// Suppose that the accounts are already seeded
when(mIdentityManagerNativeMock.findExtendedAccountInfoByEmailAddress(
NATIVE_IDENTITY_MANAGER, ACCOUNT_INFO.getEmail()))
.thenReturn(ACCOUNT_INFO);
when(mIdentityManagerNativeMock.isClearPrimaryAccountAllowed(NATIVE_IDENTITY_MANAGER))
.thenReturn(true);
AccountManagerFacadeProvider.setInstanceForTests(mFakeAccountManagerFacade);
}
@After
public void tearDown() {
mSigninManager.removeSignInStateObserver(mSignInStateObserver);
mSigninManager.destroy();
AccountInfoServiceProvider.resetForTests();
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void
testAccountManagerFacadeObserverAddedOnCreate_accountFetchSucceeded_seedAccountsRevampEnabled() {
CoreAccountInfo coreAccountInfo =
CoreAccountInfo.createFromEmailAndGaiaId("email@domain.com", "gaia-id");
AccountManagerFacade accountManagerFacadeMock = Mockito.mock(AccountManagerFacade.class);
when(accountManagerFacadeMock.getCoreAccountInfos())
.thenReturn(Promise.fulfilled(List.of(coreAccountInfo)));
when(accountManagerFacadeMock.didAccountFetchSucceed()).thenReturn(true);
AccountManagerFacadeProvider.setInstanceForTests(accountManagerFacadeMock);
createSigninManager();
verify(accountManagerFacadeMock).addObserver(mSigninManager);
verify(mIdentityMutator)
.seedAccountsThenReloadAllAccountsWithPrimaryAccount(
List.of(coreAccountInfo), null);
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void
testAccountManagerFacadeObserverAddedOnCreate_accountFetchFailed_accountListPopulated_seedAccountsRevampEnabled() {
CoreAccountInfo coreAccountInfo =
CoreAccountInfo.createFromEmailAndGaiaId("email@domain.com", "gaia-id");
AccountManagerFacade accountManagerFacadeMock = Mockito.mock(AccountManagerFacade.class);
when(accountManagerFacadeMock.getCoreAccountInfos())
.thenReturn(Promise.fulfilled(List.of(coreAccountInfo)));
when(accountManagerFacadeMock.didAccountFetchSucceed()).thenReturn(false);
AccountManagerFacadeProvider.setInstanceForTests(accountManagerFacadeMock);
createSigninManager();
verify(accountManagerFacadeMock).addObserver(mSigninManager);
verify(mIdentityMutator)
.seedAccountsThenReloadAllAccountsWithPrimaryAccount(
List.of(coreAccountInfo), null);
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void
testAccountManagerFacadeObserverAddedOnCreate_accountFetchFailed_accountListEmpty_seedAccountsRevampEnabled() {
AccountManagerFacade accountManagerFacadeMock = Mockito.mock(AccountManagerFacade.class);
when(accountManagerFacadeMock.getCoreAccountInfos())
.thenReturn(Promise.fulfilled(List.of()));
when(accountManagerFacadeMock.didAccountFetchSucceed()).thenReturn(false);
AccountManagerFacadeProvider.setInstanceForTests(accountManagerFacadeMock);
createSigninManager();
verify(accountManagerFacadeMock).addObserver(mSigninManager);
verify(mIdentityMutator, never())
.seedAccountsThenReloadAllAccountsWithPrimaryAccount(any(), any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void testOnCoreAccountInfosChanged_seedAccountsRevampEnabled() {
createSigninManager();
mFakeAccountManagerFacade.addAccount(ACCOUNT_INFO);
List<CoreAccountInfo> coreAccountInfos =
mFakeAccountManagerFacade.getCoreAccountInfos().getResult();
verify(mIdentityMutator)
.seedAccountsThenReloadAllAccountsWithPrimaryAccount(coreAccountInfos, null);
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signinAndTurnSyncOn() {
createSigninManager();
if (SigninFeatureMap.isEnabled(SigninFeatures.ENTERPRISE_POLICY_ON_SIGNIN)) {
when(mNativeMock.getUserAcceptedAccountManagement(anyLong())).thenReturn(true);
}
when(mIdentityMutator.setPrimaryAccount(any(), anyInt(), anyInt(), any()))
.thenReturn(PrimaryAccountError.NO_ERROR);
when(mSyncService.getSelectedTypes()).thenReturn(Set.of(UserSelectableType.BOOKMARKS));
// There is no signed in account. Sign in is allowed.
assertTrue(mSigninManager.isSigninAllowed());
assertTrue(mSigninManager.isSyncOptInAllowed());
// Sign out is not allowed.
assertFalse(mSigninManager.isSignOutAllowed());
doAnswer(
(args) -> {
// A sign in operation is in progress, so we do not allow a new sign
// in/out operation.
assertFalse(mSigninManager.isSigninAllowed());
assertFalse(mSigninManager.isSyncOptInAllowed());
assertFalse(mSigninManager.isSignOutAllowed());
((Runnable) args.getArgument(2)).run();
return null;
})
.when(mNativeMock)
.fetchAndApplyCloudPolicy(eq(NATIVE_SIGNIN_MANAGER), eq(ACCOUNT_INFO), any());
SigninManager.SignInCallback callback = mock(SigninManager.SignInCallback.class);
mSigninManager.signinAndEnableSync(ACCOUNT_INFO, SigninAccessPoint.START_PAGE, callback);
verify(mIdentityMutator)
.setPrimaryAccount(
eq(ACCOUNT_INFO.getId()),
eq(ConsentLevel.SYNC),
eq(SigninAccessPoint.START_PAGE),
any());
verify(mSyncService).setSyncRequested();
// Signin should be complete and callback should be invoked.
verify(callback).onSignInComplete();
verify(callback, never()).onSignInAborted();
// The primary account is now present and consented to sign in and sync. We do not allow
// another account to be signed in.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
assertFalse(mSigninManager.isSigninAllowed());
assertFalse(mSigninManager.isSyncOptInAllowed());
// Signing out is allowed.
assertTrue(mSigninManager.isSignOutAllowed());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signinAndTurnSyncOn_seedAccountsRevampEnabled() {
createSigninManager();
if (SigninFeatureMap.isEnabled(SigninFeatures.ENTERPRISE_POLICY_ON_SIGNIN)) {
when(mNativeMock.getUserAcceptedAccountManagement(anyLong())).thenReturn(true);
}
mFakeAccountManagerFacade.addAccount(ACCOUNT_INFO);
when(mIdentityMutator.setPrimaryAccount(any(), anyInt(), anyInt(), any()))
.thenReturn(PrimaryAccountError.NO_ERROR);
when(mSyncService.getSelectedTypes()).thenReturn(Set.of(UserSelectableType.BOOKMARKS));
// There is no signed in account. Sign in is allowed.
assertTrue(mSigninManager.isSigninAllowed());
assertTrue(mSigninManager.isSyncOptInAllowed());
// Sign out is not allowed.
assertFalse(mSigninManager.isSignOutAllowed());
List<CoreAccountInfo> coreAccountInfos =
mFakeAccountManagerFacade.getCoreAccountInfos().getResult();
CoreAccountInfo primaryAccountInfo =
AccountUtils.findCoreAccountInfoByEmail(coreAccountInfos, ACCOUNT_INFO.getEmail());
doAnswer(
(args) -> {
// A sign in operation is in progress, so we do not allow a new sign
// in/out operation.
assertFalse(mSigninManager.isSigninAllowed());
assertFalse(mSigninManager.isSyncOptInAllowed());
assertFalse(mSigninManager.isSignOutAllowed());
((Runnable) args.getArgument(2)).run();
return null;
})
.when(mNativeMock)
.fetchAndApplyCloudPolicy(eq(NATIVE_SIGNIN_MANAGER), eq(primaryAccountInfo), any());
SigninManager.SignInCallback callback = mock(SigninManager.SignInCallback.class);
mSigninManager.signinAndEnableSync(ACCOUNT_INFO, SigninAccessPoint.START_PAGE, callback);
verify(mIdentityMutator)
.seedAccountsThenReloadAllAccountsWithPrimaryAccount(
coreAccountInfos, primaryAccountInfo.getId());
verify(mIdentityMutator)
.setPrimaryAccount(
eq(primaryAccountInfo.getId()),
eq(ConsentLevel.SYNC),
eq(SigninAccessPoint.START_PAGE),
any());
verify(mSyncService).setSyncRequested();
// Signin should be complete and callback should be invoked.
verify(callback).onSignInComplete();
verify(callback, never()).onSignInAborted();
// The primary account is now present and consented to sign in and sync. We do not allow
// another account to be signed in.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
assertFalse(mSigninManager.isSigninAllowed());
assertFalse(mSigninManager.isSyncOptInAllowed());
// Signing out is allowed.
assertTrue(mSigninManager.isSignOutAllowed());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signinNoTurnSyncOn() {
createSigninManager();
when(mIdentityMutator.setPrimaryAccount(any(), anyInt(), anyInt(), any()))
.thenReturn(PrimaryAccountError.NO_ERROR);
assertTrue(mSigninManager.isSigninAllowed());
assertTrue(mSigninManager.isSyncOptInAllowed());
SigninManager.SignInCallback callback = mock(SigninManager.SignInCallback.class);
mSigninManager.signin(ACCOUNT_INFO, SigninAccessPoint.START_PAGE, callback);
// Signin without turning on sync shouldn't apply policies.
verify(mNativeMock, never()).fetchAndApplyCloudPolicy(anyLong(), any(), any());
verify(mIdentityMutator)
.setPrimaryAccount(
eq(ACCOUNT_INFO.getId()),
eq(ConsentLevel.SIGNIN),
eq(SigninAccessPoint.START_PAGE),
any());
verify(mSyncService, never()).setSyncRequested();
// Signin should be complete and callback should be invoked.
verify(callback).onSignInComplete();
verify(callback, never()).onSignInAborted();
// The primary account is now present and consented to sign in.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), eq(ConsentLevel.SIGNIN)))
.thenReturn(ACCOUNT_INFO);
assertFalse(mSigninManager.isSigninAllowed());
assertTrue(mSigninManager.isSyncOptInAllowed());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signinNoTurnSyncOn_seedAccountsRevampEnabled() {
createSigninManager();
mFakeAccountManagerFacade.addAccount(ACCOUNT_INFO);
when(mIdentityMutator.setPrimaryAccount(any(), anyInt(), anyInt(), any()))
.thenReturn(PrimaryAccountError.NO_ERROR);
assertTrue(mSigninManager.isSigninAllowed());
assertTrue(mSigninManager.isSyncOptInAllowed());
SigninManager.SignInCallback callback = mock(SigninManager.SignInCallback.class);
mSigninManager.signin(ACCOUNT_INFO, SigninAccessPoint.START_PAGE, callback);
// Signin without turning on sync shouldn't apply policies.
verify(mNativeMock, never()).fetchAndApplyCloudPolicy(anyLong(), any(), any());
List<CoreAccountInfo> coreAccountInfos =
mFakeAccountManagerFacade.getCoreAccountInfos().getResult();
CoreAccountId primaryAccountId =
AccountUtils.findCoreAccountInfoByEmail(coreAccountInfos, ACCOUNT_INFO.getEmail())
.getId();
verify(mIdentityMutator)
.seedAccountsThenReloadAllAccountsWithPrimaryAccount(
coreAccountInfos, primaryAccountId);
verify(mIdentityMutator)
.setPrimaryAccount(
eq(primaryAccountId),
eq(ConsentLevel.SIGNIN),
eq(SigninAccessPoint.START_PAGE),
any());
verify(mSyncService, never()).setSyncRequested();
// Signin should be complete and callback should be invoked.
verify(callback).onSignInComplete();
verify(callback, never()).onSignInAborted();
// The primary account is now present and consented to sign in.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), eq(ConsentLevel.SIGNIN)))
.thenReturn(ACCOUNT_INFO);
assertFalse(mSigninManager.isSigninAllowed());
assertTrue(mSigninManager.isSyncOptInAllowed());
}
@Test
@EnableFeatures({
SigninFeatures.ENTERPRISE_POLICY_ON_SIGNIN,
SigninFeatures.SEED_ACCOUNTS_REVAMP
})
public void signinNoTurnSyncOn_enterprisePoliciesOnSignin() {
createSigninManager();
when(mIdentityMutator.setPrimaryAccount(any(), anyInt(), anyInt(), any()))
.thenReturn(PrimaryAccountError.NO_ERROR);
when(mNativeMock.getUserAcceptedAccountManagement(anyLong())).thenReturn(true);
doAnswer(
(args) -> {
((Callback<Boolean>) args.getArgument(2)).onResult(true);
return null;
})
.when(mNativeMock)
.isAccountManaged(anyLong(), any(), any(), any());
doAnswer(
(args) -> {
((Runnable) args.getArgument(2)).run();
return null;
})
.when(mNativeMock)
.fetchAndApplyCloudPolicy(anyLong(), any(), any());
assertTrue(mSigninManager.isSigninAllowed());
assertTrue(mSigninManager.isSyncOptInAllowed());
SigninManager.SignInCallback callback = mock(SigninManager.SignInCallback.class);
mSigninManager.signin(ACCOUNT_INFO, SigninAccessPoint.START_PAGE, callback);
// Signin without turning on sync should still apply policies.
verify(mNativeMock).fetchAndApplyCloudPolicy(anyLong(), any(), any());
verify(mIdentityMutator)
.setPrimaryAccount(
eq(ACCOUNT_INFO.getId()),
eq(ConsentLevel.SIGNIN),
eq(SigninAccessPoint.START_PAGE),
any());
verify(mSyncService, never()).setSyncRequested();
// Signin should be complete and callback should be invoked.
verify(callback).onSignInComplete();
verify(callback, never()).onSignInAborted();
// The primary account is now present and consented to sign in.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), eq(ConsentLevel.SIGNIN)))
.thenReturn(ACCOUNT_INFO);
assertFalse(mSigninManager.isSigninAllowed());
assertTrue(mSigninManager.isSyncOptInAllowed());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signOutNonSyncingAccountFromJavaWithManagedDomain() {
createSigninManager();
when(mNativeMock.getManagementDomain(NATIVE_SIGNIN_MANAGER)).thenReturn("TestDomain");
// Trigger the sign out flow!
mSigninManager.signOut(SignoutReason.TEST);
// The primary account should be cleared *before* clearing any account data.
// For more information see crbug.com/589028.
InOrder inOrder = inOrder(mNativeMock, mIdentityMutator);
inOrder.verify(mIdentityMutator).clearPrimaryAccount(eq(SignoutReason.TEST));
// Sign-out should only clear the profile when the user is managed.
inOrder.verify(mNativeMock).wipeProfileData(eq(NATIVE_SIGNIN_MANAGER), any());
verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signOutNonSyncingAccountFromJavaWithManagedDomain_seedAccountsRevampEnabled() {
createSigninManager();
when(mNativeMock.getManagementDomain(NATIVE_SIGNIN_MANAGER)).thenReturn("TestDomain");
// Trigger the sign out flow!
mSigninManager.signOut(SignoutReason.TEST);
// The primary account should be cleared *before* clearing any account data.
// For more information see crbug.com/589028.
InOrder inOrder = inOrder(mNativeMock, mIdentityMutator);
inOrder.verify(mIdentityMutator).clearPrimaryAccount(eq(SignoutReason.TEST));
verify(mIdentityMutator)
.seedAccountsThenReloadAllAccountsWithPrimaryAccount(List.of(), null);
// Sign-out should only clear the profile when the user is managed.
inOrder.verify(mNativeMock).wipeProfileData(eq(NATIVE_SIGNIN_MANAGER), any());
verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signOutSyncingAccountFromJavaWithManagedDomain() {
createSigninManager();
when(mNativeMock.getManagementDomain(NATIVE_SIGNIN_MANAGER)).thenReturn("TestDomain");
// Trigger the sign out flow!
mSigninManager.signOut(SignoutReason.TEST);
// The primary account should be cleared *before* clearing any account data.
// For more information see crbug.com/589028.
InOrder inOrder = inOrder(mNativeMock, mIdentityMutator);
inOrder.verify(mIdentityMutator).clearPrimaryAccount(eq(SignoutReason.TEST));
// Sign-out should only clear the profile when the user is managed.
inOrder.verify(mNativeMock).wipeProfileData(eq(NATIVE_SIGNIN_MANAGER), any());
verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signOutSyncingAccountFromJavaWithManagedDomain_seedAccountsRevampEnabled() {
createSigninManager();
when(mNativeMock.getManagementDomain(NATIVE_SIGNIN_MANAGER)).thenReturn("TestDomain");
// Trigger the sign out flow!
mSigninManager.signOut(SignoutReason.TEST);
// The primary account should be cleared *before* clearing any account data.
// For more information see crbug.com/589028.
InOrder inOrder = inOrder(mNativeMock, mIdentityMutator);
inOrder.verify(mIdentityMutator).clearPrimaryAccount(eq(SignoutReason.TEST));
verify(mIdentityMutator)
.seedAccountsThenReloadAllAccountsWithPrimaryAccount(List.of(), null);
// Sign-out should only clear the profile when the user is managed.
inOrder.verify(mNativeMock).wipeProfileData(eq(NATIVE_SIGNIN_MANAGER), any());
verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signOutNonSyncingAccountFromJavaWithNullDomain() {
createSigninManager();
mSigninManager.signOut(SignoutReason.TEST);
// The primary account should be cleared *before* clearing any account data.
// For more information see crbug.com/589028.
InOrder inOrder = inOrder(mNativeMock, mIdentityMutator);
inOrder.verify(mIdentityMutator).clearPrimaryAccount(eq(SignoutReason.TEST));
// Sign-out should only clear the service worker cache when the user is neither managed or
// syncing.
verify(mNativeMock, never()).wipeProfileData(anyLong(), any());
inOrder.verify(mNativeMock).wipeGoogleServiceWorkerCaches(eq(NATIVE_SIGNIN_MANAGER), any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signOutNonSyncingAccountFromJavaWithNullDomain_seedAccountsRevampEnabled() {
createSigninManager();
mSigninManager.signOut(SignoutReason.TEST);
// The primary account should be cleared *before* clearing any account data.
// For more information see crbug.com/589028.
InOrder inOrder = inOrder(mNativeMock, mIdentityMutator);
inOrder.verify(mIdentityMutator).clearPrimaryAccount(eq(SignoutReason.TEST));
verify(mIdentityMutator)
.seedAccountsThenReloadAllAccountsWithPrimaryAccount(List.of(), null);
// Sign-out should only clear the service worker cache when the user is neither managed or
// syncing.
verify(mNativeMock, never()).wipeProfileData(anyLong(), any());
inOrder.verify(mNativeMock).wipeGoogleServiceWorkerCaches(eq(NATIVE_SIGNIN_MANAGER), any());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signOutSyncingAccountFromJavaWithNullDomain() {
createSigninManager();
// Simulate sign-out with non-managed account.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.signOut(SignoutReason.TEST);
// The primary account should be cleared *before* clearing any account data.
// For more information see crbug.com/589028.
InOrder inOrder = inOrder(mNativeMock, mIdentityMutator);
inOrder.verify(mIdentityMutator).clearPrimaryAccount(eq(SignoutReason.TEST));
// Sign-out should only clear the service worker cache when the user has decided not to
// wipe data.
verify(mNativeMock, never()).wipeProfileData(anyLong(), any());
inOrder.verify(mNativeMock).wipeGoogleServiceWorkerCaches(eq(NATIVE_SIGNIN_MANAGER), any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signOutSyncingAccountFromJavaWithNullDomain_seedAccountsRevampEnabled() {
createSigninManager();
// Simulate sign-out with non-managed account.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.signOut(SignoutReason.TEST);
// The primary account should be cleared *before* clearing any account data.
// For more information see crbug.com/589028.
InOrder inOrder = inOrder(mNativeMock, mIdentityMutator);
inOrder.verify(mIdentityMutator).clearPrimaryAccount(eq(SignoutReason.TEST));
verify(mIdentityMutator)
.seedAccountsThenReloadAllAccountsWithPrimaryAccount(List.of(), null);
// Sign-out should only clear the service worker cache when the user has decided not to
// wipe data.
verify(mNativeMock, never()).wipeProfileData(anyLong(), any());
inOrder.verify(mNativeMock).wipeGoogleServiceWorkerCaches(eq(NATIVE_SIGNIN_MANAGER), any());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
@EnableFeatures(ChromeFeatureList.SYNC_ANDROID_LIMIT_NTP_PROMO_IMPRESSIONS)
public void syncPromoShowCountResetWhenSignOutSyncingAccount() {
createSigninManager();
ChromeSharedPreferences.getInstance()
.writeInt(
ChromePreferenceKeys.SYNC_PROMO_SHOW_COUNT.createKey(
SigninPreferencesManager.SyncPromoAccessPointId.NTP),
1);
// Simulate sign-out with non-managed account.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.signOut(SignoutReason.TEST);
ArgumentCaptor<Runnable> callback = ArgumentCaptor.forClass(Runnable.class);
verify(mNativeMock)
.wipeGoogleServiceWorkerCaches(eq(NATIVE_SIGNIN_MANAGER), callback.capture());
assertNotNull(callback.getValue());
callback.getValue().run();
assertEquals(
0,
ChromeSharedPreferences.getInstance()
.readInt(
ChromePreferenceKeys.SYNC_PROMO_SHOW_COUNT.createKey(
SigninPreferencesManager.SyncPromoAccessPointId.NTP)));
}
@Test
@EnableFeatures({
ChromeFeatureList.SYNC_ANDROID_LIMIT_NTP_PROMO_IMPRESSIONS,
SigninFeatures.SEED_ACCOUNTS_REVAMP
})
public void syncPromoShowCountResetWhenSignOutSyncingAccount_seedAccountsRevampEnabled() {
createSigninManager();
ChromeSharedPreferences.getInstance()
.writeInt(
ChromePreferenceKeys.SYNC_PROMO_SHOW_COUNT.createKey(
SigninPreferencesManager.SyncPromoAccessPointId.NTP),
1);
// Simulate sign-out with non-managed account.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.signOut(SignoutReason.TEST);
ArgumentCaptor<Runnable> callback = ArgumentCaptor.forClass(Runnable.class);
verify(mNativeMock)
.wipeGoogleServiceWorkerCaches(eq(NATIVE_SIGNIN_MANAGER), callback.capture());
assertNotNull(callback.getValue());
callback.getValue().run();
assertEquals(
0,
ChromeSharedPreferences.getInstance()
.readInt(
ChromePreferenceKeys.SYNC_PROMO_SHOW_COUNT.createKey(
SigninPreferencesManager.SyncPromoAccessPointId.NTP)));
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signOutSyncingAccountFromJavaWithNullDomainAndForceWipe() {
createSigninManager();
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.signOut(SignoutReason.TEST, null, true);
// The primary account should be cleared *before* clearing any account data.
// For more information see crbug.com/589028.
InOrder inOrder = inOrder(mNativeMock, mIdentityMutator);
inOrder.verify(mIdentityMutator).clearPrimaryAccount(eq(SignoutReason.TEST));
// Sign-out should only clear the profile when the user is syncing and has decided to
// wipe data.
inOrder.verify(mNativeMock).wipeProfileData(eq(NATIVE_SIGNIN_MANAGER), any());
verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void
signOutSyncingAccountFromJavaWithNullDomainAndForceWipe_seedAccountsRevampEnabled() {
createSigninManager();
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.signOut(SignoutReason.TEST, null, true);
// The primary account should be cleared *before* clearing any account data.
// For more information see crbug.com/589028.
InOrder inOrder = inOrder(mNativeMock, mIdentityMutator);
inOrder.verify(mIdentityMutator).clearPrimaryAccount(eq(SignoutReason.TEST));
// Sign-out should only clear the profile when the user is syncing and has decided to
// wipe data.
inOrder.verify(mNativeMock).wipeProfileData(eq(NATIVE_SIGNIN_MANAGER), any());
verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
// TODO(crbug.com/40820738): add test for revokeSyncConsentFromJavaWithManagedDomain() and
// revokeSyncConsentFromJavaWipeData() - this requires making the BookmarkModel mockable in
// SigninManagerImpl.
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void revokeSyncConsentFromJavaWithNullDomain() {
createSigninManager();
SigninManager.SignOutCallback callback = mock(SigninManager.SignOutCallback.class);
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.revokeSyncConsent(SignoutReason.TEST, callback, false);
// The primary account should be cleared *before* clearing any account data.
// For more information see crbug.com/589028.
InOrder inOrder = inOrder(mNativeMock, mIdentityMutator);
inOrder.verify(mIdentityMutator).revokeSyncConsent(eq(SignoutReason.TEST));
// Disabling sync should only clear the service worker cache when the user is neither
// managed or syncing.
verify(mNativeMock, never()).wipeProfileData(anyLong(), any());
inOrder.verify(mNativeMock).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void revokeSyncConsentFromJavaWithNullDomain_seedAccountsRevampEnabled() {
createSigninManager();
SigninManager.SignOutCallback callback = mock(SigninManager.SignOutCallback.class);
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.revokeSyncConsent(SignoutReason.TEST, callback, false);
// The primary account should be cleared *before* clearing any account data.
// For more information see crbug.com/589028.
InOrder inOrder = inOrder(mNativeMock, mIdentityMutator);
inOrder.verify(mIdentityMutator).revokeSyncConsent(eq(SignoutReason.TEST));
// Disabling sync should only clear the service worker cache when the user is neither
// managed or syncing.
verify(mNativeMock, never()).wipeProfileData(anyLong(), any());
inOrder.verify(mNativeMock).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void revokeSyncConsentFromJavaWithNullDomainAndWipeData_noLocalUpm() {
createSigninManager();
when(mPasswordManagerUtilBridgeNativeMock.usesSplitStoresAndUPMForLocal(mPrefService))
.thenReturn(false);
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.revokeSyncConsent(
SignoutReason.TEST,
mock(SigninManager.SignOutCallback.class),
/* forceWipeUserData= */ true);
// Passwords should be among the cleared types.
int[] expectedClearedTypes =
new int[] {
BrowsingDataType.HISTORY,
BrowsingDataType.CACHE,
BrowsingDataType.SITE_DATA,
BrowsingDataType.FORM_DATA,
BrowsingDataType.PASSWORDS
};
verify(mBrowsingDataBridgeNativeMock)
.clearBrowsingData(
any(),
any(),
any(),
eq(expectedClearedTypes),
eq(TimePeriod.ALL_TIME),
any(),
any(),
any(),
any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void revokeSyncConsentFromJavaWithNullDomainAndWipeData_withLocalUpm() {
createSigninManager();
when(mPasswordManagerUtilBridgeNativeMock.usesSplitStoresAndUPMForLocal(mPrefService))
.thenReturn(true);
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.revokeSyncConsent(
SignoutReason.TEST,
mock(SigninManager.SignOutCallback.class),
/* forceWipeUserData= */ true);
// Passwords should not be among the cleared types.
int[] expectedClearedTypes =
new int[] {
BrowsingDataType.HISTORY,
BrowsingDataType.CACHE,
BrowsingDataType.SITE_DATA,
BrowsingDataType.FORM_DATA,
};
verify(mBrowsingDataBridgeNativeMock)
.clearBrowsingData(
any(),
any(),
any(),
eq(expectedClearedTypes),
eq(TimePeriod.ALL_TIME),
any(),
any(),
any(),
any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void wipeSyncDataOnly_noLocalUpm() {
createSigninManager();
when(mPasswordManagerUtilBridgeNativeMock.usesSplitStoresAndUPMForLocal(mPrefService))
.thenReturn(false);
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.wipeSyncUserData(() -> {}, SigninManager.DataWipeOption.WIPE_SYNC_DATA);
// Passwords should be among the cleared types.
int[] expectedClearedTypes =
new int[] {
BrowsingDataType.HISTORY,
BrowsingDataType.CACHE,
BrowsingDataType.SITE_DATA,
BrowsingDataType.FORM_DATA,
BrowsingDataType.PASSWORDS
};
verify(mBrowsingDataBridgeNativeMock)
.clearBrowsingData(
any(),
any(),
any(),
eq(expectedClearedTypes),
eq(TimePeriod.ALL_TIME),
any(),
any(),
any(),
any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void wipeSyncDataOnly_withLocalUpm() {
createSigninManager();
when(mPasswordManagerUtilBridgeNativeMock.usesSplitStoresAndUPMForLocal(mPrefService))
.thenReturn(true);
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.wipeSyncUserData(() -> {}, SigninManager.DataWipeOption.WIPE_SYNC_DATA);
// Passwords should not be among the cleared types.
int[] expectedClearedTypes =
new int[] {
BrowsingDataType.HISTORY,
BrowsingDataType.CACHE,
BrowsingDataType.SITE_DATA,
BrowsingDataType.FORM_DATA,
};
verify(mBrowsingDataBridgeNativeMock)
.clearBrowsingData(
any(),
any(),
any(),
eq(expectedClearedTypes),
eq(TimePeriod.ALL_TIME),
any(),
any(),
any(),
any());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void clearingAccountCookieDoesNotTriggerSignoutWhenUserIsSignedOut() {
createSigninManager();
mFakeAccountManagerFacade.addAccount(
AccountUtils.createAccountFromName(ACCOUNT_INFO.getEmail()));
mIdentityManager.onAccountsCookieDeletedByUserAction();
verify(mIdentityMutator, never()).clearPrimaryAccount(anyInt());
verify(mNativeMock, never()).wipeProfileData(anyLong(), any());
verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void
clearingAccountCookieDoesNotTriggerSignoutWhenUserIsSignedOut_seedAccountsRevampEnabled() {
createSigninManager();
mFakeAccountManagerFacade.addAccount(ACCOUNT_INFO);
mIdentityManager.onAccountsCookieDeletedByUserAction();
verify(mIdentityMutator, never()).clearPrimaryAccount(anyInt());
verify(mNativeMock, never()).wipeProfileData(anyLong(), any());
verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void clearingAccountCookieDoesNotTriggerSignoutWhenUserIsSignedInAndSync() {
createSigninManager();
mFakeAccountManagerFacade.addAccount(
AccountUtils.createAccountFromName(ACCOUNT_INFO.getEmail()));
mIdentityManager.onAccountsCookieDeletedByUserAction();
verify(mIdentityMutator, never()).clearPrimaryAccount(anyInt());
verify(mNativeMock, never()).wipeProfileData(anyLong(), any());
verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void
clearingAccountCookieDoesNotTriggerSignoutWhenUserIsSignedInAndSync_seedAccountsRevampEnabled() {
createSigninManager();
mFakeAccountManagerFacade.addAccount(
AccountUtils.createAccountFromName(ACCOUNT_INFO.getEmail()));
mIdentityManager.onAccountsCookieDeletedByUserAction();
verify(mIdentityMutator, never()).clearPrimaryAccount(anyInt());
verify(mNativeMock, never()).wipeProfileData(anyLong(), any());
verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void clearingAccountCookieDoesNotTriggerSignoutWhenUserIsSignedInWithoutSync() {
createSigninManager();
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
NATIVE_IDENTITY_MANAGER, ConsentLevel.SIGNIN))
.thenReturn(ACCOUNT_INFO);
mFakeAccountManagerFacade.addAccount(
AccountUtils.createAccountFromName(ACCOUNT_INFO.getEmail()));
mIdentityManager.onAccountsCookieDeletedByUserAction();
verify(mIdentityMutator, never()).clearPrimaryAccount(anyInt());
verify(mNativeMock, never()).wipeProfileData(anyLong(), any());
verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void
clearingAccountCookieDoesNotTriggerSignoutWhenUserIsSignedInWithoutSync_seedAccountsRevampEnabled() {
createSigninManager();
mFakeAccountManagerFacade.addAccount(ACCOUNT_INFO);
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
NATIVE_IDENTITY_MANAGER, ConsentLevel.SIGNIN))
.thenReturn(
AccountUtils.findCoreAccountInfoByEmail(
mFakeAccountManagerFacade.getCoreAccountInfos().getResult(),
ACCOUNT_INFO.getEmail()));
mIdentityManager.onAccountsCookieDeletedByUserAction();
verify(mIdentityMutator, never()).clearPrimaryAccount(anyInt());
verify(mNativeMock, never()).wipeProfileData(anyLong(), any());
verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(anyLong(), any());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void callbackNotifiedWhenNoOperationIsInProgress() {
createSigninManager();
AtomicInteger callCount = new AtomicInteger(0);
mSigninManager.runAfterOperationInProgress(callCount::incrementAndGet);
assertEquals(1, callCount.get());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void callbackNotifiedWhenNoOperationIsInProgress_seedAccountsRevampEnabled() {
createSigninManager();
AtomicInteger callCount = new AtomicInteger(0);
mSigninManager.runAfterOperationInProgress(callCount::incrementAndGet);
assertEquals(1, callCount.get());
}
@Test
// TODO(crbug.com/40858677): Disabling the feature explicitly, because native is not available
// to
// provide a default value. This should be enabled if the feature is enabled by default or
// removed if the flag is removed.
@DisableFeatures({
ChromeFeatureList.SYNC_ANDROID_LIMIT_NTP_PROMO_IMPRESSIONS,
SigninFeatures.SEED_ACCOUNTS_REVAMP
})
public void callbackNotifiedOnSignout() {
createSigninManager();
doAnswer(
invocation -> {
mIdentityManager.onPrimaryAccountChanged(
new PrimaryAccountChangeEvent(
PrimaryAccountChangeEvent.Type.CLEARED,
PrimaryAccountChangeEvent.Type.NONE));
return null;
})
.when(mIdentityMutator)
.clearPrimaryAccount(anyInt());
mSigninManager.signOut(SignoutReason.TEST);
AtomicInteger callCount = new AtomicInteger(0);
mSigninManager.runAfterOperationInProgress(callCount::incrementAndGet);
assertEquals(0, callCount.get());
mSigninManager.finishSignOut();
assertEquals(1, callCount.get());
}
@Test
// TODO(crbug.com/40858677): Disabling the feature explicitly, because native is not available
// to
// provide a default value. This should be enabled if the feature is enabled by default or
// removed if the flag is removed.
@DisableFeatures(ChromeFeatureList.SYNC_ANDROID_LIMIT_NTP_PROMO_IMPRESSIONS)
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void callbackNotifiedOnSignout_seedAccountsRevampEnabled() {
createSigninManager();
doAnswer(
invocation -> {
mIdentityManager.onPrimaryAccountChanged(
new PrimaryAccountChangeEvent(
PrimaryAccountChangeEvent.Type.CLEARED,
PrimaryAccountChangeEvent.Type.NONE));
return null;
})
.when(mIdentityMutator)
.clearPrimaryAccount(anyInt());
mSigninManager.signOut(SignoutReason.TEST);
AtomicInteger callCount = new AtomicInteger(0);
mSigninManager.runAfterOperationInProgress(callCount::incrementAndGet);
assertEquals(0, callCount.get());
mSigninManager.finishSignOut();
assertEquals(1, callCount.get());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void callbackNotifiedOnSignin() {
createSigninManager();
if (SigninFeatureMap.isEnabled(SigninFeatures.ENTERPRISE_POLICY_ON_SIGNIN)) {
when(mNativeMock.getUserAcceptedAccountManagement(anyLong())).thenReturn(true);
}
final Answer<Integer> setPrimaryAccountAnswer =
invocation -> {
// From now on getPrimaryAccountInfo should return account.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
return PrimaryAccountError.NO_ERROR;
};
doAnswer(setPrimaryAccountAnswer)
.when(mIdentityMutator)
.setPrimaryAccount(
eq(ACCOUNT_INFO.getId()),
eq(ConsentLevel.SYNC),
eq(SigninAccessPoint.UNKNOWN),
any());
AtomicInteger callCount = new AtomicInteger(0);
doAnswer(
(args) -> {
mSigninManager.runAfterOperationInProgress(callCount::incrementAndGet);
assertEquals(0, callCount.get());
((Runnable) args.getArgument(2)).run();
return null;
})
.when(mNativeMock)
.fetchAndApplyCloudPolicy(anyLong(), any(), any());
mSigninManager.signinAndEnableSync(ACCOUNT_INFO, SigninAccessPoint.UNKNOWN, null);
assertEquals(1, callCount.get());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void callbackNotifiedOnSignin_seedAccountsRevampEnabled() {
createSigninManager();
if (SigninFeatureMap.isEnabled(SigninFeatures.ENTERPRISE_POLICY_ON_SIGNIN)) {
when(mNativeMock.getUserAcceptedAccountManagement(anyLong())).thenReturn(true);
}
mFakeAccountManagerFacade.addAccount(ACCOUNT_INFO);
final Answer<Integer> setPrimaryAccountAnswer =
invocation -> {
// From now on getPrimaryAccountInfo should return account.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
return PrimaryAccountError.NO_ERROR;
};
doAnswer(setPrimaryAccountAnswer)
.when(mIdentityMutator)
.setPrimaryAccount(
eq(ACCOUNT_INFO.getId()),
eq(ConsentLevel.SYNC),
eq(SigninAccessPoint.UNKNOWN),
any());
AtomicInteger callCount = new AtomicInteger(0);
doAnswer(
(args) -> {
mSigninManager.runAfterOperationInProgress(callCount::incrementAndGet);
assertEquals(0, callCount.get());
((Runnable) args.getArgument(2)).run();
return null;
})
.when(mNativeMock)
.fetchAndApplyCloudPolicy(anyLong(), any(), any());
mSigninManager.signinAndEnableSync(ACCOUNT_INFO, SigninAccessPoint.UNKNOWN, null);
assertEquals(1, callCount.get());
}
@Test(expected = AssertionError.class)
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signinfailsWhenAlreadySignedIn() {
createSigninManager();
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.signinAndEnableSync(ACCOUNT_INFO, SigninAccessPoint.UNKNOWN, null);
}
@Test(expected = AssertionError.class)
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signinfailsWhenAlreadySignedIn_seedAccountsRevampEnabled() {
createSigninManager();
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
mSigninManager.signinAndEnableSync(ACCOUNT_INFO, SigninAccessPoint.UNKNOWN, null);
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signInStateObserverCallOnSignIn() {
createSigninManager();
if (SigninFeatureMap.isEnabled(SigninFeatures.ENTERPRISE_POLICY_ON_SIGNIN)) {
when(mNativeMock.getUserAcceptedAccountManagement(anyLong())).thenReturn(true);
}
final Answer<Integer> setPrimaryAccountAnswer =
invocation -> {
// From now on getPrimaryAccountInfo should return account.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
return PrimaryAccountError.NO_ERROR;
};
doAnswer(setPrimaryAccountAnswer)
.when(mIdentityMutator)
.setPrimaryAccount(
eq(ACCOUNT_INFO.getId()),
eq(ConsentLevel.SYNC),
eq(SigninAccessPoint.START_PAGE),
any());
doAnswer(
(args) -> {
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mSignInStateObserver).onSignInAllowedChanged();
((Runnable) args.getArgument(2)).run();
return null;
})
.when(mNativeMock)
.fetchAndApplyCloudPolicy(anyLong(), any(), any());
mSigninManager.signinAndEnableSync(ACCOUNT_INFO, SigninAccessPoint.START_PAGE, null);
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mSignInStateObserver).onSignOutAllowedChanged();
assertFalse(mSigninManager.isSigninAllowed());
assertTrue(mSigninManager.isSignOutAllowed());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signInStateObserverCallOnSignIn_seedAccountsRevampEnabled() {
createSigninManager();
if (SigninFeatureMap.isEnabled(SigninFeatures.ENTERPRISE_POLICY_ON_SIGNIN)) {
when(mNativeMock.getUserAcceptedAccountManagement(anyLong())).thenReturn(true);
}
mFakeAccountManagerFacade.addAccount(ACCOUNT_INFO);
List<CoreAccountInfo> coreAccountInfos =
mFakeAccountManagerFacade.getCoreAccountInfos().getResult();
CoreAccountInfo primaryAccountInfo =
AccountUtils.findCoreAccountInfoByEmail(coreAccountInfos, ACCOUNT_INFO.getEmail());
final Answer<Integer> setPrimaryAccountAnswer =
invocation -> {
// From now on getPrimaryAccountInfo should return account.
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(primaryAccountInfo);
return PrimaryAccountError.NO_ERROR;
};
doAnswer(setPrimaryAccountAnswer)
.when(mIdentityMutator)
.setPrimaryAccount(
eq(primaryAccountInfo.getId()),
eq(ConsentLevel.SYNC),
eq(SigninAccessPoint.START_PAGE),
any());
doAnswer(
(args) -> {
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mSignInStateObserver).onSignInAllowedChanged();
((Runnable) args.getArgument(2)).run();
return null;
})
.when(mNativeMock)
.fetchAndApplyCloudPolicy(anyLong(), any(), any());
mSigninManager.signinAndEnableSync(ACCOUNT_INFO, SigninAccessPoint.START_PAGE, null);
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mSignInStateObserver).onSignOutAllowedChanged();
assertFalse(mSigninManager.isSigninAllowed());
assertTrue(mSigninManager.isSignOutAllowed());
}
@Test
@DisableFeatures({
ChromeFeatureList.SYNC_ANDROID_LIMIT_NTP_PROMO_IMPRESSIONS,
SigninFeatures.SEED_ACCOUNTS_REVAMP
})
public void signInStateObserverCallOnSignOut() {
createSigninManager();
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
assertTrue(mSigninManager.isSignOutAllowed());
mSigninManager.signOut(SignoutReason.TEST);
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mSignInStateObserver).onSignOutAllowedChanged();
assertFalse(mSigninManager.isSignOutAllowed());
}
@Test
@DisableFeatures(ChromeFeatureList.SYNC_ANDROID_LIMIT_NTP_PROMO_IMPRESSIONS)
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signInStateObserverCallOnSignOut_seedAccountsRevampEnabled() {
createSigninManager();
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
assertTrue(mSigninManager.isSignOutAllowed());
mSigninManager.signOut(SignoutReason.TEST);
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mSignInStateObserver).onSignOutAllowedChanged();
assertFalse(mSigninManager.isSignOutAllowed());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signOutNotAllowedForChildAccounts() {
createSigninManager();
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
when(mIdentityManagerNativeMock.isClearPrimaryAccountAllowed(NATIVE_IDENTITY_MANAGER))
.thenReturn(false);
assertFalse(mSigninManager.isSignOutAllowed());
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signOutNotAllowedForChildAccounts_seedAccountsRevampEnabled() {
createSigninManager();
when(mIdentityManagerNativeMock.getPrimaryAccountInfo(
eq(NATIVE_IDENTITY_MANAGER), anyInt()))
.thenReturn(ACCOUNT_INFO);
when(mIdentityManagerNativeMock.isClearPrimaryAccountAllowed(NATIVE_IDENTITY_MANAGER))
.thenReturn(false);
assertFalse(mSigninManager.isSignOutAllowed());
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signInShouldBeSupportedForNonDemoUsers() {
createSigninManager();
when(mExternalAuthUtils.canUseGooglePlayServices()).thenReturn(true);
// Make sure that the user is not a demo user.
ShadowApplication shadowApplication = ShadowApplication.getInstance();
UserManager userManager = Mockito.mock(UserManager.class);
Mockito.when(userManager.isDemoUser()).thenReturn(false);
shadowApplication.setSystemService(Context.USER_SERVICE, userManager);
assertTrue(mSigninManager.isSigninSupported(/* requireUpdatedPlayServices= */ true));
assertTrue(mSigninManager.isSigninSupported(/* requireUpdatedPlayServices= */ false));
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signInShouldBeSupportedForNonDemoUsers_seedAccountsRevampEnabled() {
createSigninManager();
when(mExternalAuthUtils.canUseGooglePlayServices()).thenReturn(true);
// Make sure that the user is not a demo user.
ShadowApplication shadowApplication = ShadowApplication.getInstance();
UserManager userManager = Mockito.mock(UserManager.class);
Mockito.when(userManager.isDemoUser()).thenReturn(false);
shadowApplication.setSystemService(Context.USER_SERVICE, userManager);
assertTrue(mSigninManager.isSigninSupported(/* requireUpdatedPlayServices= */ true));
assertTrue(mSigninManager.isSigninSupported(/* requireUpdatedPlayServices= */ false));
}
@Test
@DisableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void signInShouldNotBeSupportedWhenGooglePlayServicesIsRequiredAndNotAvailable() {
createSigninManager();
when(mExternalAuthUtils.canUseGooglePlayServices()).thenReturn(false);
// Make sure that the user is not a demo user.
ShadowApplication shadowApplication = ShadowApplication.getInstance();
UserManager userManager = Mockito.mock(UserManager.class);
Mockito.when(userManager.isDemoUser()).thenReturn(false);
shadowApplication.setSystemService(Context.USER_SERVICE, userManager);
assertFalse(mSigninManager.isSigninSupported(/* requireUpdatedPlayServices= */ true));
}
@Test
@EnableFeatures(SigninFeatures.SEED_ACCOUNTS_REVAMP)
public void
signInShouldNotBeSupportedWhenGooglePlayServicesIsRequiredAndNotAvailable_seedAccountsRevampEnabled() {
createSigninManager();
when(mExternalAuthUtils.canUseGooglePlayServices()).thenReturn(false);
// Make sure that the user is not a demo user.
ShadowApplication shadowApplication = ShadowApplication.getInstance();
UserManager userManager = Mockito.mock(UserManager.class);
Mockito.when(userManager.isDemoUser()).thenReturn(false);
shadowApplication.setSystemService(Context.USER_SERVICE, userManager);
assertFalse(mSigninManager.isSigninSupported(/* requireUpdatedPlayServices= */ true));
}
private void createSigninManager() {
mSigninManager =
(SigninManagerImpl)
SigninManagerImpl.create(
NATIVE_SIGNIN_MANAGER,
mProfile,
mAccountTrackerService,
mIdentityManager,
mIdentityMutator,
mSyncService);
mSigninManager.addSignInStateObserver(mSignInStateObserver);
}
}