1 /* 2 * Copyright (C) 2021 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.car.settings.common; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.ArgumentMatchers.anyString; 24 import static org.mockito.ArgumentMatchers.eq; 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.withSettings; 29 import static org.testng.Assert.assertThrows; 30 31 import android.car.drivingstate.CarUxRestrictions; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentSender; 35 import android.widget.Toast; 36 37 import androidx.fragment.app.DialogFragment; 38 import androidx.fragment.app.Fragment; 39 import androidx.fragment.app.FragmentManager; 40 import androidx.preference.Preference; 41 import androidx.test.annotation.UiThreadTest; 42 import androidx.test.core.app.ApplicationProvider; 43 import androidx.test.ext.junit.runners.AndroidJUnit4; 44 import androidx.test.platform.app.InstrumentationRegistry; 45 import androidx.test.rule.ActivityTestRule; 46 47 import com.android.car.settings.R; 48 import com.android.car.settings.testutils.BaseCarSettingsTestActivity; 49 import com.android.car.settings.testutils.TestFinishActivity; 50 import com.android.dx.mockito.inline.extended.ExtendedMockito; 51 52 import org.junit.Before; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 import org.mockito.MockitoAnnotations; 56 import org.mockito.MockitoSession; 57 58 import java.util.concurrent.atomic.AtomicReference; 59 60 @RunWith(AndroidJUnit4.class) 61 public abstract class SettingsFragmentTestCase<T extends BaseCarSettingsTestActivity> { 62 protected static final String TEST_TAG = "test_tag"; 63 64 protected Context mContext = ApplicationProvider.getApplicationContext(); 65 protected BaseCarSettingsTestActivity mActivity; 66 protected FragmentManager mFragmentManager; 67 protected SettingsFragment mFragment; 68 getActivityTestRule()69 abstract ActivityTestRule<T> getActivityTestRule(); 70 71 @Before setUp()72 public void setUp() throws Throwable { 73 MockitoAnnotations.initMocks(this); 74 mActivity = getActivityTestRule().getActivity(); 75 mFragmentManager = mActivity.getSupportFragmentManager(); 76 setUpFragment(); 77 } 78 79 @Test 80 @UiThreadTest use_returnsController()81 public void use_returnsController() { 82 mFragment.onCreate(null); 83 assertThat(mFragment.use(FakePreferenceController.class, 84 R.string.tpk_fake_controller)).isNotNull(); 85 } 86 87 @Test onAttach_registersLifecycleObservers()88 public void onAttach_registersLifecycleObservers() throws Throwable { 89 AtomicReference<FakePreferenceController> controller = new AtomicReference<>(); 90 getActivityTestRule().runOnUiThread(() -> { 91 mFragment.onCreate(null); 92 controller.set(mFragment.use(FakePreferenceController.class, 93 R.string.tpk_fake_controller)); 94 assertThat(controller.get().getOnCreateInternalCallCount()).isEqualTo(1); 95 }); 96 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 97 getActivityTestRule().finishActivity(); 98 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 99 assertThat(controller.get().getOnCreateInternalCallCount()).isEqualTo(1); 100 } 101 102 @Test 103 @UiThreadTest onUxRestrictionsChanged_propagatesToControllers()104 public void onUxRestrictionsChanged_propagatesToControllers() { 105 mFragment.onCreate(null); 106 FakePreferenceController controller = mFragment.use(FakePreferenceController.class, 107 R.string.tpk_fake_controller); 108 CarUxRestrictions uxRestrictions = new CarUxRestrictions.Builder(/* reqOpt= */ true, 109 CarUxRestrictions.UX_RESTRICTIONS_NO_KEYBOARD, /* timestamp= */ 0).build(); 110 mFragment.onUxRestrictionsChanged(uxRestrictions); 111 assertThat(controller.getUxRestrictions()).isEqualTo(uxRestrictions); 112 } 113 114 @Test 115 @UiThreadTest onUxRestrictedPreferenceTapped_showToast()116 public void onUxRestrictedPreferenceTapped_showToast() { 117 MockitoSession session = ExtendedMockito.mockitoSession().mockStatic(Toast.class, 118 withSettings().lenient()).startMocking(); 119 Toast mockToast = mock(Toast.class); 120 ExtendedMockito.when(Toast.makeText(any(), anyString(), anyInt())) 121 .thenReturn(mockToast); 122 123 mFragment.onCreate(null); 124 FakePreferenceController controller = mFragment.use(FakePreferenceController.class, 125 R.string.tpk_fake_controller); 126 CarUxRestrictions uxRestrictions = new CarUxRestrictions.Builder(/* reqOpt= */ true, 127 CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP, /* timestamp= */ 0).build(); 128 mFragment.onUxRestrictionsChanged(uxRestrictions); 129 controller.getPreference().performClick(); 130 verify(mockToast).show(); 131 132 session.finishMocking(); 133 } 134 135 @Test 136 @UiThreadTest onDisplayPreferenceDialog_unknownPreferenceType_throwsIllegalArgumentException()137 public void onDisplayPreferenceDialog_unknownPreferenceType_throwsIllegalArgumentException() { 138 mFragment.onCreate(null); 139 140 assertThrows(IllegalArgumentException.class, 141 () -> mFragment.onDisplayPreferenceDialog(new Preference(mContext))); 142 } 143 144 @Test 145 @UiThreadTest launchFragment_dialogFragment_throwsError()146 public void launchFragment_dialogFragment_throwsError() { 147 mFragment.onCreate(null); 148 DialogFragment dialogFragment = new DialogFragment(); 149 assertThrows(IllegalArgumentException.class, 150 () -> mFragment.launchFragment(dialogFragment)); 151 } 152 153 @Test 154 @UiThreadTest showDialog_noTag_launchesDialogFragment()155 public void showDialog_noTag_launchesDialogFragment() { 156 mFragment.onCreate(null); 157 DialogFragment dialogFragment = mock(DialogFragment.class); 158 mFragment.showDialog(dialogFragment, /* tag= */ null); 159 verify(dialogFragment).show(mFragment.getFragmentManager(), null); 160 } 161 162 @Test 163 @UiThreadTest showDialog_withTag_launchesDialogFragment()164 public void showDialog_withTag_launchesDialogFragment() { 165 mFragment.onCreate(null); 166 DialogFragment dialogFragment = mock(DialogFragment.class); 167 mFragment.showDialog(dialogFragment, TEST_TAG); 168 verify(dialogFragment).show(mFragment.getFragmentManager(), TEST_TAG); 169 } 170 171 @Test findDialogByTag_retrieveOriginalDialog_returnsDialog()172 public void findDialogByTag_retrieveOriginalDialog_returnsDialog() throws Throwable { 173 DialogFragment dialogFragment = new DialogFragment(); 174 getActivityTestRule().runOnUiThread(() -> { 175 mFragment.onCreate(null); 176 mFragment.showDialog(dialogFragment, TEST_TAG); 177 }); 178 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 179 assertThat(mFragment.findDialogByTag(TEST_TAG)).isEqualTo(dialogFragment); 180 } 181 182 @Test 183 @UiThreadTest findDialogByTag_notDialogFragment_returnsNull()184 public void findDialogByTag_notDialogFragment_returnsNull() { 185 mFragment.onCreate(null); 186 TestSettingsFragment 187 fragment = new TestSettingsFragment(); 188 mFragment.getFragmentManager().beginTransaction().add(fragment, TEST_TAG).commit(); 189 assertThat(mFragment.findDialogByTag(TEST_TAG)).isNull(); 190 } 191 192 @Test 193 @UiThreadTest findDialogByTag_noSuchFragment_returnsNull()194 public void findDialogByTag_noSuchFragment_returnsNull() { 195 mFragment.onCreate(null); 196 assertThat(mFragment.findDialogByTag(TEST_TAG)).isNull(); 197 } 198 199 @Test 200 @UiThreadTest startActivityForResult_largeRequestCode_throwsError()201 public void startActivityForResult_largeRequestCode_throwsError() { 202 mFragment.onCreate(null); 203 assertThrows(() -> mFragment.startActivityForResult(new Intent(), 0xffff, 204 mock(ActivityResultCallback.class))); 205 } 206 207 @Test 208 @UiThreadTest startActivityForResult_tooManyRequests_throwsError()209 public void startActivityForResult_tooManyRequests_throwsError() { 210 mFragment.onCreate(null); 211 assertThrows(() -> { 212 for (int i = 0; i < 0xff; i++) { 213 mFragment.startActivityForResult(new Intent(), i, 214 mock(ActivityResultCallback.class)); 215 } 216 }); 217 } 218 219 @Test 220 @UiThreadTest startIntentSenderForResult_largeRequestCode_throwsError()221 public void startIntentSenderForResult_largeRequestCode_throwsError() { 222 mFragment.onCreate(null); 223 assertThrows( 224 () -> mFragment.startIntentSenderForResult( 225 mock(IntentSender.class), /* requestCode= */ 0xffff, 226 /* fillInIntent= */ null, /* flagsMask= */ 0, 227 /* flagsValues= */ 0, /* options= */ null, 228 mock(ActivityResultCallback.class))); 229 } 230 231 @Test 232 @UiThreadTest startIntentSenderForResult_tooManyRequests_throwsError()233 public void startIntentSenderForResult_tooManyRequests_throwsError() { 234 mFragment.onCreate(null); 235 assertThrows(() -> { 236 for (int i = 0; i < 0xff; i++) { 237 mFragment.startIntentSenderForResult( 238 mock(IntentSender.class), /* requestCode= */ 0xffff, 239 /* fillInIntent= */ null, /* flagsMask= */ 0, 240 /* flagsValues= */ 0, /* options= */ null, 241 mock(ActivityResultCallback.class)); 242 } 243 }); 244 } 245 246 @Test 247 @UiThreadTest onActivityResult_hasValidRequestCode_triggersOnActivityResult()248 public void onActivityResult_hasValidRequestCode_triggersOnActivityResult() { 249 mFragment.onCreate(null); 250 ActivityResultCallback callback = mock(ActivityResultCallback.class); 251 Intent intent = new Intent(mContext, TestFinishActivity.class); 252 253 int reqCode = 100; 254 int resCode = -1; 255 mFragment.startActivityForResult(intent, reqCode, callback); 256 int fragmentReqCode = (1 << 8) + reqCode; 257 mFragment.onActivityResult(fragmentReqCode, resCode, intent); 258 verify(callback).processActivityResult(eq(reqCode), eq(resCode), any(Intent.class)); 259 } 260 261 @Test 262 @UiThreadTest onActivityResult_wrongRequestCode_doesntTriggerOnActivityResult()263 public void onActivityResult_wrongRequestCode_doesntTriggerOnActivityResult() { 264 mFragment.onCreate(null); 265 ActivityResultCallback callback = mock(ActivityResultCallback.class); 266 Intent intent = new Intent(mContext, TestFinishActivity.class); 267 268 int reqCode = 100; 269 int resCode = -1; 270 mFragment.startActivityForResult(intent, reqCode, callback); 271 int fragmentReqCode = (2 << 8) + reqCode; 272 mFragment.onActivityResult(fragmentReqCode, resCode, intent); 273 verify(callback, never()).processActivityResult(anyInt(), anyInt(), 274 any(Intent.class)); 275 } 276 setUpFragment()277 protected void setUpFragment() throws Throwable { 278 setUpFragment(null); 279 } 280 setUpFragment(Fragment fragment)281 protected void setUpFragment(Fragment fragment) throws Throwable { 282 String settingsFragmentTag = "settings_fragment"; 283 getActivityTestRule().runOnUiThread(() -> { 284 mFragmentManager.beginTransaction() 285 .replace(R.id.fragment_container, 286 fragment != null ? fragment : new TestSettingsFragment(), 287 settingsFragmentTag) 288 .commitNow(); 289 }); 290 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 291 mFragment = (SettingsFragment) mFragmentManager.findFragmentByTag(settingsFragmentTag); 292 } 293 294 /** Concrete {@link SettingsFragment} for testing. */ 295 public static class TestSettingsFragment extends SettingsFragment { 296 297 @Override getPreferenceScreenResId()298 protected int getPreferenceScreenResId() { 299 return R.xml.test_base_settings_fragment; 300 } 301 302 } 303 } 304