1 /* 2 * Copyright (C) 2018 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 17 package com.android.settings.users; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.eq; 23 import static org.mockito.ArgumentMatchers.same; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.spy; 26 import static org.mockito.Mockito.times; 27 import static org.mockito.Mockito.verify; 28 import static org.mockito.Mockito.when; 29 30 import android.app.Dialog; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.pm.UserInfo; 34 import android.graphics.drawable.Drawable; 35 import android.widget.EditText; 36 import android.widget.ImageView; 37 38 import androidx.appcompat.app.AlertDialog; 39 import androidx.fragment.app.Fragment; 40 import androidx.fragment.app.FragmentActivity; 41 42 import com.android.settings.R; 43 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; 44 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 import org.mockito.Answers; 49 import org.mockito.Mock; 50 import org.mockito.MockitoAnnotations; 51 import org.robolectric.RobolectricTestRunner; 52 import org.robolectric.android.controller.ActivityController; 53 import org.robolectric.annotation.Config; 54 55 import java.util.stream.Collectors; 56 import java.util.stream.Stream; 57 58 @RunWith(RobolectricTestRunner.class) 59 public class EditUserInfoControllerTest { 60 private static final int MAX_USER_NAME_LENGTH = 100; 61 62 @Mock 63 private Fragment mFragment; 64 @Mock 65 private Drawable mCurrentIcon; 66 67 private boolean mCanChangePhoto; 68 69 private FragmentActivity mActivity; 70 private TestEditUserInfoController mController; 71 72 public class TestEditUserInfoController extends EditUserInfoController { 73 private EditUserPhotoController mPhotoController; 74 getPhotoController()75 private EditUserPhotoController getPhotoController() { 76 return mPhotoController; 77 } 78 79 @Override createEditUserPhotoController(Fragment fragment, ImageView userPhotoView, Drawable drawable)80 protected EditUserPhotoController createEditUserPhotoController(Fragment fragment, 81 ImageView userPhotoView, Drawable drawable) { 82 mPhotoController = mock(EditUserPhotoController.class, Answers.RETURNS_DEEP_STUBS); 83 return mPhotoController; 84 } 85 86 @Override canChangePhoto(Context context, UserInfo user)87 boolean canChangePhoto(Context context, UserInfo user) { 88 return mCanChangePhoto; 89 } 90 } 91 92 @Before setUp()93 public void setUp() { 94 MockitoAnnotations.initMocks(this); 95 mActivity = spy(ActivityController.of(new FragmentActivity()).get()); 96 when(mFragment.getActivity()).thenReturn(mActivity); 97 mController = new TestEditUserInfoController(); 98 mCanChangePhoto = true; 99 } 100 101 @Test photoControllerOnActivityResult_whenWaiting_isCalled()102 public void photoControllerOnActivityResult_whenWaiting_isCalled() { 103 mController.createDialog(mFragment, mCurrentIcon, "test user", 104 "title", null, 105 android.os.Process.myUserHandle(), null); 106 mController.startingActivityForResult(); 107 Intent resultData = new Intent(); 108 mController.onActivityResult(0, 0, resultData); 109 EditUserPhotoController photoController = mController.getPhotoController(); 110 assertThat(photoController).isNotNull(); 111 verify(photoController).onActivityResult(eq(0), eq(0), same(resultData)); 112 } 113 114 @Test 115 @Config(shadows = ShadowAlertDialogCompat.class) userNameView_inputLongName_shouldBeConstrained()116 public void userNameView_inputLongName_shouldBeConstrained() { 117 // generate a string of 200 'A's 118 final String longName = Stream.generate( 119 () -> String.valueOf('A')).limit(200).collect(Collectors.joining()); 120 final AlertDialog dialog = (AlertDialog) mController.createDialog(mFragment, mCurrentIcon, 121 "test user", "title", null, 122 android.os.Process.myUserHandle(), null); 123 final EditText userName = ShadowAlertDialogCompat.shadowOf(dialog).getView() 124 .findViewById(R.id.user_name); 125 126 userName.setText(longName); 127 128 assertThat(userName.getText().length()).isEqualTo(MAX_USER_NAME_LENGTH); 129 } 130 131 @Test onDialogCompleteCallback_isCalled_whenCancelled()132 public void onDialogCompleteCallback_isCalled_whenCancelled() { 133 EditUserInfoController.OnContentChangedCallback contentChangeCallback = mock( 134 EditUserInfoController.OnContentChangedCallback.class); 135 136 EditUserInfoController.OnDialogCompleteCallback dialogCompleteCallback = mock( 137 EditUserInfoController.OnDialogCompleteCallback.class); 138 139 AlertDialog dialog = (AlertDialog) mController.createDialog( 140 mFragment, mCurrentIcon, "test", 141 "title", contentChangeCallback, 142 android.os.Process.myUserHandle(), 143 dialogCompleteCallback); 144 145 dialog.show(); 146 dialog.cancel(); 147 148 verify(contentChangeCallback, times(0)) 149 .onLabelChanged(any(), any()); 150 verify(contentChangeCallback, times(0)) 151 .onPhotoChanged(any(), any()); 152 verify(dialogCompleteCallback, times(0)).onPositive(); 153 verify(dialogCompleteCallback, times(1)).onNegativeOrCancel(); 154 } 155 156 @Test onDialogCompleteCallback_isCalled_whenPositiveClicked()157 public void onDialogCompleteCallback_isCalled_whenPositiveClicked() { 158 EditUserInfoController.OnContentChangedCallback contentChangeCallback = mock( 159 EditUserInfoController.OnContentChangedCallback.class); 160 161 EditUserInfoController.OnDialogCompleteCallback dialogCompleteCallback = mock( 162 EditUserInfoController.OnDialogCompleteCallback.class); 163 164 AlertDialog dialog = (AlertDialog) mController.createDialog( 165 mFragment, mCurrentIcon, "test", 166 "title", contentChangeCallback, 167 android.os.Process.myUserHandle(), 168 dialogCompleteCallback); 169 170 // No change to the photo. 171 when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(mCurrentIcon); 172 173 dialog.show(); 174 dialog.getButton(Dialog.BUTTON_POSITIVE).performClick(); 175 176 verify(contentChangeCallback, times(0)) 177 .onLabelChanged(any(), any()); 178 verify(contentChangeCallback, times(0)) 179 .onPhotoChanged(any(), any()); 180 verify(dialogCompleteCallback, times(1)).onPositive(); 181 verify(dialogCompleteCallback, times(0)).onNegativeOrCancel(); 182 } 183 184 @Test onDialogCompleteCallback_isCalled_whenNegativeClicked()185 public void onDialogCompleteCallback_isCalled_whenNegativeClicked() { 186 EditUserInfoController.OnContentChangedCallback contentChangeCallback = mock( 187 EditUserInfoController.OnContentChangedCallback.class); 188 189 EditUserInfoController.OnDialogCompleteCallback dialogCompleteCallback = mock( 190 EditUserInfoController.OnDialogCompleteCallback.class); 191 192 AlertDialog dialog = (AlertDialog) mController.createDialog( 193 mFragment, mCurrentIcon, "test", 194 "title", contentChangeCallback, 195 android.os.Process.myUserHandle(), 196 dialogCompleteCallback); 197 198 dialog.show(); 199 dialog.getButton(Dialog.BUTTON_NEGATIVE).performClick(); 200 201 verify(contentChangeCallback, times(0)) 202 .onLabelChanged(any(), any()); 203 verify(contentChangeCallback, times(0)) 204 .onPhotoChanged(any(), any()); 205 verify(dialogCompleteCallback, times(0)).onPositive(); 206 verify(dialogCompleteCallback, times(1)).onNegativeOrCancel(); 207 } 208 209 @Test onContentChangedCallback_isCalled_whenLabelChanges()210 public void onContentChangedCallback_isCalled_whenLabelChanges() { 211 EditUserInfoController.OnContentChangedCallback contentChangeCallback = mock( 212 EditUserInfoController.OnContentChangedCallback.class); 213 214 EditUserInfoController.OnDialogCompleteCallback dialogCompleteCallback = mock( 215 EditUserInfoController.OnDialogCompleteCallback.class); 216 217 AlertDialog dialog = (AlertDialog) mController.createDialog( 218 mFragment, mCurrentIcon, "test", 219 "title", contentChangeCallback, 220 android.os.Process.myUserHandle(), 221 dialogCompleteCallback); 222 223 // No change to the photo. 224 when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(mCurrentIcon); 225 226 dialog.show(); 227 String expectedNewName = "new test user"; 228 EditText editText = (EditText) dialog.findViewById(R.id.user_name); 229 editText.setText(expectedNewName); 230 231 dialog.getButton(Dialog.BUTTON_POSITIVE).performClick(); 232 233 verify(contentChangeCallback, times(1)) 234 .onLabelChanged(any(), eq(expectedNewName)); 235 verify(contentChangeCallback, times(0)) 236 .onPhotoChanged(any(), any()); 237 verify(dialogCompleteCallback, times(1)).onPositive(); 238 verify(dialogCompleteCallback, times(0)).onNegativeOrCancel(); 239 } 240 241 @Test onContentChangedCallback_isCalled_whenPhotoChanges()242 public void onContentChangedCallback_isCalled_whenPhotoChanges() { 243 EditUserInfoController.OnContentChangedCallback contentChangeCallback = mock( 244 EditUserInfoController.OnContentChangedCallback.class); 245 246 EditUserInfoController.OnDialogCompleteCallback dialogCompleteCallback = mock( 247 EditUserInfoController.OnDialogCompleteCallback.class); 248 249 AlertDialog dialog = (AlertDialog) mController.createDialog( 250 mFragment, mCurrentIcon, "test", 251 "title", contentChangeCallback, 252 android.os.Process.myUserHandle(), 253 dialogCompleteCallback); 254 255 // A different drawable. 256 Drawable newPhoto = mock(Drawable.class); 257 when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(newPhoto); 258 259 dialog.show(); 260 dialog.getButton(Dialog.BUTTON_POSITIVE).performClick(); 261 262 verify(contentChangeCallback, times(0)) 263 .onLabelChanged(any(), any()); 264 verify(contentChangeCallback, times(1)) 265 .onPhotoChanged(any(), eq(newPhoto)); 266 verify(dialogCompleteCallback, times(1)).onPositive(); 267 verify(dialogCompleteCallback, times(0)).onNegativeOrCancel(); 268 } 269 270 @Test createDialog_canNotChangePhoto_nullPhotoController()271 public void createDialog_canNotChangePhoto_nullPhotoController() { 272 mCanChangePhoto = false; 273 274 mController.createDialog( 275 mFragment, mCurrentIcon, "test", 276 "title", null, 277 android.os.Process.myUserHandle(), 278 null); 279 280 assertThat(mController.mPhotoController).isNull(); 281 } 282 } 283