DrawerLayoutTest.java revision 35232c6eaeb9b99f390cb8ef0ac83bf45fa0b3fa
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 */
16package android.support.v7.app;
17
18import static android.support.test.espresso.Espresso.onView;
19import static android.support.test.espresso.matcher.ViewMatchers.withId;
20import static android.support.v7.testutils.DrawerLayoutActions.closeDrawer;
21import static android.support.v7.testutils.DrawerLayoutActions.openDrawer;
22import static android.support.v7.testutils.DrawerLayoutActions.setDrawerLockMode;
23import static android.support.v7.testutils.DrawerLayoutActions.wrap;
24import static android.support.v7.testutils.TestUtilsMatchers.inAscendingOrder;
25import static android.support.v7.testutils.TestUtilsMatchers.inDescendingOrder;
26import static android.support.v7.testutils.TestUtilsMatchers.inRange;
27
28import static org.hamcrest.MatcherAssert.assertThat;
29import static org.junit.Assert.assertEquals;
30import static org.junit.Assert.assertFalse;
31import static org.junit.Assert.assertTrue;
32import static org.junit.Assert.fail;
33import static org.mockito.Mockito.any;
34import static org.mockito.Mockito.atLeastOnce;
35import static org.mockito.Mockito.eq;
36import static org.mockito.Mockito.inOrder;
37import static org.mockito.Mockito.mock;
38import static org.mockito.Mockito.never;
39import static org.mockito.Mockito.times;
40import static org.mockito.Mockito.verify;
41
42import android.os.Build;
43import android.support.test.espresso.action.GeneralLocation;
44import android.support.test.espresso.action.GeneralSwipeAction;
45import android.support.test.espresso.action.Press;
46import android.support.test.espresso.action.Swipe;
47import android.support.test.filters.LargeTest;
48import android.support.test.filters.MediumTest;
49import android.support.v4.view.GravityCompat;
50import android.support.v4.widget.DrawerLayout;
51import android.support.v7.appcompat.test.R;
52import android.support.v7.custom.CustomDrawerLayout;
53import android.view.View;
54
55import org.junit.Before;
56import org.junit.Test;
57import org.mockito.ArgumentCaptor;
58import org.mockito.InOrder;
59
60public class DrawerLayoutTest extends BaseInstrumentationTestCase<DrawerLayoutActivity> {
61    private CustomDrawerLayout mDrawerLayout;
62
63    private View mStartDrawer;
64
65    private View mContentView;
66
67    public DrawerLayoutTest() {
68        super(DrawerLayoutActivity.class);
69    }
70
71    @Before
72    public void setUp() {
73        final DrawerLayoutActivity activity = mActivityTestRule.getActivity();
74        mDrawerLayout = (CustomDrawerLayout) activity.findViewById(R.id.drawer_layout);
75        mStartDrawer = mDrawerLayout.findViewById(R.id.start_drawer);
76        mContentView = mDrawerLayout.findViewById(R.id.content);
77
78        // Close the drawer to reset the state for the next test
79        onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START));
80    }
81
82    // Tests for opening and closing the drawer and checking the open state
83
84    @Test
85    @MediumTest
86    public void testDrawerOpenCloseViaAPI() {
87        assertFalse("Initial state", mDrawerLayout.isDrawerOpen(GravityCompat.START));
88
89        for (int i = 0; i < 5; i++) {
90            onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
91            assertTrue("Opened drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START));
92
93            onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START));
94            assertFalse("Closed drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START));
95        }
96    }
97
98    @Test
99    @MediumTest
100    public void testDrawerOpenCloseNoAnimationViaAPI() {
101        assertFalse("Initial state", mDrawerLayout.isDrawerOpen(GravityCompat.START));
102
103        for (int i = 0; i < 5; i++) {
104            onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START, false));
105            assertTrue("Opened drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START));
106
107            onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START, false));
108            assertFalse("Closed drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START));
109        }
110    }
111
112    @Test
113    @MediumTest
114    public void testDrawerOpenCloseFocus() throws Throwable {
115        assertFalse("Initial state", mDrawerLayout.isDrawerOpen(GravityCompat.START));
116
117        mActivityTestRule.runOnUiThread(new Runnable() {
118            @Override
119            public void run() {
120                mContentView.setFocusableInTouchMode(true);
121                mContentView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
122                    @Override
123                    public void onFocusChange(View v, boolean hasFocus) {
124                        fail("Unnecessary focus change");
125                    }
126                });
127            }
128        });
129
130        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
131        assertTrue("Opened drawer", mDrawerLayout.isDrawerOpen(GravityCompat.START));
132
133        onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START));
134        assertFalse("Closed drawer", mDrawerLayout.isDrawerOpen(GravityCompat.START));
135    }
136
137    @Test
138    @MediumTest
139    public void testDrawerOpenCloseWithRedundancyViaAPI() {
140        assertFalse("Initial state", mDrawerLayout.isDrawerOpen(GravityCompat.START));
141
142        for (int i = 0; i < 5; i++) {
143            onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
144            assertTrue("Opened drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START));
145
146            // Try opening the drawer when it's already opened
147            onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
148            assertTrue("Opened drawer is still opened #" + i,
149                    mDrawerLayout.isDrawerOpen(GravityCompat.START));
150
151            onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START));
152            assertFalse("Closed drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START));
153
154            // Try closing the drawer when it's already closed
155            onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START));
156            assertFalse("Closed drawer is still closed #" + i,
157                    mDrawerLayout.isDrawerOpen(GravityCompat.START));
158        }
159    }
160
161    @Test
162    @MediumTest
163    public void testDrawerOpenCloseNoAnimationWithRedundancyViaAPI() {
164        assertFalse("Initial state", mDrawerLayout.isDrawerOpen(GravityCompat.START));
165
166        for (int i = 0; i < 5; i++) {
167            onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START, false));
168            assertTrue("Opened drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START));
169
170            // Try opening the drawer when it's already opened
171            onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START, false));
172            assertTrue("Opened drawer is still opened #" + i,
173                    mDrawerLayout.isDrawerOpen(GravityCompat.START));
174
175            onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START, false));
176            assertFalse("Closed drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START));
177
178            // Try closing the drawer when it's already closed
179            onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START, false));
180            assertFalse("Closed drawer is still closed #" + i,
181                    mDrawerLayout.isDrawerOpen(GravityCompat.START));
182        }
183    }
184
185    @Test
186    @MediumTest
187    public void testDrawerOpenCloseViaSwipes() {
188        assertFalse("Initial state", mDrawerLayout.isDrawerOpen(GravityCompat.START));
189
190        // Note that we're using GeneralSwipeAction instead of swipeLeft() / swipeRight().
191        // Those Espresso actions use edge fuzzying which doesn't work well with edge-based
192        // detection of swiping the drawers open in DrawerLayout.
193        // It's critically important to wrap the GeneralSwipeAction to "wait" until the
194        // DrawerLayout has settled to STATE_IDLE state before continuing to query the drawer
195        // open / close state. This is done in DrawerLayoutActions.wrap method.
196        for (int i = 0; i < 5; i++) {
197            onView(withId(R.id.drawer_layout)).perform(
198                    wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_LEFT,
199                            GeneralLocation.CENTER_RIGHT, Press.FINGER)));
200            assertTrue("Opened drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START));
201
202            onView(withId(R.id.drawer_layout)).perform(
203                    wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_RIGHT,
204                            GeneralLocation.CENTER_LEFT, Press.FINGER)));
205            assertFalse("Closed drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START));
206        }
207    }
208
209    @Test
210    @LargeTest
211    public void testDrawerOpenCloseWithRedundancyViaSwipes() {
212        assertFalse("Initial state", mDrawerLayout.isDrawerOpen(GravityCompat.START));
213
214        // Note that we're using GeneralSwipeAction instead of swipeLeft() / swipeRight().
215        // Those Espresso actions use edge fuzzying which doesn't work well with edge-based
216        // detection of swiping the drawers open in DrawerLayout.
217        // It's critically important to wrap the GeneralSwipeAction to "wait" until the
218        // DrawerLayout has settled to STATE_IDLE state before continuing to query the drawer
219        // open / close state. This is done in DrawerLayoutActions.wrap method.
220        for (int i = 0; i < 5; i++) {
221            onView(withId(R.id.drawer_layout)).perform(
222                    wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_LEFT,
223                            GeneralLocation.CENTER_RIGHT, Press.FINGER)));
224            assertTrue("Opened drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START));
225
226            // Try opening the drawer when it's already opened
227            onView(withId(R.id.drawer_layout)).perform(
228                    wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_LEFT,
229                            GeneralLocation.CENTER_RIGHT, Press.FINGER)));
230            assertTrue("Opened drawer is still opened #" + i,
231                    mDrawerLayout.isDrawerOpen(GravityCompat.START));
232
233            onView(withId(R.id.drawer_layout)).perform(
234                    wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_RIGHT,
235                            GeneralLocation.CENTER_LEFT, Press.FINGER)));
236            assertFalse("Closed drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START));
237
238            // Try closing the drawer when it's already closed
239            onView(withId(R.id.drawer_layout)).perform(
240                    wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_RIGHT,
241                            GeneralLocation.CENTER_LEFT, Press.FINGER)));
242            assertFalse("Closed drawer is still closed #" + i,
243                    mDrawerLayout.isDrawerOpen(GravityCompat.START));
244        }
245    }
246
247    @Test
248    @MediumTest
249    public void testDrawerHeight() {
250        // Open the drawer so it becomes visible
251        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
252
253        final int drawerLayoutHeight = mDrawerLayout.getHeight();
254        final int startDrawerHeight = mStartDrawer.getHeight();
255        final int contentHeight = mContentView.getHeight();
256
257        // On all devices the height of the drawer layout and the drawer should be identical.
258        assertEquals("Drawer layout and drawer heights", drawerLayoutHeight, startDrawerHeight);
259
260        if (Build.VERSION.SDK_INT < 21) {
261            // On pre-L devices the content height should be the same as the drawer layout height.
262            assertEquals("Drawer layout and content heights on pre-L",
263                    drawerLayoutHeight, contentHeight);
264        } else {
265            // Our drawer layout is configured with android:fitsSystemWindows="true" which should be
266            // respected on L+ devices to extend the drawer layout into the system status bar.
267            // The start drawer is also configured with the same attribute so it should have the
268            // same height as the drawer layout. The main content does not have that attribute
269            // specified, so it should have its height reduced by the height of the system status
270            // bar.
271
272            final int[] contentViewLocationOnScreen = new int[2];
273            mContentView.getLocationOnScreen(contentViewLocationOnScreen);
274            final int statusBarHeight = contentViewLocationOnScreen[1];
275            // Get the system window top inset that was propagated to the top-level DrawerLayout
276            // during its layout.
277            int drawerTopInset = mDrawerLayout.getSystemWindowInsetTop();
278            if (statusBarHeight > 0) {
279                assertEquals("Drawer top inset is positive on L+", statusBarHeight, drawerTopInset);
280            } else {
281                assertEquals("Drawer top inset 0 due to no status bar", 0, drawerTopInset);
282            }
283            assertEquals("Drawer layout and drawer heights on L+",
284                    drawerLayoutHeight - drawerTopInset, contentHeight);
285        }
286    }
287
288    // Tests for listener(s) being notified of various events
289
290    @Test
291    @MediumTest
292    public void testDrawerListenerCallbacksOnOpeningViaAPI() {
293        // Register a mock listener
294        DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class);
295        mDrawerLayout.addDrawerListener(mockedListener);
296
297        // Open the drawer so it becomes visible
298        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
299
300        // We expect that our listener has been notified that the drawer has been opened
301        // with the reference to our drawer
302        verify(mockedListener, times(1)).onDrawerOpened(mStartDrawer);
303        // We expect that our listener has not been notified that the drawer has been closed
304        verify(mockedListener, never()).onDrawerClosed(any(View.class));
305
306        // We expect that our listener has been notified at least once on the drawer slide
307        // event. We expect that all such callbacks pass the reference to our drawer as the first
308        // parameter, and we capture the float slide values for further analysis
309        ArgumentCaptor<Float> floatSlideCaptor = ArgumentCaptor.forClass(float.class);
310        verify(mockedListener, atLeastOnce()).onDrawerSlide(eq(mStartDrawer),
311                floatSlideCaptor.capture());
312        // Now we verify that calls to onDrawerSlide "gave" us an increasing sequence of values
313        // in [0..1] range. Note that we don't have any expectation on how many times onDrawerSlide
314        // is called since that depends on the hardware capabilities of the device and the current
315        // load on the CPU / GPU.
316        assertThat(floatSlideCaptor.getAllValues(), inRange(0.0f, 1.0f));
317        assertThat(floatSlideCaptor.getAllValues(), inAscendingOrder());
318
319        // We expect that our listener will be called with specific state changes
320        InOrder inOrder = inOrder(mockedListener);
321        inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_SETTLING);
322        inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_IDLE);
323
324        mDrawerLayout.removeDrawerListener(mockedListener);
325    }
326
327    @Test
328    @MediumTest
329    public void testDrawerListenerCallbacksOnOpeningNoAnimationViaAPI() {
330        // Register a mock listener
331        DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class);
332        mDrawerLayout.addDrawerListener(mockedListener);
333
334        // Open the drawer so it becomes visible
335        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START, false));
336
337        // We expect that our listener has been notified that the drawer has been opened
338        // with the reference to our drawer
339        verify(mockedListener, times(1)).onDrawerOpened(mStartDrawer);
340        // We expect that our listener has not been notified that the drawer has been closed
341        verify(mockedListener, never()).onDrawerClosed(any(View.class));
342
343        verify(mockedListener, times(1)).onDrawerSlide(any(View.class), eq(1f));
344
345        // Request to open the drawer again
346        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START, false));
347
348        // We expect that our listener has not been notified again that the drawer has been opened
349        verify(mockedListener, times(1)).onDrawerOpened(mStartDrawer);
350        // We expect that our listener has not been notified that the drawer has been closed
351        verify(mockedListener, never()).onDrawerClosed(any(View.class));
352
353        mDrawerLayout.removeDrawerListener(mockedListener);
354    }
355
356    @Test
357    @LargeTest
358    public void testDrawerListenerCallbacksOnClosingViaAPI() {
359        // Open the drawer so it becomes visible
360        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
361
362        // Register a mock listener
363        DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class);
364        mDrawerLayout.addDrawerListener(mockedListener);
365
366        // Close the drawer
367        onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START));
368
369        // We expect that our listener has not been notified that the drawer has been opened
370        verify(mockedListener, never()).onDrawerOpened(any(View.class));
371        // We expect that our listener has been notified that the drawer has been closed
372        // with the reference to our drawer
373        verify(mockedListener, times(1)).onDrawerClosed(mStartDrawer);
374
375        // We expect that our listener has been notified at least once on the drawer slide
376        // event. We expect that all such callbacks pass the reference to our drawer as the first
377        // parameter, and we capture the float slide values for further analysis
378        ArgumentCaptor<Float> floatSlideCaptor = ArgumentCaptor.forClass(float.class);
379        verify(mockedListener, atLeastOnce()).onDrawerSlide(eq(mStartDrawer),
380                floatSlideCaptor.capture());
381        // Now we verify that calls to onDrawerSlide "gave" us a decreasing sequence of values
382        // in [0..1] range. Note that we don't have any expectation on how many times onDrawerSlide
383        // is called since that depends on the hardware capabilities of the device and the current
384        // load on the CPU / GPU.
385        assertThat(floatSlideCaptor.getAllValues(), inRange(0.0f, 1.0f));
386        assertThat(floatSlideCaptor.getAllValues(), inDescendingOrder());
387
388        // We expect that our listener will be called with specific state changes
389        InOrder inOrder = inOrder(mockedListener);
390        inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_SETTLING);
391        inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_IDLE);
392
393        mDrawerLayout.removeDrawerListener(mockedListener);
394    }
395
396    @Test
397    @MediumTest
398    public void testDrawerListenerCallbacksOnClosingNoAnimationViaAPI() {
399        // Open the drawer so it becomes visible
400        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START, false));
401
402        // Register a mock listener
403        DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class);
404        mDrawerLayout.addDrawerListener(mockedListener);
405
406        // Close the drawer
407        onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START, false));
408
409        // We expect that our listener has not been notified that the drawer has been opened
410        verify(mockedListener, never()).onDrawerOpened(any(View.class));
411        // We expect that our listener has been notified that the drawer has been closed
412        // with the reference to our drawer
413        verify(mockedListener, times(1)).onDrawerClosed(mStartDrawer);
414
415        verify(mockedListener, times(1)).onDrawerSlide(any(View.class), eq(0f));
416
417        // Attempt to close the drawer again.
418        onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START, false));
419
420        // We expect that our listener has not been notified that the drawer has been opened
421        verify(mockedListener, never()).onDrawerOpened(any(View.class));
422        // We expect that our listener has not been notified again that the drawer has been closed
423        verify(mockedListener, times(1)).onDrawerClosed(mStartDrawer);
424
425        mDrawerLayout.removeDrawerListener(mockedListener);
426    }
427
428    @Test
429    @MediumTest
430    public void testDrawerListenerCallbacksOnOpeningViaSwipes() {
431        // Register a mock listener
432        DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class);
433        mDrawerLayout.addDrawerListener(mockedListener);
434
435        // Open the drawer so it becomes visible
436        // Note that we're using GeneralSwipeAction instead of swipeLeft() / swipeRight().
437        // Those Espresso actions use edge fuzzying which doesn't work well with edge-based
438        // detection of swiping the drawers open in DrawerLayout.
439        // It's critically important to wrap the GeneralSwipeAction to "wait" until the
440        // DrawerLayout has settled to STATE_IDLE state before continuing to query the drawer
441        // open / close state. This is done in DrawerLayoutActions.wrap method.
442        onView(withId(R.id.drawer_layout)).perform(
443                wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_LEFT,
444                        GeneralLocation.CENTER_RIGHT, Press.FINGER)));
445
446        // We expect that our listener has been notified that the drawer has been opened
447        // with the reference to our drawer
448        verify(mockedListener, times(1)).onDrawerOpened(mStartDrawer);
449        // We expect that our listener has not been notified that the drawer has been closed
450        verify(mockedListener, never()).onDrawerClosed(any(View.class));
451
452        // We expect that our listener has been notified at least once on the drawer slide
453        // event. We expect that all such callbacks pass the reference to our drawer as the first
454        // parameter, and we capture the float slide values for further analysis
455        ArgumentCaptor<Float> floatSlideCaptor = ArgumentCaptor.forClass(float.class);
456        verify(mockedListener, atLeastOnce()).onDrawerSlide(eq(mStartDrawer),
457                floatSlideCaptor.capture());
458        // Now we verify that calls to onDrawerSlide "gave" us an increasing sequence of values
459        // in [0..1] range. Note that we don't have any expectation on how many times onDrawerSlide
460        // is called since that depends on the hardware capabilities of the device and the current
461        // load on the CPU / GPU.
462        assertThat(floatSlideCaptor.getAllValues(), inRange(0.0f, 1.0f));
463        assertThat(floatSlideCaptor.getAllValues(), inAscendingOrder());
464
465        // We expect that our listener will be called with specific state changes
466        InOrder inOrder = inOrder(mockedListener);
467        inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_DRAGGING);
468        inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_IDLE);
469
470        mDrawerLayout.removeDrawerListener(mockedListener);
471    }
472
473    @Test
474    @LargeTest
475    public void testDrawerListenerCallbacksOnClosingViaSwipes() {
476        // Open the drawer so it becomes visible
477        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
478
479        // Register a mock listener
480        DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class);
481        mDrawerLayout.addDrawerListener(mockedListener);
482
483        // Close the drawer
484        // Note that we're using GeneralSwipeAction instead of swipeLeft() / swipeRight().
485        // Those Espresso actions use edge fuzzying which doesn't work well with edge-based
486        // detection of swiping the drawers open in DrawerLayout.
487        // It's critically important to wrap the GeneralSwipeAction to "wait" until the
488        // DrawerLayout has settled to STATE_IDLE state before continuing to query the drawer
489        // open / close state. This is done in DrawerLayoutActions.wrap method.
490        onView(withId(R.id.drawer_layout)).perform(
491                wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_RIGHT,
492                        GeneralLocation.CENTER_LEFT, Press.FINGER)));
493
494        // We expect that our listener has not been notified that the drawer has been opened
495        verify(mockedListener, never()).onDrawerOpened(any(View.class));
496        // We expect that our listener has been notified that the drawer has been closed
497        // with the reference to our drawer
498        verify(mockedListener, times(1)).onDrawerClosed(mStartDrawer);
499
500        // We expect that our listener has been notified at least once on the drawer slide
501        // event. We expect that all such callbacks pass the reference to our drawer as the first
502        // parameter, and we capture the float slide values for further analysis
503        ArgumentCaptor<Float> floatSlideCaptor = ArgumentCaptor.forClass(float.class);
504        verify(mockedListener, atLeastOnce()).onDrawerSlide(eq(mStartDrawer),
505                floatSlideCaptor.capture());
506        // Now we verify that calls to onDrawerSlide "gave" us a decreasing sequence of values
507        // in [0..1] range. Note that we don't have any expectation on how many times onDrawerSlide
508        // is called since that depends on the hardware capabilities of the device and the current
509        // load on the CPU / GPU.
510        assertThat(floatSlideCaptor.getAllValues(), inRange(0.0f, 1.0f));
511        assertThat(floatSlideCaptor.getAllValues(), inDescendingOrder());
512
513        // We expect that our listener will be called with specific state changes
514        InOrder inOrder = inOrder(mockedListener);
515        inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_DRAGGING);
516        inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_IDLE);
517
518        mDrawerLayout.removeDrawerListener(mockedListener);
519    }
520
521    @Test
522    @LargeTest
523    public void testDrawerLockUnlock() {
524        assertEquals("Drawer is unlocked in initial state",
525                DrawerLayout.LOCK_MODE_UNLOCKED, mDrawerLayout.getDrawerLockMode(mStartDrawer));
526
527        // Lock the drawer open
528        onView(withId(R.id.drawer_layout)).perform(
529                setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, GravityCompat.START));
530        // Check that it's locked open
531        assertEquals("Drawer is now locked open",
532                DrawerLayout.LOCK_MODE_LOCKED_OPEN, mDrawerLayout.getDrawerLockMode(mStartDrawer));
533        // and also opened
534        assertTrue("Drawer is also opened", mDrawerLayout.isDrawerOpen(mStartDrawer));
535
536        // Unlock the drawer
537        onView(withId(R.id.drawer_layout)).perform(
538                setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, mStartDrawer));
539        // Check that it's still opened
540        assertTrue("Drawer is still opened", mDrawerLayout.isDrawerOpen(mStartDrawer));
541        // Close the drawer
542        onView(withId(R.id.drawer_layout)).perform(closeDrawer(mStartDrawer));
543        // Check that the drawer is unlocked
544        assertEquals("Start drawer is now unlocked",
545                DrawerLayout.LOCK_MODE_UNLOCKED, mDrawerLayout.getDrawerLockMode(mStartDrawer));
546
547        // Open the drawer and then clock it closed
548        onView(withId(R.id.drawer_layout)).perform(openDrawer(mStartDrawer));
549        onView(withId(R.id.drawer_layout)).perform(
550                setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.START));
551        // Check that the drawer is locked close
552        assertEquals("Drawer is now locked close",
553                DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
554                mDrawerLayout.getDrawerLockMode(mStartDrawer));
555        // and also closed
556        assertFalse("Drawer is also closed", mDrawerLayout.isDrawerOpen(mStartDrawer));
557
558        // Unlock the drawer
559        onView(withId(R.id.drawer_layout)).perform(
560                setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, mStartDrawer));
561        // Check that it's still closed
562        assertFalse("Drawer is still closed", mDrawerLayout.isDrawerOpen(mStartDrawer));
563    }
564}
565