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