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