1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.settings.accounts; 17 18 import static com.google.common.truth.Truth.assertThat; 19 20 import static org.mockito.Answers.RETURNS_DEEP_STUBS; 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.ArgumentMatchers.eq; 24 import static org.mockito.ArgumentMatchers.nullable; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.never; 27 import static org.mockito.Mockito.verify; 28 import static org.mockito.Mockito.when; 29 30 import android.accounts.Account; 31 import android.accounts.AccountManager; 32 import android.accounts.AccountManagerCallback; 33 import android.accounts.AccountManagerFuture; 34 import android.accounts.AuthenticatorDescription; 35 import android.accounts.AuthenticatorException; 36 import android.accounts.OperationCanceledException; 37 import android.app.Activity; 38 import android.content.Context; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.os.UserHandle; 42 43 import androidx.fragment.app.FragmentActivity; 44 import androidx.fragment.app.FragmentManager; 45 import androidx.fragment.app.FragmentTransaction; 46 import androidx.preference.PreferenceFragmentCompat; 47 import androidx.preference.PreferenceManager; 48 import androidx.preference.PreferenceScreen; 49 50 import com.android.settings.R; 51 import com.android.settings.testutils.shadow.ShadowAccountManager; 52 import com.android.settings.testutils.shadow.ShadowContentResolver; 53 import com.android.settings.testutils.shadow.ShadowDevicePolicyManager; 54 import com.android.settings.testutils.shadow.ShadowFragment; 55 import com.android.settings.testutils.shadow.ShadowUserManager; 56 import com.android.settings.widget.RestrictedButton; 57 import com.android.settingslib.widget.LayoutPreference; 58 59 import org.junit.After; 60 import org.junit.Before; 61 import org.junit.Test; 62 import org.junit.runner.RunWith; 63 import org.mockito.ArgumentCaptor; 64 import org.mockito.Mock; 65 import org.mockito.MockitoAnnotations; 66 import org.robolectric.Robolectric; 67 import org.robolectric.RobolectricTestRunner; 68 import org.robolectric.RuntimeEnvironment; 69 import org.robolectric.annotation.Config; 70 import org.robolectric.shadows.ShadowApplication; 71 72 import java.io.IOException; 73 74 @RunWith(RobolectricTestRunner.class) 75 @Config(shadows = { 76 ShadowUserManager.class, 77 ShadowDevicePolicyManager.class, 78 ShadowFragment.class, 79 }) 80 public class RemoveAccountPreferenceControllerTest { 81 82 private static final String KEY_REMOVE_ACCOUNT = "remove_account"; 83 private static final String TAG_REMOVE_ACCOUNT_DIALOG = "confirmRemoveAccount"; 84 85 @Mock(answer = RETURNS_DEEP_STUBS) 86 private AccountManager mAccountManager; 87 @Mock 88 private PreferenceFragmentCompat mFragment; 89 @Mock 90 private PreferenceManager mPreferenceManager; 91 @Mock 92 private PreferenceScreen mScreen; 93 @Mock 94 private FragmentManager mFragmentManager; 95 @Mock 96 private FragmentTransaction mFragmentTransaction; 97 @Mock 98 private LayoutPreference mPreference; 99 100 private RemoveAccountPreferenceController mController; 101 102 @Before setUp()103 public void setUp() { 104 MockitoAnnotations.initMocks(this); 105 ShadowApplication.getInstance().setSystemService(Context.ACCOUNT_SERVICE, mAccountManager); 106 107 when(mFragment.getPreferenceScreen()).thenReturn(mScreen); 108 when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager); 109 when(mPreferenceManager.getContext()).thenReturn(RuntimeEnvironment.application); 110 when(mFragment.getFragmentManager()).thenReturn(mFragmentManager); 111 when(mFragmentManager.beginTransaction()).thenReturn(mFragmentTransaction); 112 when(mAccountManager.getAuthenticatorTypesAsUser(anyInt())) 113 .thenReturn(new AuthenticatorDescription[0]); 114 when(mAccountManager.getAccountsAsUser(anyInt())).thenReturn(new Account[0]); 115 mController = new RemoveAccountPreferenceController( 116 Robolectric.setupActivity(Activity.class), mFragment); 117 } 118 119 @After tearDown()120 public void tearDown() { 121 ShadowContentResolver.reset(); 122 } 123 124 @Test displayPreference_shouldAddClickListener()125 public void displayPreference_shouldAddClickListener() { 126 when(mScreen.findPreference(KEY_REMOVE_ACCOUNT)).thenReturn(mPreference); 127 final RestrictedButton button = mock(RestrictedButton.class); 128 when(mPreference.findViewById(R.id.button)).thenReturn(button); 129 130 mController.displayPreference(mScreen); 131 132 verify(button).setOnClickListener(mController); 133 } 134 135 @Test onClick_shouldStartConfirmDialog()136 public void onClick_shouldStartConfirmDialog() { 137 when(mFragment.isAdded()).thenReturn(true); 138 mController.onClick(null); 139 140 verify(mFragmentTransaction).add( 141 any(RemoveAccountPreferenceController.ConfirmRemoveAccountDialog.class), 142 eq(TAG_REMOVE_ACCOUNT_DIALOG)); 143 } 144 145 @Test 146 @Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class, 147 ShadowFragment.class}) confirmRemove_shouldRemoveAccount()148 public void confirmRemove_shouldRemoveAccount() 149 throws AuthenticatorException, OperationCanceledException, IOException { 150 when(mFragment.isAdded()).thenReturn(true); 151 FragmentActivity activity = mock(FragmentActivity.class); 152 when(activity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager); 153 when(mFragment.getActivity()).thenReturn(activity); 154 155 Account account = new Account("Account11", "com.acct1"); 156 UserHandle userHandle = new UserHandle(10); 157 RemoveAccountPreferenceController.ConfirmRemoveAccountDialog dialog = 158 RemoveAccountPreferenceController.ConfirmRemoveAccountDialog.show( 159 mFragment, account, userHandle); 160 dialog.onCreate(new Bundle()); 161 dialog.onClick(null, 0); 162 ArgumentCaptor<AccountManagerCallback<Bundle>> callbackCaptor = ArgumentCaptor.forClass( 163 AccountManagerCallback.class); 164 verify(mAccountManager).removeAccountAsUser(eq(account), nullable(Activity.class), 165 callbackCaptor.capture(), nullable(Handler.class), eq(userHandle)); 166 167 AccountManagerCallback<Bundle> callback = callbackCaptor.getValue(); 168 assertThat(callback).isNotNull(); 169 AccountManagerFuture<Bundle> future = mock(AccountManagerFuture.class); 170 Bundle resultBundle = new Bundle(); 171 resultBundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); 172 when(future.getResult()).thenReturn(resultBundle); 173 174 callback.run(future); 175 verify(activity).finish(); 176 } 177 178 @Test 179 @Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class, 180 ShadowFragment.class}) confirmRemove_activityGone_shouldSilentlyRemoveAccount()181 public void confirmRemove_activityGone_shouldSilentlyRemoveAccount() 182 throws AuthenticatorException, OperationCanceledException, IOException { 183 final Account account = new Account("Account11", "com.acct1"); 184 final UserHandle userHandle = new UserHandle(10); 185 final FragmentActivity activity = mock(FragmentActivity.class); 186 when(mFragment.isAdded()).thenReturn(true); 187 when(activity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager); 188 when(mFragment.getActivity()).thenReturn(activity).thenReturn(null); 189 190 final RemoveAccountPreferenceController.ConfirmRemoveAccountDialog dialog = 191 RemoveAccountPreferenceController.ConfirmRemoveAccountDialog.show( 192 mFragment, account, userHandle); 193 dialog.onCreate(new Bundle()); 194 dialog.onClick(null, 0); 195 196 ArgumentCaptor<AccountManagerCallback<Bundle>> callbackCaptor = ArgumentCaptor.forClass( 197 AccountManagerCallback.class); 198 verify(mAccountManager).removeAccountAsUser(eq(account), nullable(Activity.class), 199 callbackCaptor.capture(), nullable(Handler.class), eq(userHandle)); 200 201 AccountManagerCallback<Bundle> callback = callbackCaptor.getValue(); 202 assertThat(callback).isNotNull(); 203 AccountManagerFuture<Bundle> future = mock(AccountManagerFuture.class); 204 Bundle resultBundle = new Bundle(); 205 resultBundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); 206 when(future.getResult()).thenReturn(resultBundle); 207 208 callback.run(future); 209 verify(activity, never()).finish(); 210 } 211 } 212