1/* 2 * Copyright (C) 2015 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 17package androidx.appcompat.app; 18 19import static android.support.test.espresso.Espresso.onView; 20import static android.support.test.espresso.matcher.ViewMatchers.withId; 21 22import static androidx.appcompat.testutils.TestUtilsActions.setSystemUiVisibility; 23 24import static org.junit.Assert.assertEquals; 25import static org.junit.Assert.assertNotNull; 26import static org.junit.Assert.assertNull; 27import static org.junit.Assert.assertTrue; 28import static org.mockito.Matchers.any; 29import static org.mockito.Matchers.eq; 30import static org.mockito.Mockito.atLeastOnce; 31import static org.mockito.Mockito.mock; 32import static org.mockito.Mockito.never; 33import static org.mockito.Mockito.reset; 34import static org.mockito.Mockito.spy; 35import static org.mockito.Mockito.verify; 36import static org.mockito.Mockito.verifyNoMoreInteractions; 37import static org.mockito.Mockito.when; 38 39import android.annotation.SuppressLint; 40import android.app.Activity; 41import android.content.pm.PackageManager; 42import android.support.test.annotation.UiThreadTest; 43import android.support.test.filters.SdkSuppress; 44import android.support.test.filters.SmallTest; 45import android.support.test.rule.ActivityTestRule; 46import android.support.test.runner.AndroidJUnit4; 47import android.view.Menu; 48import android.view.View; 49import android.view.WindowInsets; 50 51import androidx.annotation.RequiresApi; 52import androidx.appcompat.custom.FitWindowsContentLayout; 53import androidx.appcompat.test.R; 54import androidx.appcompat.testutils.BaseTestActivity; 55import androidx.appcompat.view.ActionMode; 56 57import org.junit.Rule; 58import org.junit.Test; 59import org.junit.runner.RunWith; 60 61@SmallTest 62@RunWith(AndroidJUnit4.class) 63public abstract class BaseBasicsTestCase<A extends BaseTestActivity> { 64 @Rule 65 public final ActivityTestRule<A> mActivityTestRule; 66 67 68 protected BaseBasicsTestCase(Class<A> activityClass) { 69 mActivityTestRule = new ActivityTestRule<>(activityClass); 70 } 71 72 @Test 73 public void testActionBarExists() { 74 assertNotNull("ActionBar is not null", 75 mActivityTestRule.getActivity().getSupportActionBar()); 76 } 77 78 @Test 79 public void testDefaultActionBarTitle() { 80 assertEquals(mActivityTestRule.getActivity().getTitle(), 81 mActivityTestRule.getActivity().getSupportActionBar().getTitle()); 82 } 83 84 @UiThreadTest 85 @Test 86 public void testSetActionBarTitle() { 87 final String newTitle = "hello"; 88 mActivityTestRule.getActivity().setTitle(newTitle); 89 assertEquals("New title is set to ActionBar", 90 newTitle, mActivityTestRule.getActivity().getSupportActionBar().getTitle()); 91 } 92 93 @Test 94 @SdkSuppress(minSdkVersion = 16) 95 @RequiresApi(16) 96 public void testFitSystemWindowsReachesContent() { 97 final FitWindowsContentLayout content = 98 mActivityTestRule.getActivity().findViewById(R.id.test_content); 99 assertNotNull(content); 100 101 if (!canShowSystemUi(mActivityTestRule.getActivity())) { 102 // Device cannot show system UI so setSystemUiVisibility will do nothing. 103 return; 104 } 105 106 // Call setSystemUiVisibility with flags which will cause window insets to be dispatched 107 final int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; 108 onView(withId(R.id.test_content)).perform(setSystemUiVisibility(flags)); 109 110 assertTrue(content.getFitsSystemWindowsCalled()); 111 } 112 113 @Test 114 @SdkSuppress(minSdkVersion = 21) 115 @RequiresApi(21) 116 public void testOnApplyWindowInsetsReachesContent() { 117 final View content = mActivityTestRule.getActivity().findViewById(R.id.test_content); 118 assertNotNull(content); 119 120 if (!canShowSystemUi(mActivityTestRule.getActivity())) { 121 // Device cannot show system UI so setSystemUiVisibility will do nothing. 122 return; 123 } 124 125 // Create a spy of one of our test listener and set it on our content 126 final View.OnApplyWindowInsetsListener spyListener 127 = spy(new TestOnApplyWindowInsetsListener()); 128 content.setOnApplyWindowInsetsListener(spyListener); 129 130 // Call setSystemUiVisibility with flags which will cause window insets to be dispatched 131 final int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; 132 onView(withId(R.id.test_content)).perform(setSystemUiVisibility(flags)); 133 134 // Verify that the listener was called at least once 135 verify(spyListener, atLeastOnce()) 136 .onApplyWindowInsets(eq(content), any(WindowInsets.class)); 137 } 138 139 @Test 140 @UiThreadTest 141 public void testSupportActionModeCallbacks() { 142 final A activity = mActivityTestRule.getActivity(); 143 144 // Create a mock action mode callback which returns true from onCreateActionMode 145 final ActionMode.Callback callback = mock(ActionMode.Callback.class); 146 when(callback.onCreateActionMode(any(ActionMode.class), any(Menu.class))).thenReturn(true); 147 148 // Start an action mode 149 final ActionMode actionMode = activity.startSupportActionMode(callback); 150 assertNotNull(actionMode); 151 152 // Now verify that onCreateActionMode and onPrepareActionMode are called once 153 verify(callback).onCreateActionMode(any(ActionMode.class), any(Menu.class)); 154 verify(callback).onPrepareActionMode(any(ActionMode.class), any(Menu.class)); 155 156 // Now finish and verify that onDestroyActionMode is called once, and there are no more 157 // interactions 158 actionMode.finish(); 159 verify(callback).onDestroyActionMode(any(ActionMode.class)); 160 verifyNoMoreInteractions(callback); 161 } 162 163 @Test 164 @UiThreadTest 165 public void testSupportActionModeCallbacksInvalidate() { 166 final A activity = mActivityTestRule.getActivity(); 167 168 // Create a mock action mode callback which returns true from onCreateActionMode 169 final ActionMode.Callback callback = mock(ActionMode.Callback.class); 170 when(callback.onCreateActionMode(any(ActionMode.class), any(Menu.class))).thenReturn(true); 171 172 // Start an action mode 173 final ActionMode actionMode = activity.startSupportActionMode(callback); 174 // Assert that one was created 175 assertNotNull(actionMode); 176 // Reset the mock so that any callback counts from the create are reset 177 reset(callback); 178 179 // Now invalidate the action mode 180 actionMode.invalidate(); 181 182 // Now verify that onCreateActionMode is not called, and onPrepareActionMode is called once 183 verify(callback, never()).onCreateActionMode(any(ActionMode.class), any(Menu.class)); 184 verify(callback).onPrepareActionMode(any(ActionMode.class), any(Menu.class)); 185 } 186 187 @Test 188 @UiThreadTest 189 public void testSupportActionModeCallbacksWithFalseOnCreate() { 190 final A activity = mActivityTestRule.getActivity(); 191 192 // Create a mock action mode callback which returns true from onCreateActionMode 193 final ActionMode.Callback callback = mock(ActionMode.Callback.class); 194 when(callback.onCreateActionMode(any(ActionMode.class), any(Menu.class))).thenReturn(false); 195 196 // Start an action mode 197 final ActionMode actionMode = activity.startSupportActionMode(callback); 198 199 // Now verify that onCreateActionMode is called once 200 verify(callback).onCreateActionMode(any(ActionMode.class), any(Menu.class)); 201 202 // Now verify that onPrepareActionMode is not called (since onCreateActionMode 203 // returns false) 204 verify(callback, never()).onPrepareActionMode(any(ActionMode.class), any(Menu.class)); 205 206 // Assert that an action mode was not created 207 assertNull(actionMode); 208 } 209 210 @SuppressWarnings("deprecation") 211 @SuppressLint("InlinedApi") 212 private static boolean canShowSystemUi(Activity activity) { 213 PackageManager manager = activity.getPackageManager(); 214 return !manager.hasSystemFeature(PackageManager.FEATURE_TELEVISION) 215 && !manager.hasSystemFeature(PackageManager.FEATURE_LEANBACK) 216 && !manager.hasSystemFeature(PackageManager.FEATURE_WATCH); 217 } 218 219 protected void testSupportActionModeAppCompatCallbacks(final boolean fromWindow) { 220 final A activity = mActivityTestRule.getActivity(); 221 222 // Create a mock action mode callback which returns true from onCreateActionMode 223 final ActionMode.Callback amCallback = mock(ActionMode.Callback.class); 224 when(amCallback.onCreateActionMode(any(ActionMode.class), any(Menu.class))) 225 .thenReturn(true); 226 227 // Create a mock AppCompatCallback, which returns null from 228 // onWindowStartingSupportActionMode, and set it on the Activity 229 final AppCompatCallback apCallback = mock(AppCompatCallback.class); 230 when(apCallback.onWindowStartingSupportActionMode(any(ActionMode.Callback.class))) 231 .thenReturn(null); 232 activity.setAppCompatCallback(apCallback); 233 234 // Start an action mode with the action mode callback 235 final ActionMode actionMode = activity.startSupportActionMode(amCallback); 236 237 if (fromWindow) { 238 // Verify that the callback's onWindowStartingSupportActionMode was called 239 verify(apCallback).onWindowStartingSupportActionMode(any(ActionMode.Callback.class)); 240 } 241 242 // Now assert that an action mode was created 243 assertNotNull(actionMode); 244 245 // Now verify that onSupportActionModeStarted is called once 246 verify(apCallback).onSupportActionModeStarted(any(ActionMode.class)); 247 248 // Now finish and verify that onDestroyActionMode is called once 249 actionMode.finish(); 250 verify(apCallback).onSupportActionModeFinished(any(ActionMode.class)); 251 } 252 253 public static class TestOnApplyWindowInsetsListener 254 implements View.OnApplyWindowInsetsListener { 255 @Override 256 public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { 257 return windowInsets; 258 } 259 } 260} 261