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