DrawerLayoutTest.java revision 21f78eb90b3217aa5cf69c3ffd359506468b55f4
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 android.os.Build; 19import android.support.test.espresso.action.GeneralLocation; 20import android.support.test.espresso.action.GeneralSwipeAction; 21import android.support.test.espresso.action.Press; 22import android.support.test.espresso.action.Swipe; 23import android.support.v4.view.GravityCompat; 24import android.support.v4.widget.DrawerLayout; 25import android.support.v7.appcompat.test.R; 26import android.support.v7.custom.CustomDrawerLayout; 27import android.test.suitebuilder.annotation.MediumTest; 28import android.test.suitebuilder.annotation.SmallTest; 29import android.view.View; 30import org.junit.Before; 31import org.junit.Test; 32import org.mockito.ArgumentCaptor; 33import org.mockito.InOrder; 34 35import static android.support.test.espresso.Espresso.onView; 36import static android.support.test.espresso.matcher.ViewMatchers.withId; 37import static android.support.v7.testutils.DrawerLayoutActions.*; 38import static android.support.v7.testutils.TestUtilsMatchers.*; 39import static org.hamcrest.MatcherAssert.assertThat; 40import static org.junit.Assert.*; 41import static org.mockito.Mockito.*; 42 43public class DrawerLayoutTest extends BaseInstrumentationTestCase<DrawerLayoutActivity> { 44 private CustomDrawerLayout mDrawerLayout; 45 46 private View mStartDrawer; 47 48 private View mContentView; 49 50 public DrawerLayoutTest() { 51 super(DrawerLayoutActivity.class); 52 } 53 54 @Before 55 public void setUp() { 56 final DrawerLayoutActivity activity = mActivityTestRule.getActivity(); 57 mDrawerLayout = (CustomDrawerLayout) activity.findViewById(R.id.drawer_layout); 58 mStartDrawer = mDrawerLayout.findViewById(R.id.start_drawer); 59 mContentView = mDrawerLayout.findViewById(R.id.content); 60 61 // Close the drawer to reset the state for the next test 62 onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START)); 63 } 64 65 // Tests for opening and closing the drawer and checking the open state 66 67 @Test 68 @MediumTest 69 public void testDrawerOpenCloseViaAPI() { 70 assertFalse("Initial state", mDrawerLayout.isDrawerOpen(GravityCompat.START)); 71 72 for (int i = 0; i < 5; i++) { 73 onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START)); 74 assertTrue("Opened drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START)); 75 76 onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START)); 77 assertFalse("Closed drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START)); 78 } 79 } 80 81 @Test 82 @MediumTest 83 public void testDrawerOpenCloseWithRedundancyViaAPI() { 84 assertFalse("Initial state", mDrawerLayout.isDrawerOpen(GravityCompat.START)); 85 86 for (int i = 0; i < 5; i++) { 87 onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START)); 88 assertTrue("Opened drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START)); 89 90 // Try opening the drawer when it's already opened 91 onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START)); 92 assertTrue("Opened drawer is still opened #" + i, 93 mDrawerLayout.isDrawerOpen(GravityCompat.START)); 94 95 onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START)); 96 assertFalse("Closed drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START)); 97 98 // Try closing the drawer when it's already closed 99 onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START)); 100 assertFalse("Closed drawer is still closed #" + i, 101 mDrawerLayout.isDrawerOpen(GravityCompat.START)); 102 } 103 } 104 105 @Test 106 @MediumTest 107 public void testDrawerOpenCloseViaSwipes() { 108 assertFalse("Initial state", mDrawerLayout.isDrawerOpen(GravityCompat.START)); 109 110 // Note that we're using GeneralSwipeAction instead of swipeLeft() / swipeRight(). 111 // Those Espresso actions use edge fuzzying which doesn't work well with edge-based 112 // detection of swiping the drawers open in DrawerLayout. 113 // It's critically important to wrap the GeneralSwipeAction to "wait" until the 114 // DrawerLayout has settled to STATE_IDLE state before continuing to query the drawer 115 // open / close state. This is done in DrawerLayoutActions.wrap method. 116 for (int i = 0; i < 5; i++) { 117 onView(withId(R.id.drawer_layout)).perform( 118 wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_LEFT, 119 GeneralLocation.CENTER_RIGHT, Press.FINGER))); 120 assertTrue("Opened drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START)); 121 122 onView(withId(R.id.drawer_layout)).perform( 123 wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_RIGHT, 124 GeneralLocation.CENTER_LEFT, Press.FINGER))); 125 assertFalse("Closed drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START)); 126 } 127 } 128 129 @Test 130 @MediumTest 131 public void testDrawerOpenCloseWithRedundancyViaSwipes() { 132 assertFalse("Initial state", mDrawerLayout.isDrawerOpen(GravityCompat.START)); 133 134 // Note that we're using GeneralSwipeAction instead of swipeLeft() / swipeRight(). 135 // Those Espresso actions use edge fuzzying which doesn't work well with edge-based 136 // detection of swiping the drawers open in DrawerLayout. 137 // It's critically important to wrap the GeneralSwipeAction to "wait" until the 138 // DrawerLayout has settled to STATE_IDLE state before continuing to query the drawer 139 // open / close state. This is done in DrawerLayoutActions.wrap method. 140 for (int i = 0; i < 5; i++) { 141 onView(withId(R.id.drawer_layout)).perform( 142 wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_LEFT, 143 GeneralLocation.CENTER_RIGHT, Press.FINGER))); 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( 148 wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_LEFT, 149 GeneralLocation.CENTER_RIGHT, Press.FINGER))); 150 assertTrue("Opened drawer is still opened #" + i, 151 mDrawerLayout.isDrawerOpen(GravityCompat.START)); 152 153 onView(withId(R.id.drawer_layout)).perform( 154 wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_RIGHT, 155 GeneralLocation.CENTER_LEFT, Press.FINGER))); 156 assertFalse("Closed drawer #" + i, mDrawerLayout.isDrawerOpen(GravityCompat.START)); 157 158 // Try closing the drawer when it's already closed 159 onView(withId(R.id.drawer_layout)).perform( 160 wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_RIGHT, 161 GeneralLocation.CENTER_LEFT, Press.FINGER))); 162 assertFalse("Closed drawer is still closed #" + i, 163 mDrawerLayout.isDrawerOpen(GravityCompat.START)); 164 } 165 } 166 167 @Test 168 @SmallTest 169 public void testDrawerHeight() { 170 // Open the drawer so it becomes visible 171 onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START)); 172 173 final int drawerLayoutHeight = mDrawerLayout.getHeight(); 174 final int startDrawerHeight = mStartDrawer.getHeight(); 175 final int contentHeight = mContentView.getHeight(); 176 177 // On all devices the height of the drawer layout and the drawer should be identical. 178 assertEquals("Drawer layout and drawer heights", drawerLayoutHeight, startDrawerHeight); 179 180 if (Build.VERSION.SDK_INT < 21) { 181 // On pre-L devices the content height should be the same as the drawer layout height. 182 assertEquals("Drawer layout and content heights on pre-L", 183 drawerLayoutHeight, contentHeight); 184 } else { 185 // Our drawer layout is configured with android:fitsSystemWindows="true" which should be 186 // respected on L+ devices to extend the drawer layout into the system status bar. 187 // The start drawer is also configured with the same attribute so it should have the 188 // same height as the drawer layout. The main content does not have that attribute 189 // specified, so it should have its height reduced by the height of the system status 190 // bar. 191 192 // Get the system window top inset that was propagated to the top-level DrawerLayout 193 // during its layout. 194 int drawerTopInset = mDrawerLayout.getSystemWindowInsetTop(); 195 assertTrue("Drawer top inset is positive on L+", drawerTopInset > 0); 196 assertEquals("Drawer layout and drawer heights on L+", 197 drawerLayoutHeight - drawerTopInset, contentHeight); 198 } 199 } 200 201 // Tests for listener(s) being notified of various events 202 203 @Test 204 @SmallTest 205 public void testDrawerListenerCallbacksOnOpeningViaAPI() { 206 // Register a mock listener 207 DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class); 208 mDrawerLayout.addDrawerListener(mockedListener); 209 210 // Open the drawer so it becomes visible 211 onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START)); 212 213 // We expect that our listener has been notified that the drawer has been opened 214 // with the reference to our drawer 215 verify(mockedListener, times(1)).onDrawerOpened(mStartDrawer); 216 // We expect that our listener has not been notified that the drawer has been closed 217 verify(mockedListener, never()).onDrawerClosed(any(View.class)); 218 219 // We expect that our listener has been notified at least once on the drawer slide 220 // event. We expect that all such callbacks pass the reference to our drawer as the first 221 // parameter, and we capture the float slide values for further analysis 222 ArgumentCaptor<Float> floatSlideCaptor = ArgumentCaptor.forClass(float.class); 223 verify(mockedListener, atLeastOnce()).onDrawerSlide(eq(mStartDrawer), 224 floatSlideCaptor.capture()); 225 // Now we verify that calls to onDrawerSlide "gave" us an increasing sequence of values 226 // in [0..1] range. Note that we don't have any expectation on how many times onDrawerSlide 227 // is called since that depends on the hardware capabilities of the device and the current 228 // load on the CPU / GPU. 229 assertThat(floatSlideCaptor.getAllValues(), inRange(0.0f, 1.0f)); 230 assertThat(floatSlideCaptor.getAllValues(), inAscendingOrder()); 231 232 // We expect that our listener will be called with specific state changes 233 InOrder inOrder = inOrder(mockedListener); 234 inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_SETTLING); 235 inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_IDLE); 236 237 mDrawerLayout.removeDrawerListener(mockedListener); 238 } 239 240 @Test 241 @SmallTest 242 public void testDrawerListenerCallbacksOnClosingViaAPI() { 243 // Open the drawer so it becomes visible 244 onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START)); 245 246 // Register a mock listener 247 DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class); 248 mDrawerLayout.addDrawerListener(mockedListener); 249 250 // Close the drawer 251 onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START)); 252 253 // We expect that our listener has not been notified that the drawer has been opened 254 verify(mockedListener, never()).onDrawerOpened(any(View.class)); 255 // We expect that our listener has been notified that the drawer has been closed 256 // with the reference to our drawer 257 verify(mockedListener, times(1)).onDrawerClosed(mStartDrawer); 258 259 // We expect that our listener has been notified at least once on the drawer slide 260 // event. We expect that all such callbacks pass the reference to our drawer as the first 261 // parameter, and we capture the float slide values for further analysis 262 ArgumentCaptor<Float> floatSlideCaptor = ArgumentCaptor.forClass(float.class); 263 verify(mockedListener, atLeastOnce()).onDrawerSlide(eq(mStartDrawer), 264 floatSlideCaptor.capture()); 265 // Now we verify that calls to onDrawerSlide "gave" us a decreasing sequence of values 266 // in [0..1] range. Note that we don't have any expectation on how many times onDrawerSlide 267 // is called since that depends on the hardware capabilities of the device and the current 268 // load on the CPU / GPU. 269 assertThat(floatSlideCaptor.getAllValues(), inRange(0.0f, 1.0f)); 270 assertThat(floatSlideCaptor.getAllValues(), inDescendingOrder()); 271 272 // We expect that our listener will be called with specific state changes 273 InOrder inOrder = inOrder(mockedListener); 274 inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_SETTLING); 275 inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_IDLE); 276 277 mDrawerLayout.removeDrawerListener(mockedListener); 278 } 279 280 @Test 281 @SmallTest 282 public void testDrawerListenerCallbacksOnOpeningViaSwipes() { 283 // Register a mock listener 284 DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class); 285 mDrawerLayout.addDrawerListener(mockedListener); 286 287 // Open the drawer so it becomes visible 288 // Note that we're using GeneralSwipeAction instead of swipeLeft() / swipeRight(). 289 // Those Espresso actions use edge fuzzying which doesn't work well with edge-based 290 // detection of swiping the drawers open in DrawerLayout. 291 // It's critically important to wrap the GeneralSwipeAction to "wait" until the 292 // DrawerLayout has settled to STATE_IDLE state before continuing to query the drawer 293 // open / close state. This is done in DrawerLayoutActions.wrap method. 294 onView(withId(R.id.drawer_layout)).perform( 295 wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_LEFT, 296 GeneralLocation.CENTER_RIGHT, Press.FINGER))); 297 298 // We expect that our listener has been notified that the drawer has been opened 299 // with the reference to our drawer 300 verify(mockedListener, times(1)).onDrawerOpened(mStartDrawer); 301 // We expect that our listener has not been notified that the drawer has been closed 302 verify(mockedListener, never()).onDrawerClosed(any(View.class)); 303 304 // We expect that our listener has been notified at least once on the drawer slide 305 // event. We expect that all such callbacks pass the reference to our drawer as the first 306 // parameter, and we capture the float slide values for further analysis 307 ArgumentCaptor<Float> floatSlideCaptor = ArgumentCaptor.forClass(float.class); 308 verify(mockedListener, atLeastOnce()).onDrawerSlide(eq(mStartDrawer), 309 floatSlideCaptor.capture()); 310 // Now we verify that calls to onDrawerSlide "gave" us an increasing sequence of values 311 // in [0..1] range. Note that we don't have any expectation on how many times onDrawerSlide 312 // is called since that depends on the hardware capabilities of the device and the current 313 // load on the CPU / GPU. 314 assertThat(floatSlideCaptor.getAllValues(), inRange(0.0f, 1.0f)); 315 assertThat(floatSlideCaptor.getAllValues(), inAscendingOrder()); 316 317 // We expect that our listener will be called with specific state changes 318 InOrder inOrder = inOrder(mockedListener); 319 inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_DRAGGING); 320 inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_IDLE); 321 322 mDrawerLayout.removeDrawerListener(mockedListener); 323 } 324 325 @Test 326 @SmallTest 327 public void testDrawerListenerCallbacksOnClosingViaSwipes() { 328 // Open the drawer so it becomes visible 329 onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START)); 330 331 // Register a mock listener 332 DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class); 333 mDrawerLayout.addDrawerListener(mockedListener); 334 335 // Close the drawer 336 // Note that we're using GeneralSwipeAction instead of swipeLeft() / swipeRight(). 337 // Those Espresso actions use edge fuzzying which doesn't work well with edge-based 338 // detection of swiping the drawers open in DrawerLayout. 339 // It's critically important to wrap the GeneralSwipeAction to "wait" until the 340 // DrawerLayout has settled to STATE_IDLE state before continuing to query the drawer 341 // open / close state. This is done in DrawerLayoutActions.wrap method. 342 onView(withId(R.id.drawer_layout)).perform( 343 wrap(new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_RIGHT, 344 GeneralLocation.CENTER_LEFT, Press.FINGER))); 345 346 // We expect that our listener has not been notified that the drawer has been opened 347 verify(mockedListener, never()).onDrawerOpened(any(View.class)); 348 // We expect that our listener has been notified that the drawer has been closed 349 // with the reference to our drawer 350 verify(mockedListener, times(1)).onDrawerClosed(mStartDrawer); 351 352 // We expect that our listener has been notified at least once on the drawer slide 353 // event. We expect that all such callbacks pass the reference to our drawer as the first 354 // parameter, and we capture the float slide values for further analysis 355 ArgumentCaptor<Float> floatSlideCaptor = ArgumentCaptor.forClass(float.class); 356 verify(mockedListener, atLeastOnce()).onDrawerSlide(eq(mStartDrawer), 357 floatSlideCaptor.capture()); 358 // Now we verify that calls to onDrawerSlide "gave" us a decreasing sequence of values 359 // in [0..1] range. Note that we don't have any expectation on how many times onDrawerSlide 360 // is called since that depends on the hardware capabilities of the device and the current 361 // load on the CPU / GPU. 362 assertThat(floatSlideCaptor.getAllValues(), inRange(0.0f, 1.0f)); 363 assertThat(floatSlideCaptor.getAllValues(), inDescendingOrder()); 364 365 // We expect that our listener will be called with specific state changes 366 InOrder inOrder = inOrder(mockedListener); 367 inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_DRAGGING); 368 inOrder.verify(mockedListener).onDrawerStateChanged(DrawerLayout.STATE_IDLE); 369 370 mDrawerLayout.removeDrawerListener(mockedListener); 371 } 372} 373