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