BaseKeyEventsTestCase.java revision 8d3b808ab4720ef4e9d58f2bba4e31f741d3898c
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 android.support.v7.app; 18 19import static android.support.test.espresso.Espresso.onView; 20import static android.support.test.espresso.action.ViewActions.click; 21import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist; 22import static android.support.test.espresso.assertion.ViewAssertions.matches; 23import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; 24import static android.support.test.espresso.matcher.ViewMatchers.withClassName; 25import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription; 26import static android.support.test.espresso.matcher.ViewMatchers.withId; 27 28import static org.junit.Assert.assertEquals; 29import static org.junit.Assert.assertFalse; 30import static org.junit.Assert.assertNotNull; 31import static org.junit.Assert.assertTrue; 32 33import android.app.Instrumentation; 34import android.support.test.InstrumentationRegistry; 35import android.support.test.filters.LargeTest; 36import android.support.test.filters.SmallTest; 37import android.support.test.rule.ActivityTestRule; 38import android.support.test.runner.AndroidJUnit4; 39import android.support.v4.view.MenuItemCompat; 40import android.support.v7.appcompat.test.R; 41import android.support.v7.testutils.BaseTestActivity; 42import android.support.v7.view.ActionMode; 43import android.view.KeyEvent; 44import android.view.Menu; 45import android.view.MenuItem; 46 47import org.hamcrest.Matchers; 48import org.junit.Before; 49import org.junit.Rule; 50import org.junit.Test; 51import org.junit.runner.RunWith; 52 53import java.util.concurrent.atomic.AtomicBoolean; 54 55@RunWith(AndroidJUnit4.class) 56public abstract class BaseKeyEventsTestCase<A extends BaseTestActivity> { 57 @Rule 58 public final ActivityTestRule<A> mActivityTestRule; 59 60 private Instrumentation mInstrumentation; 61 private A mActivity; 62 63 protected BaseKeyEventsTestCase(Class<A> activityClass) { 64 mActivityTestRule = new ActivityTestRule<>(activityClass); 65 } 66 67 @Before 68 public void setup() { 69 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 70 mActivity = mActivityTestRule.getActivity(); 71 } 72 73 @Test 74 @SmallTest 75 public void testBackDismissesActionMode() { 76 final AtomicBoolean destroyed = new AtomicBoolean(); 77 78 mActivity.runOnUiThread(new Runnable() { 79 @Override 80 public void run() { 81 mActivity.startSupportActionMode(new ActionMode.Callback() { 82 @Override 83 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 84 mode.getMenuInflater().inflate(R.menu.sample_actions, menu); 85 return true; 86 } 87 88 @Override 89 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 90 return false; 91 } 92 93 @Override 94 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 95 return false; 96 } 97 98 @Override 99 public void onDestroyActionMode(ActionMode mode) { 100 destroyed.set(true); 101 } 102 }); 103 } 104 }); 105 106 mInstrumentation.waitForIdleSync(); 107 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK); 108 mInstrumentation.waitForIdleSync(); 109 110 assertFalse("Activity was not finished", mActivity.isFinishing()); 111 assertTrue("ActionMode was destroyed", destroyed.get()); 112 } 113 114 @Test 115 @LargeTest 116 public void testBackCollapsesActionView() throws InterruptedException { 117 // Click on the Search menu item 118 onView(withId(R.id.action_search)).perform(click()); 119 // Check that the action view is displayed (expanded) 120 onView(withClassName(Matchers.is(CustomCollapsibleView.class.getName()))) 121 .check(matches(isDisplayed())); 122 123 // Let things settle 124 mInstrumentation.waitForIdleSync(); 125 // Now send a back event to collapse the custom action view 126 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK); 127 mInstrumentation.waitForIdleSync(); 128 129 // Check that the Activity is still running 130 assertFalse(mActivity.isFinishing()); 131 assertFalse(mActivity.isDestroyed()); 132 // ... and that our action view is not attached 133 onView(withClassName(Matchers.is(CustomCollapsibleView.class.getName()))) 134 .check(doesNotExist()); 135 } 136 137 @Test 138 @SmallTest 139 public void testMenuPressInvokesPanelCallbacks() throws InterruptedException { 140 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU); 141 mInstrumentation.waitForIdleSync(); 142 assertTrue("onMenuOpened called", mActivity.wasOnMenuOpenedCalled()); 143 144 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU); 145 mInstrumentation.waitForIdleSync(); 146 assertTrue("onPanelClosed called", mActivity.wasOnPanelClosedCalled()); 147 } 148 149 @Test 150 @SmallTest 151 public void testBackPressWithMenuInvokesOnPanelClosed() throws InterruptedException { 152 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU); 153 mInstrumentation.waitForIdleSync(); 154 155 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK); 156 mInstrumentation.waitForIdleSync(); 157 assertTrue("onPanelClosed called", mActivity.wasOnPanelClosedCalled()); 158 } 159 160 @Test 161 @SmallTest 162 public void testBackPressWithEmptyMenuFinishesActivity() throws InterruptedException { 163 repopulateWithEmptyMenu(); 164 165 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU); 166 mInstrumentation.waitForIdleSync(); 167 168 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK); 169 assertTrue(mActivity.isFinishing()); 170 } 171 172 @Test 173 @SmallTest 174 public void testDelKeyEventReachesActivity() { 175 // First send the event 176 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DEL); 177 mInstrumentation.waitForIdleSync(); 178 179 KeyEvent downEvent = mActivity.getInvokedKeyDownEvent(); 180 assertNotNull("onKeyDown called", downEvent); 181 assertEquals("onKeyDown event matches", KeyEvent.KEYCODE_DEL, downEvent.getKeyCode()); 182 183 KeyEvent upEvent = mActivity.getInvokedKeyUpEvent(); 184 assertNotNull("onKeyUp called", upEvent); 185 assertEquals("onKeyUp event matches", KeyEvent.KEYCODE_DEL, upEvent.getKeyCode()); 186 } 187 188 @Test 189 @SmallTest 190 public void testMenuKeyEventReachesActivity() throws InterruptedException { 191 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU); 192 mInstrumentation.waitForIdleSync(); 193 194 KeyEvent downEvent = mActivity.getInvokedKeyDownEvent(); 195 assertNotNull("onKeyDown called", downEvent); 196 assertEquals("onKeyDown event matches", KeyEvent.KEYCODE_MENU, downEvent.getKeyCode()); 197 198 KeyEvent upEvent = mActivity.getInvokedKeyUpEvent(); 199 assertNotNull("onKeyUp called", upEvent); 200 assertEquals("onKeyDown event matches", KeyEvent.KEYCODE_MENU, upEvent.getKeyCode()); 201 } 202 203 @Test 204 @SmallTest 205 public void testActionMenuContent() throws Throwable { 206 onView(withId(R.id.action_search)) 207 .check(matches(isDisplayed())) 208 .check(matches(withContentDescription(R.string.search_menu_description))); 209 210 onView(withId(R.id.action_alpha_shortcut)) 211 .check(matches(isDisplayed())) 212 .check(matches(withContentDescription((String) null))); 213 214 Menu menu = mActivity.getMenu(); 215 final MenuItem alphaItem = menu.findItem(R.id.action_alpha_shortcut); 216 assertNotNull(alphaItem); 217 218 mActivityTestRule.runOnUiThread(new Runnable() { 219 @Override 220 public void run() { 221 MenuItemCompat.setContentDescription(alphaItem, 222 mActivity.getString(R.string.alpha_menu_description)); 223 MenuItemCompat.setTooltipText(alphaItem, 224 mActivity.getString(R.string.alpha_menu_tooltip)); 225 } 226 }); 227 228 onView(withId(R.id.action_alpha_shortcut)) 229 .check(matches(isDisplayed())) 230 .check(matches(withContentDescription(R.string.alpha_menu_description))); 231 } 232 233 private void repopulateWithEmptyMenu() throws InterruptedException { 234 int count = 0; 235 mActivity.setShouldPopulateOptionsMenu(false); 236 while (count++ < 10) { 237 Menu menu = mActivity.getMenu(); 238 if (menu == null || menu.size() != 0) { 239 Thread.sleep(100); 240 } else { 241 return; 242 } 243 } 244 } 245} 246