OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser; |
| 6 |
| 7 import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_E
ND_DEVICE; |
| 8 import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_PHONE; |
| 9 |
| 10 import android.content.DialogInterface; |
| 11 import android.content.pm.ActivityInfo; |
| 12 import android.os.Debug; |
| 13 import android.os.SystemClock; |
| 14 import android.test.FlakyTest; |
| 15 import android.test.suitebuilder.annotation.LargeTest; |
| 16 import android.test.suitebuilder.annotation.MediumTest; |
| 17 import android.test.suitebuilder.annotation.SmallTest; |
| 18 import android.test.suitebuilder.annotation.Smoke; |
| 19 import android.util.Log; |
| 20 import android.view.View; |
| 21 |
| 22 import com.google.android.apps.chrome.R; |
| 23 |
| 24 import junit.framework.Assert; |
| 25 |
| 26 import org.chromium.base.CommandLine; |
| 27 import org.chromium.base.ThreadUtils; |
| 28 import org.chromium.base.annotations.SuppressFBWarnings; |
| 29 import org.chromium.base.test.util.DisabledTest; |
| 30 import org.chromium.base.test.util.Feature; |
| 31 import org.chromium.base.test.util.Restriction; |
| 32 import org.chromium.base.test.util.UrlUtils; |
| 33 import org.chromium.chrome.browser.compositor.CompositorViewHolder; |
| 34 import org.chromium.chrome.browser.compositor.layouts.Layout; |
| 35 import org.chromium.chrome.browser.compositor.layouts.LayoutManager; |
| 36 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome; |
| 37 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChromePhone; |
| 38 import org.chromium.chrome.browser.compositor.layouts.StaticLayout; |
| 39 import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab; |
| 40 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeEvent
Filter.ScrollDirection; |
| 41 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeHandl
er; |
| 42 import org.chromium.chrome.browser.compositor.layouts.phone.StackLayout; |
| 43 import org.chromium.chrome.browser.compositor.layouts.phone.stack.Stack; |
| 44 import org.chromium.chrome.browser.compositor.layouts.phone.stack.StackTab; |
| 45 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; |
| 46 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver; |
| 47 import org.chromium.chrome.browser.tabmodel.TabModel; |
| 48 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; |
| 49 import org.chromium.chrome.browser.tabmodel.TabModelObserver; |
| 50 import org.chromium.chrome.browser.tabmodel.TabModelSelector; |
| 51 import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl; |
| 52 import org.chromium.chrome.browser.tabmodel.TabModelUtils; |
| 53 import org.chromium.chrome.browser.toolbar.ToolbarPhone; |
| 54 import org.chromium.chrome.test.ChromeTabbedActivityTestBase; |
| 55 import org.chromium.chrome.test.util.ChromeTabUtils; |
| 56 import org.chromium.chrome.test.util.MenuUtils; |
| 57 import org.chromium.chrome.test.util.OverviewModeBehaviorWatcher; |
| 58 import org.chromium.chrome.test.util.TestHttpServerClient; |
| 59 import org.chromium.content.browser.ContentViewCore; |
| 60 import org.chromium.content.browser.test.util.CallbackHelper; |
| 61 import org.chromium.content.browser.test.util.Criteria; |
| 62 import org.chromium.content.browser.test.util.CriteriaHelper; |
| 63 import org.chromium.content.browser.test.util.JavaScriptUtils; |
| 64 import org.chromium.content.browser.test.util.UiUtils; |
| 65 import org.chromium.content.common.ContentSwitches; |
| 66 import org.chromium.content_public.browser.WebContents; |
| 67 import org.chromium.content_public.browser.WebContentsObserver; |
| 68 |
| 69 import java.util.concurrent.Callable; |
| 70 import java.util.concurrent.TimeUnit; |
| 71 import java.util.concurrent.TimeoutException; |
| 72 import java.util.concurrent.atomic.AtomicBoolean; |
| 73 import java.util.concurrent.atomic.AtomicReference; |
| 74 |
| 75 /** |
| 76 * General Tab tests. |
| 77 */ |
| 78 public class TabsTest extends ChromeTabbedActivityTestBase { |
| 79 |
| 80 private static final String TEST_FILE_PATH = "chrome/test/data/android/tabst
est/tabs_test.html"; |
| 81 private static final String TEST_PAGE_FILE_PATH = "chrome/test/data/google/g
oogle.html"; |
| 82 |
| 83 private float mPxToDp = 1.0f; |
| 84 private float mTabsViewHeightDp; |
| 85 private float mTabsViewWidthDp; |
| 86 |
| 87 private boolean mNotifyChangedCalled; |
| 88 |
| 89 private static final int SWIPE_TO_RIGHT_DIRECTION = 1; |
| 90 private static final int SWIPE_TO_LEFT_DIRECTION = -1; |
| 91 |
| 92 private static final long WAIT_RESIZE_TIMEOUT_MS = 3000; |
| 93 |
| 94 private static final int STRESSFUL_TAB_COUNT = 100; |
| 95 |
| 96 private static final String RESIZE_TEST_URL = UrlUtils.encodeHtmlDataUri( |
| 97 "<html><head><script>" |
| 98 + " var resizeHappened = false;" |
| 99 + " function onResize() {" |
| 100 + " resizeHappened = true;" |
| 101 + " document.getElementById('test').textContent =" |
| 102 + " window.innerWidth + 'x' + window.innerHeight;" |
| 103 + " }" |
| 104 + "</script></head>" |
| 105 + "<body onresize=\"onResize()\">" |
| 106 + " <div id=\"test\">No resize event has been received yet.</div>" |
| 107 + "</body></html>"); |
| 108 |
| 109 /** |
| 110 * Verify that spawning a popup from a background tab in a different model w
orks properly. |
| 111 * @throws InterruptedException |
| 112 * @throws TimeoutException |
| 113 */ |
| 114 @LargeTest |
| 115 @Feature({"Navigation"}) |
| 116 @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) |
| 117 public void testSpawnPopupOnBackgroundTab() throws InterruptedException, Tim
eoutException { |
| 118 loadUrl(UrlUtils.getIsolatedTestFileUrl(TEST_FILE_PATH)); |
| 119 final Tab tab = getActivity().getActivityTab(); |
| 120 |
| 121 newIncognitoTabFromMenu(); |
| 122 |
| 123 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 124 @Override |
| 125 public void run() { |
| 126 tab.getWebContents().evaluateJavaScript( |
| 127 "(function() {" |
| 128 + " window.open('www.google.com');" |
| 129 + "})()", |
| 130 null); |
| 131 } |
| 132 }); |
| 133 |
| 134 assertTrue("Tab never spawned in normal model.", |
| 135 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 136 @Override |
| 137 public boolean isSatisfied() { |
| 138 return getActivity().getTabModelSelector().getModel(fals
e).getCount() == 2; |
| 139 } |
| 140 })); |
| 141 } |
| 142 |
| 143 @MediumTest |
| 144 public void testAlertDialogDoesNotChangeActiveModel() throws InterruptedExce
ption { |
| 145 newIncognitoTabFromMenu(); |
| 146 loadUrl(UrlUtils.getIsolatedTestFileUrl(TEST_FILE_PATH)); |
| 147 final Tab tab = getActivity().getActivityTab(); |
| 148 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 149 @Override |
| 150 public void run() { |
| 151 tab.getWebContents().evaluateJavaScript( |
| 152 "(function() {" |
| 153 + " alert('hi');" |
| 154 + "})()", |
| 155 null); |
| 156 } |
| 157 }); |
| 158 |
| 159 final AtomicReference<JavascriptAppModalDialog> dialog = |
| 160 new AtomicReference<JavascriptAppModalDialog>(); |
| 161 |
| 162 CriteriaHelper.pollForCriteria(new Criteria() { |
| 163 @Override |
| 164 public boolean isSatisfied() { |
| 165 dialog.set(JavascriptAppModalDialog.getCurrentDialogForTest()); |
| 166 |
| 167 return dialog.get() != null; |
| 168 } |
| 169 }); |
| 170 |
| 171 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 172 @Override |
| 173 public void run() { |
| 174 dialog.get().onClick(null, DialogInterface.BUTTON_POSITIVE); |
| 175 } |
| 176 }); |
| 177 |
| 178 dialog.set(null); |
| 179 |
| 180 CriteriaHelper.pollForCriteria(new Criteria() { |
| 181 @Override |
| 182 public boolean isSatisfied() { |
| 183 return JavascriptAppModalDialog.getCurrentDialogForTest() == nul
l; |
| 184 } |
| 185 }); |
| 186 |
| 187 assertTrue("Incognito model was not selected", |
| 188 getActivity().getTabModelSelector().isIncognitoSelected()); |
| 189 } |
| 190 |
| 191 /** |
| 192 * Verify New Tab Open and Close Event not from the context menu. |
| 193 * @throws InterruptedException |
| 194 * |
| 195 * https://crbug.com/490473 |
| 196 * @LargeTest |
| 197 * @Feature({"Android-TabSwitcher"}) |
| 198 * @Restriction(RESTRICTION_TYPE_PHONE) |
| 199 */ |
| 200 @DisabledTest |
| 201 public void testOpenAndCloseNewTabButton() throws InterruptedException { |
| 202 startMainActivityWithURL(UrlUtils.getIsolatedTestFileUrl(TEST_FILE_PATH)
); |
| 203 getInstrumentation().runOnMainSync(new Runnable() { |
| 204 @Override |
| 205 public void run() { |
| 206 String title = getActivity().getCurrentTabModel().getTabAt(0).ge
tTitle(); |
| 207 assertEquals("Data file for TabsTest", title); |
| 208 } |
| 209 }); |
| 210 final int tabCount = getActivity().getCurrentTabModel().getCount(); |
| 211 OverviewModeBehaviorWatcher overviewModeWatcher = new OverviewModeBehavi
orWatcher( |
| 212 getActivity().getLayoutManager(), true, false); |
| 213 View tabSwitcherButton = getActivity().findViewById(R.id.tab_switcher_bu
tton); |
| 214 assertNotNull("'tab_switcher_button' view is not found", tabSwitcherButt
on); |
| 215 singleClickView(tabSwitcherButton, tabSwitcherButton.getWidth() / 2, |
| 216 tabSwitcherButton.getHeight() / 2); |
| 217 overviewModeWatcher.waitForBehavior(); |
| 218 overviewModeWatcher = new OverviewModeBehaviorWatcher( |
| 219 getActivity().getLayoutManager(), false, true); |
| 220 View newTabButton = getActivity().findViewById(R.id.new_tab_button); |
| 221 assertNotNull("'new_tab_button' view is not found", newTabButton); |
| 222 singleClickView(newTabButton, newTabButton.getWidth() / 2, newTabButton.
getHeight() / 2); |
| 223 overviewModeWatcher.waitForBehavior(); |
| 224 |
| 225 getInstrumentation().runOnMainSync(new Runnable() { |
| 226 @Override |
| 227 public void run() { |
| 228 assertEquals("The tab count is wrong", |
| 229 tabCount + 1, getActivity().getCurrentTabModel().getCoun
t()); |
| 230 } |
| 231 }); |
| 232 |
| 233 assertTrue(CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 234 @Override |
| 235 public boolean isSatisfied() { |
| 236 Tab tab = getActivity().getCurrentTabModel().getTabAt(1); |
| 237 String title = tab.getTitle().toLowerCase(); |
| 238 String expectedTitle = "new tab"; |
| 239 return title.startsWith(expectedTitle); |
| 240 } |
| 241 })); |
| 242 |
| 243 ChromeTabUtils.closeCurrentTab(getInstrumentation(), getActivity()); |
| 244 getInstrumentation().runOnMainSync(new Runnable() { |
| 245 @Override |
| 246 public void run() { |
| 247 assertEquals(tabCount, getActivity().getCurrentTabModel().getCou
nt()); |
| 248 } |
| 249 }); |
| 250 } |
| 251 |
| 252 static class SimulateClickOnMainThread implements Runnable { |
| 253 private final LayoutManagerChrome mLayoutManager; |
| 254 private final float mX; |
| 255 private final float mY; |
| 256 |
| 257 public SimulateClickOnMainThread(LayoutManagerChrome layoutManager, floa
t x, float y) { |
| 258 mLayoutManager = layoutManager; |
| 259 mX = x; |
| 260 mY = y; |
| 261 } |
| 262 |
| 263 @Override |
| 264 public void run() { |
| 265 mLayoutManager.simulateClick(mX, mY); |
| 266 } |
| 267 } |
| 268 |
| 269 static class SimulateTabSwipeOnMainThread implements Runnable { |
| 270 private final LayoutManagerChrome mLayoutManager; |
| 271 private final float mX; |
| 272 private final float mY; |
| 273 private final float mDeltaX; |
| 274 private final float mDeltaY; |
| 275 |
| 276 public SimulateTabSwipeOnMainThread(LayoutManagerChrome layoutManager, f
loat x, float y, |
| 277 float dX, float dY) { |
| 278 mLayoutManager = layoutManager; |
| 279 mX = x; |
| 280 mY = y; |
| 281 mDeltaX = dX; |
| 282 mDeltaY = dY; |
| 283 } |
| 284 |
| 285 @Override |
| 286 public void run() { |
| 287 mLayoutManager.simulateDrag(mX, mY, mDeltaX, mDeltaY); |
| 288 } |
| 289 } |
| 290 |
| 291 /** |
| 292 * Verify that the provided click position closes a tab. |
| 293 * @throws InterruptedException |
| 294 */ |
| 295 private void checkCloseTabAtPosition(final float x, final float y) |
| 296 throws InterruptedException { |
| 297 getActivity(); |
| 298 |
| 299 int initialTabCount = getActivity().getCurrentTabModel().getCount(); |
| 300 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 301 getInstrumentation().waitForIdleSync(); |
| 302 View button = getActivity().findViewById(R.id.tab_switcher_button); |
| 303 OverviewModeBehaviorWatcher overviewModeWatcher = new OverviewModeBehavi
orWatcher( |
| 304 getActivity().getLayoutManager(), true, false); |
| 305 singleClickView(button, button.getWidth() / 2, button.getHeight() / 2); |
| 306 overviewModeWatcher.waitForBehavior(); |
| 307 assertTrue("Expected: " + (initialTabCount + 1) + " tab Got: " |
| 308 + getActivity().getCurrentTabModel().getCount(), |
| 309 (initialTabCount + 1) == getActivity().getCurrentTabModel().getC
ount()); |
| 310 getInstrumentation().waitForIdleSync(); |
| 311 final LayoutManagerChrome layoutManager = updateTabsViewSize(); |
| 312 ChromeTabUtils.closeTabWithAction(getInstrumentation(), getActivity(), n
ew Runnable() { |
| 313 @Override |
| 314 public void run() { |
| 315 getInstrumentation().runOnMainSync( |
| 316 new SimulateClickOnMainThread(layoutManager, x, y)); |
| 317 } |
| 318 }); |
| 319 assertTrue("Expected: " + initialTabCount + " tab Got: " |
| 320 + getActivity().getCurrentTabModel().getCount(), |
| 321 initialTabCount == getActivity().getCurrentTabModel().getCount()
); |
| 322 } |
| 323 |
| 324 /** |
| 325 * Verify close button works in the TabSwitcher in portrait mode. |
| 326 * This code does not handle properly different screen densities. |
| 327 * @throws InterruptedException |
| 328 */ |
| 329 @LargeTest |
| 330 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 331 @Feature({"Android-TabSwitcher"}) |
| 332 public void testTabSwitcherPortraitCloseButton() throws InterruptedException
{ |
| 333 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PO
RTRAIT); |
| 334 int portraitWidth = Math.min(getActivity().getResources().getDisplayMetr
ics().widthPixels, |
| 335 getActivity().getResources().getDisplayMetrics().heightPixels); |
| 336 // Hard-coded coordinates of the close button on the top right of the sc
reen. |
| 337 // If the coordinates need to be updated, the easiest is to take a scree
nshot and measure. |
| 338 // Note that starting from the right of the screen should cover any scre
en size. |
| 339 checkCloseTabAtPosition(portraitWidth * mPxToDp - 32, 70); |
| 340 } |
| 341 |
| 342 /** |
| 343 * Verify close button works in the TabSwitcher in landscape mode. |
| 344 * This code does not handle properly different screen densities. |
| 345 * @throws InterruptedException |
| 346 * Bug: crbug.com/170179 |
| 347 * @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE
}) |
| 348 * @LargeTest |
| 349 * @Feature({"Android-TabSwitcher"}) |
| 350 */ |
| 351 @FlakyTest |
| 352 public void testTabSwitcherLandscapeCloseButton() throws InterruptedExceptio
n { |
| 353 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LA
NDSCAPE); |
| 354 // Hard-coded coordinates of the close button on the bottom left of the
screen. |
| 355 // If the coordinates need to be updated, the easiest is to take a scree
nshot and measure. |
| 356 checkCloseTabAtPosition(31 * mPxToDp, 31 * mPxToDp); |
| 357 } |
| 358 |
| 359 /** |
| 360 * Verify that we can open a large number of tabs without running out of |
| 361 * memory. This test waits for the NTP to load before opening the next one. |
| 362 * This is a LargeTest but because we're doing it "slowly", we need to furth
er scale |
| 363 * the timeout for adb am instrument and the various events. |
| 364 */ |
| 365 /* |
| 366 * @EnormousTest |
| 367 * @TimeoutScale(10) |
| 368 * @Feature({"Android-TabSwitcher"}) |
| 369 * Bug crbug.com/166208 |
| 370 */ |
| 371 @DisabledTest |
| 372 public void testOpenManyTabsSlowly() throws InterruptedException { |
| 373 int startCount = getActivity().getCurrentTabModel().getCount(); |
| 374 for (int i = 1; i <= STRESSFUL_TAB_COUNT; ++i) { |
| 375 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 376 getInstrumentation().waitForIdleSync(); |
| 377 assertEquals(startCount + i, getActivity().getCurrentTabModel().getC
ount()); |
| 378 } |
| 379 } |
| 380 |
| 381 /** |
| 382 * Verify that we can open a large number of tabs without running out of |
| 383 * memory. This test hammers the "new tab" button quickly to stress the app. |
| 384 * |
| 385 * @LargeTest |
| 386 * @TimeoutScale(10) |
| 387 * @Feature({"Android-TabSwitcher"}) |
| 388 * |
| 389 */ |
| 390 @FlakyTest |
| 391 public void testOpenManyTabsQuickly() { |
| 392 int startCount = getActivity().getCurrentTabModel().getCount(); |
| 393 for (int i = 1; i <= STRESSFUL_TAB_COUNT; ++i) { |
| 394 MenuUtils.invokeCustomMenuActionSync(getInstrumentation(), getActivi
ty(), |
| 395 R.id.new_tab_menu_id); |
| 396 assertEquals(startCount + i, getActivity().getCurrentTabModel().getC
ount()); |
| 397 } |
| 398 } |
| 399 |
| 400 /** |
| 401 * Verify that we can open a burst of new tabs, even when there are already |
| 402 * a large number of tabs open. |
| 403 * Bug: crbug.com/180718 |
| 404 * @EnormousTest |
| 405 * @TimeoutScale(30) |
| 406 * @Feature({"Navigation"}) |
| 407 */ |
| 408 @FlakyTest |
| 409 public void testOpenManyTabsInBursts() throws InterruptedException { |
| 410 final int burstSize = 5; |
| 411 final String url = TestHttpServerClient.getUrl(TEST_PAGE_FILE_PATH); |
| 412 final int startCount = getActivity().getCurrentTabModel().getCount(); |
| 413 for (int tabCount = startCount; tabCount < STRESSFUL_TAB_COUNT; tabCount
+= burstSize) { |
| 414 loadUrlInManyNewTabs(url, burstSize); |
| 415 assertEquals(tabCount + burstSize, getActivity().getCurrentTabModel(
).getCount()); |
| 416 } |
| 417 } |
| 418 |
| 419 /** |
| 420 * Verify opening 10 tabs at once and that each tab loads when selected. |
| 421 */ |
| 422 /* |
| 423 * @EnormousTest |
| 424 * @TimeoutScale(30) |
| 425 * @Feature({"Navigation"}) |
| 426 * crbug/223110 |
| 427 */ |
| 428 @FlakyTest |
| 429 public void testOpenManyTabsAtOnce10() throws InterruptedException { |
| 430 openAndVerifyManyTestTabs(10); |
| 431 } |
| 432 |
| 433 /** |
| 434 * Verify that we can open a large number of tabs all at once and that each |
| 435 * tab loads when selected. |
| 436 */ |
| 437 private void openAndVerifyManyTestTabs(final int num) throws InterruptedExce
ption { |
| 438 final String url = TestHttpServerClient.getUrl(TEST_PAGE_FILE_PATH); |
| 439 int startCount = getActivity().getCurrentTabModel().getCount(); |
| 440 loadUrlInManyNewTabs(url, num); |
| 441 assertEquals(startCount + num, |
| 442 getActivity().getCurrentTabModel().getCount()); |
| 443 } |
| 444 |
| 445 class ClickOptionButtonOnMainThread implements Runnable { |
| 446 @Override |
| 447 public void run() { |
| 448 // This is equivalent to clickById(R.id.tab_switcher_button) but doe
s not rely on the |
| 449 // event pipeline. |
| 450 View button = getActivity().findViewById(R.id.tab_switcher_button); |
| 451 assertNotNull("Could not find view R.id.tab_switcher_button", button
); |
| 452 View toolbar = getActivity().findViewById(R.id.toolbar); |
| 453 assertNotNull("Could not find view R.id.toolbar", toolbar); |
| 454 assertTrue("R.id.toolbar is not a ToolbarPhone", toolbar instanceof
ToolbarPhone); |
| 455 ((ToolbarPhone) toolbar).onClick(button); |
| 456 } |
| 457 } |
| 458 |
| 459 /** |
| 460 * Displays the tabSwitcher mode and wait for it to settle. |
| 461 */ |
| 462 private void showOverviewAndWaitForAnimation() throws InterruptedException { |
| 463 OverviewModeBehaviorWatcher overviewModeWatcher = new OverviewModeBehavi
orWatcher( |
| 464 getActivity().getLayoutManager(), true, false); |
| 465 // For some unknown reasons calling clickById(R.id.tab_switcher_button)
sometimes hang. |
| 466 // The following is verbose but more reliable. |
| 467 getInstrumentation().runOnMainSync(new ClickOptionButtonOnMainThread()); |
| 468 overviewModeWatcher.waitForBehavior(); |
| 469 } |
| 470 |
| 471 /** |
| 472 * Exits the tabSwitcher mode and wait for it to settle. |
| 473 */ |
| 474 private void hideOverviewAndWaitForAnimation() throws InterruptedException { |
| 475 OverviewModeBehaviorWatcher overviewModeWatcher = new OverviewModeBehavi
orWatcher( |
| 476 getActivity().getLayoutManager(), false, true); |
| 477 getInstrumentation().runOnMainSync(new ClickOptionButtonOnMainThread()); |
| 478 overviewModeWatcher.waitForBehavior(); |
| 479 } |
| 480 |
| 481 /** |
| 482 * Opens tabs to populate the model to a given count. |
| 483 * @param targetTabCount The desired number of tabs in the model. |
| 484 * @param waitToLoad Whether the tabs need to be fully loaded. |
| 485 * @return The new number of tabs in the model. |
| 486 * @throws InterruptedException |
| 487 */ |
| 488 private int openTabs(final int targetTabCount, boolean waitToLoad) throws In
terruptedException { |
| 489 int tabCount = getActivity().getCurrentTabModel().getCount(); |
| 490 while (tabCount < targetTabCount) { |
| 491 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 492 tabCount++; |
| 493 assertEquals("The tab count is wrong", |
| 494 tabCount, getActivity().getCurrentTabModel().getCount()); |
| 495 Tab tab = TabModelUtils.getCurrentTab(getActivity().getCurrentTabMod
el()); |
| 496 while (waitToLoad && tab.isLoading()) { |
| 497 Thread.yield(); |
| 498 } |
| 499 } |
| 500 return tabCount; |
| 501 } |
| 502 |
| 503 /** |
| 504 * Verifies that when more than 9 tabs are open only at most 8 are drawn. Ba
sically it verifies |
| 505 * that the tab culling mechanism works properly. |
| 506 */ |
| 507 /* |
| 508 @LargeTest |
| 509 @Feature({"Android-TabSwitcher"}) |
| 510 Bug http://crbug.com/156746 |
| 511 */ |
| 512 @DisabledTest |
| 513 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 514 public void testTabsCulling() throws InterruptedException { |
| 515 // Open one more tabs than maxTabsDrawn. |
| 516 final int maxTabsDrawn = 8; |
| 517 int tabCount = openTabs(maxTabsDrawn + 1, false); |
| 518 showOverviewAndWaitForAnimation(); |
| 519 |
| 520 // Check counts. |
| 521 LayoutManagerChromePhone layoutManager = |
| 522 (LayoutManagerChromePhone) getActivity().getLayoutManager(); |
| 523 int drawnCount = layoutManager.getOverviewLayout().getLayoutTabsDrawnCou
nt(); |
| 524 int drawnExpected = Math.min(tabCount, maxTabsDrawn); |
| 525 assertEquals("The number of drawn tab is wrong", drawnExpected, drawnCou
nt); |
| 526 } |
| 527 |
| 528 /** |
| 529 * Checks the stacked tabs in the stack are visible. |
| 530 * @throws InterruptedException |
| 531 */ |
| 532 private void checkTabsStacking() throws InterruptedException { |
| 533 final int count = getActivity().getCurrentTabModel().getCount(); |
| 534 assertEquals("The number of tab in the stack should match the number of
tabs in the model", |
| 535 count, getLayoutTabInStackCount(false)); |
| 536 |
| 537 assertTrue("The selected tab should always be visible", |
| 538 stackTabIsVisible(false, getActivity().getCurrentTabModel().inde
x())); |
| 539 for (int i = 0; i < Stack.MAX_NUMBER_OF_STACKED_TABS_TOP && i < count; i
++) { |
| 540 assertTrue("The stacked tab " + i + " from the top should always be
visible", |
| 541 stackTabIsVisible(false, i)); |
| 542 } |
| 543 for (int i = 0; i < Stack.MAX_NUMBER_OF_STACKED_TABS_BOTTOM && i < count
; i++) { |
| 544 assertTrue("The stacked tab " + i + " from the bottom should always
be visible", |
| 545 stackTabIsVisible(false, count - 1 - i)); |
| 546 } |
| 547 } |
| 548 |
| 549 /** |
| 550 * Verifies that the tab are actually stacking at the bottom and top of the
screen. |
| 551 */ |
| 552 /** |
| 553 * Bug: crbug.com/170179 |
| 554 * @LargeTest |
| 555 * @Feature({"Android-TabSwitcher"}) |
| 556 */ |
| 557 @FlakyTest |
| 558 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 559 public void testTabsStacking() throws InterruptedException { |
| 560 final int count = openTabs(12, false); |
| 561 |
| 562 // Selecting the first tab to scroll all the way to the top. |
| 563 getInstrumentation().runOnMainSync(new Runnable() { |
| 564 @Override |
| 565 public void run() { |
| 566 TabModelUtils.setIndex(getActivity().getCurrentTabModel(), 0); |
| 567 } |
| 568 }); |
| 569 showOverviewAndWaitForAnimation(); |
| 570 checkTabsStacking(); |
| 571 |
| 572 // Selecting the last tab to scroll all the way to the bottom. |
| 573 hideOverviewAndWaitForAnimation(); |
| 574 getInstrumentation().runOnMainSync(new Runnable() { |
| 575 @Override |
| 576 public void run() { |
| 577 TabModelUtils.setIndex(getActivity().getCurrentTabModel(), count
- 1); |
| 578 } |
| 579 }); |
| 580 showOverviewAndWaitForAnimation(); |
| 581 checkTabsStacking(); |
| 582 } |
| 583 |
| 584 /** |
| 585 * @return A stable read of allocated size (native + dalvik) after gc. |
| 586 */ |
| 587 @SuppressFBWarnings("DM_GC") |
| 588 private long getStableAllocatedSize() { |
| 589 // Measure the equivalent of allocated size native + dalvik in: |
| 590 // adb shell dumpsys meminfo | grep chrome -A 20 |
| 591 int maxTries = 8; |
| 592 int tries = 0; |
| 593 long threshold = 512; // bytes |
| 594 long lastAllocatedSize = Long.MAX_VALUE; |
| 595 long currentAllocatedSize = 0; |
| 596 while (tries < maxTries && Math.abs(currentAllocatedSize - lastAllocated
Size) > threshold) { |
| 597 System.gc(); |
| 598 try { |
| 599 Thread.sleep(1000 + tries * 500); // Memory measurement is not a
n exact science... |
| 600 lastAllocatedSize = currentAllocatedSize; |
| 601 currentAllocatedSize = Debug.getNativeHeapAllocatedSize() |
| 602 + Runtime.getRuntime().totalMemory(); |
| 603 //Log.w("MEMORY_MEASURE", "[" + tries + "/" + maxTries + "]" + |
| 604 // "currentAllocatedSize " + currentAllocatedSize); |
| 605 } catch (InterruptedException e) { |
| 606 e.printStackTrace(); |
| 607 } |
| 608 tries++; |
| 609 } |
| 610 assertTrue("Could not have a stable read on native allocated size even a
fter " |
| 611 + tries + " gc.", tries < maxTries); |
| 612 return currentAllocatedSize; |
| 613 } |
| 614 |
| 615 /** |
| 616 * Verify that switching back and forth to the tabswitcher does not leak mem
ory. |
| 617 */ |
| 618 /** |
| 619 * Bug: crbug.com/303319 |
| 620 * @LargeTest |
| 621 * @Feature({"Android-TabSwitcher"}) |
| 622 */ |
| 623 @FlakyTest |
| 624 @Restriction(RESTRICTION_TYPE_PHONE) |
| 625 public void testTabSwitcherMemoryLeak() throws InterruptedException { |
| 626 openTabs(4, true); |
| 627 |
| 628 int maxTries = 10; |
| 629 int tries = 0; |
| 630 long threshold = 1024; // bytes |
| 631 long lastAllocatedSize = 0; |
| 632 long currentAllocatedSize = 2 * threshold; |
| 633 while (tries < maxTries && (lastAllocatedSize + threshold) < currentAllo
catedSize) { |
| 634 showOverviewAndWaitForAnimation(); |
| 635 |
| 636 lastAllocatedSize = currentAllocatedSize; |
| 637 currentAllocatedSize = getStableAllocatedSize(); |
| 638 //Log.w("MEMORY_TEST", "[" + tries + "/" + maxTries + "]" + |
| 639 // "currentAllocatedSize " + currentAllocatedSize); |
| 640 |
| 641 hideOverviewAndWaitForAnimation(); |
| 642 tries++; |
| 643 } |
| 644 |
| 645 assertTrue("Native heap allocated size keeps increasing even after " |
| 646 + tries + " iterations", tries < maxTries); |
| 647 } |
| 648 |
| 649 /** |
| 650 * Verify that switching back and forth stay stable. This test last for at l
east 8 seconds. |
| 651 */ |
| 652 @LargeTest |
| 653 @Restriction(RESTRICTION_TYPE_PHONE) |
| 654 @Feature({"Android-TabSwitcher"}) |
| 655 public void testTabSwitcherStability() throws InterruptedException { |
| 656 openTabs(8, true); |
| 657 |
| 658 // This is about as fast as you can ever click. |
| 659 final long fastestUserInput = 20; // ms |
| 660 for (int i = 0; i < 200; i++) { |
| 661 // Show overview |
| 662 getInstrumentation().runOnMainSync(new ClickOptionButtonOnMainThread
()); |
| 663 Thread.sleep(fastestUserInput); |
| 664 |
| 665 // hide overview |
| 666 getInstrumentation().runOnMainSync(new ClickOptionButtonOnMainThread
()); |
| 667 Thread.sleep(fastestUserInput); |
| 668 } |
| 669 } |
| 670 |
| 671 @LargeTest |
| 672 @Feature({"Android-TabSwitcher"}) |
| 673 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 674 public void testTabSelectionPortrait() throws InterruptedException { |
| 675 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PO
RTRAIT); |
| 676 checkTabSelection(2, 0, false); |
| 677 |
| 678 // Ensure all tabs following the selected tab are off the screen when th
e animation is |
| 679 // complete. |
| 680 final int count = getLayoutTabInStackCount(false); |
| 681 for (int i = 1; i < count; i++) { |
| 682 float y = getLayoutTabInStackXY(false, i)[1]; |
| 683 assertTrue( |
| 684 String.format("Tab %d's final draw Y, %f, should exceed the
view height, %f.", |
| 685 i, y, mTabsViewHeightDp), |
| 686 y >= mTabsViewHeightDp); |
| 687 } |
| 688 } |
| 689 |
| 690 /** |
| 691 * Bug: crbug.com/170179 |
| 692 * @LargeTest |
| 693 * @Feature({"Android-TabSwitcher"}) |
| 694 */ |
| 695 @FlakyTest |
| 696 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 697 public void testTabSelectionLandscape() throws InterruptedException { |
| 698 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LA
NDSCAPE); |
| 699 checkTabSelection(2, 0, true); |
| 700 |
| 701 // Ensure all tabs following the selected tab are off the screen when th
e animation is |
| 702 // complete. |
| 703 final int count = getLayoutTabInStackCount(false); |
| 704 for (int i = 1; i < count; i++) { |
| 705 float x = getLayoutTabInStackXY(false, i)[0]; |
| 706 assertTrue( |
| 707 String.format("Tab %d's final draw X, %f, should exceed the
view width, %f.", |
| 708 i, x, mTabsViewWidthDp), |
| 709 x >= mTabsViewWidthDp); |
| 710 } |
| 711 } |
| 712 |
| 713 /** |
| 714 * Verify that we don't crash and show the overview mode after closing the l
ast tab. |
| 715 */ |
| 716 @SmallTest |
| 717 @Restriction(RESTRICTION_TYPE_PHONE) |
| 718 @Feature({"Android-TabSwitcher"}) |
| 719 public void testCloseLastTabFromMain() throws InterruptedException { |
| 720 OverviewModeBehaviorWatcher overviewModeWatcher = new OverviewModeBehavi
orWatcher( |
| 721 getActivity().getLayoutManager(), true, false); |
| 722 ChromeTabUtils.closeCurrentTab(getInstrumentation(), getActivity()); |
| 723 getInstrumentation().waitForIdleSync(); |
| 724 overviewModeWatcher.waitForBehavior(); |
| 725 } |
| 726 |
| 727 private LayoutManagerChrome updateTabsViewSize() { |
| 728 View tabsView = getActivity().getTabsView(); |
| 729 mTabsViewHeightDp = tabsView.getHeight() * mPxToDp; |
| 730 mTabsViewWidthDp = tabsView.getWidth() * mPxToDp; |
| 731 return getActivity().getLayoutManager(); |
| 732 } |
| 733 |
| 734 private Stack getStack(final LayoutManagerChrome layoutManager, boolean isIn
cognito) { |
| 735 assertTrue("getStack must be executed on the ui thread", |
| 736 ThreadUtils.runningOnUiThread()); |
| 737 LayoutManagerChromePhone layoutManagerPhone = (LayoutManagerChromePhone)
layoutManager; |
| 738 StackLayout layout = (StackLayout) layoutManagerPhone.getOverviewLayout(
); |
| 739 return (layout).getTabStack(isIncognito); |
| 740 } |
| 741 |
| 742 private int getLayoutTabInStackCount(final boolean isIncognito) { |
| 743 final LayoutManagerChrome layoutManager = updateTabsViewSize(); |
| 744 final int[] count = new int[1]; |
| 745 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 746 @Override |
| 747 public void run() { |
| 748 Stack stack = getStack(layoutManager, isIncognito); |
| 749 count[0] = stack.getTabs().length; |
| 750 } |
| 751 }); |
| 752 return count[0]; |
| 753 } |
| 754 |
| 755 private boolean stackTabIsVisible(final boolean isIncognito, final int index
) { |
| 756 final LayoutManagerChrome layoutManager = updateTabsViewSize(); |
| 757 final boolean[] isVisible = new boolean[1]; |
| 758 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 759 @Override |
| 760 public void run() { |
| 761 Stack stack = getStack(layoutManager, isIncognito); |
| 762 isVisible[0] = (stack.getTabs())[index].getLayoutTab().isVisible
(); |
| 763 } |
| 764 }); |
| 765 return isVisible[0]; |
| 766 } |
| 767 |
| 768 private float[] getLayoutTabInStackXY(final boolean isIncognito, final int i
ndex) { |
| 769 final LayoutManagerChrome layoutManager = updateTabsViewSize(); |
| 770 final float[] xy = new float[2]; |
| 771 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 772 @Override |
| 773 public void run() { |
| 774 Stack stack = getStack(layoutManager, isIncognito); |
| 775 xy[0] = (stack.getTabs())[index].getLayoutTab().getX(); |
| 776 xy[1] = (stack.getTabs())[index].getLayoutTab().getY(); |
| 777 } |
| 778 }); |
| 779 return xy; |
| 780 } |
| 781 |
| 782 private float[] getStackTabClickTarget(final int tabIndexToSelect, final boo
lean isIncognito, |
| 783 final boolean isLandscape) { |
| 784 final LayoutManagerChrome layoutManager = updateTabsViewSize(); |
| 785 final float[] target = new float[2]; |
| 786 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 787 @Override |
| 788 public void run() { |
| 789 Stack stack = getStack(layoutManager, isIncognito); |
| 790 StackTab[] tabs = stack.getTabs(); |
| 791 // The position of the click is expressed from the top left corn
er of the content. |
| 792 // The aim is to find an offset that is inside the content but n
ot on the close |
| 793 // button. For this, we calculate the center of the visible tab
area. |
| 794 LayoutTab layoutTab = tabs[tabIndexToSelect].getLayoutTab(); |
| 795 LayoutTab nextLayoutTab = (tabIndexToSelect + 1) < tabs.length |
| 796 ? tabs[tabIndexToSelect + 1].getLayoutTab() : null; |
| 797 |
| 798 float tabOffsetX = layoutTab.getX(); |
| 799 float tabOffsetY = layoutTab.getY(); |
| 800 float tabRightX, tabBottomY; |
| 801 if (isLandscape) { |
| 802 tabRightX = nextLayoutTab != null |
| 803 ? nextLayoutTab.getX() |
| 804 : tabOffsetX + layoutTab.getScaledContentWidth(); |
| 805 tabBottomY = tabOffsetY + layoutTab.getScaledContentHeight()
; |
| 806 } else { |
| 807 tabRightX = tabOffsetX + layoutTab.getScaledContentWidth(); |
| 808 tabBottomY = nextLayoutTab != null |
| 809 ? nextLayoutTab.getY() |
| 810 : tabOffsetY + layoutTab.getScaledContentHeight(); |
| 811 } |
| 812 tabRightX = Math.min(tabRightX, mTabsViewWidthDp); |
| 813 tabBottomY = Math.min(tabBottomY, mTabsViewHeightDp); |
| 814 |
| 815 target[0] = (tabOffsetX + tabRightX) / 2.0f; |
| 816 target[1] = (tabOffsetY + tabBottomY) / 2.0f; |
| 817 } |
| 818 }); |
| 819 return target; |
| 820 } |
| 821 |
| 822 private void checkTabSelection(int additionalTabsToOpen, int tabIndexToSelec
t, |
| 823 boolean isLandscape) throws InterruptedException { |
| 824 for (int i = 0; i < additionalTabsToOpen; i++) { |
| 825 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 826 } |
| 827 assertEquals("Number of open tabs does not match", |
| 828 additionalTabsToOpen + 1 , getActivity().getCurrentTabModel().ge
tCount()); |
| 829 showOverviewAndWaitForAnimation(); |
| 830 |
| 831 float[] coordinates = getStackTabClickTarget(tabIndexToSelect, false, is
Landscape); |
| 832 float clickX = coordinates[0]; |
| 833 float clickY = coordinates[1]; |
| 834 |
| 835 OverviewModeBehaviorWatcher overviewModeWatcher = new OverviewModeBehavi
orWatcher( |
| 836 getActivity().getLayoutManager(), false, true); |
| 837 |
| 838 final LayoutManagerChrome layoutManager = updateTabsViewSize(); |
| 839 getInstrumentation().runOnMainSync(new SimulateClickOnMainThread(layoutM
anager, |
| 840 (int) clickX, (int) clickY)); |
| 841 overviewModeWatcher.waitForBehavior(); |
| 842 |
| 843 // Make sure we did not accidentally close a tab. |
| 844 assertEquals("Number of open tabs does not match", |
| 845 additionalTabsToOpen + 1 , getActivity().getCurrentTabModel().ge
tCount()); |
| 846 } |
| 847 |
| 848 public void swipeToCloseTab(final int tabIndexToClose, final boolean isLands
cape, |
| 849 final boolean isIncognito, final int swipeDirection) throws Interrup
tedException { |
| 850 final LayoutManagerChrome layoutManager = updateTabsViewSize(); |
| 851 float[] coordinates = getStackTabClickTarget(tabIndexToClose, isIncognit
o, isLandscape); |
| 852 final float clickX = coordinates[0]; |
| 853 final float clickY = coordinates[1]; |
| 854 Log.v("ChromeTest", String.format("clickX %f clickY %f", clickX, clickY)
); |
| 855 |
| 856 ChromeTabUtils.closeTabWithAction(getInstrumentation(), getActivity(), n
ew Runnable() { |
| 857 @Override |
| 858 public void run() { |
| 859 if (isLandscape) { |
| 860 getInstrumentation().runOnMainSync(new SimulateTabSwipeOnMai
nThread( |
| 861 layoutManager, clickX, clickY, 0, swipeDirection * m
TabsViewWidthDp)); |
| 862 } else { |
| 863 getInstrumentation().runOnMainSync(new SimulateTabSwipeOnMai
nThread( |
| 864 layoutManager, clickX, clickY, swipeDirection * mTab
sViewHeightDp, 0)); |
| 865 } |
| 866 } |
| 867 }); |
| 868 assertTrue("Did not finish animation", |
| 869 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 870 @Override |
| 871 public boolean isSatisfied() { |
| 872 Layout layout = getActivity().getLayoutManager().getActi
veLayout(); |
| 873 return !layout.isLayoutAnimating(); |
| 874 } |
| 875 })); |
| 876 } |
| 877 |
| 878 private void swipeToCloseNTabs(int number, boolean isLandscape, boolean isIn
cognito, |
| 879 int swipeDirection) |
| 880 throws InterruptedException { |
| 881 |
| 882 for (int i = number - 1; i >= 0; i--) { |
| 883 swipeToCloseTab(i, isLandscape, isIncognito, swipeDirection); |
| 884 } |
| 885 } |
| 886 |
| 887 /** |
| 888 * Test closing few tabs by swiping them in Overview portrait mode. |
| 889 */ |
| 890 @MediumTest |
| 891 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 892 @Feature({"Android-TabSwitcher", "Main"}) |
| 893 public void testCloseTabPortrait() throws InterruptedException { |
| 894 startMainActivityWithURL(TestHttpServerClient.getUrl("chrome/test/data/a
ndroid/test.html")); |
| 895 |
| 896 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PO
RTRAIT); |
| 897 |
| 898 int tabCount = getActivity().getCurrentTabModel().getCount(); |
| 899 ChromeTabUtils.newTabsFromMenu(getInstrumentation(), getActivity(), 3); |
| 900 assertEquals("wrong count after new tabs", tabCount + 3, |
| 901 getActivity().getCurrentTabModel().getCount()); |
| 902 |
| 903 showOverviewAndWaitForAnimation(); |
| 904 swipeToCloseNTabs(3, false, false, SWIPE_TO_LEFT_DIRECTION); |
| 905 |
| 906 assertEquals("Wrong tab counts after closing a few of them", |
| 907 tabCount, getActivity().getCurrentTabModel().getCount()); |
| 908 } |
| 909 |
| 910 /** |
| 911 * Test closing few tabs by swiping them in Overview landscape mode. |
| 912 */ |
| 913 @MediumTest |
| 914 @Feature({"Android-TabSwitcher", "Main"}) |
| 915 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 916 public void testCloseTabLandscape() throws InterruptedException { |
| 917 startMainActivityWithURL(TestHttpServerClient.getUrl("chrome/test/data/a
ndroid/test.html")); |
| 918 |
| 919 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LA
NDSCAPE); |
| 920 |
| 921 int tabCount = getActivity().getCurrentTabModel().getCount(); |
| 922 ChromeTabUtils.newTabsFromMenu(getInstrumentation(), getActivity(), 3); |
| 923 assertEquals("wrong count after new tabs", tabCount + 3, |
| 924 getActivity().getCurrentTabModel().getCount()); |
| 925 |
| 926 showOverviewAndWaitForAnimation(); |
| 927 swipeToCloseTab(0, true, false, SWIPE_TO_LEFT_DIRECTION); |
| 928 swipeToCloseTab(0, true, false, SWIPE_TO_LEFT_DIRECTION); |
| 929 swipeToCloseTab(0, true, false, SWIPE_TO_LEFT_DIRECTION); |
| 930 |
| 931 assertEquals("Wrong tab counts after closing a few of them", |
| 932 tabCount, getActivity().getCurrentTabModel().getCount()); |
| 933 } |
| 934 |
| 935 /** |
| 936 * Test close Incognito tab by swiping in Overview Portrait mode. |
| 937 */ |
| 938 @MediumTest |
| 939 @Feature({"Android-TabSwitcher"}) |
| 940 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 941 public void testCloseIncognitoTabPortrait() throws InterruptedException { |
| 942 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PO
RTRAIT); |
| 943 newIncognitoTabsFromMenu(2); |
| 944 |
| 945 showOverviewAndWaitForAnimation(); |
| 946 UiUtils.settleDownUI(getInstrumentation()); |
| 947 swipeToCloseNTabs(2, false, true, SWIPE_TO_LEFT_DIRECTION); |
| 948 } |
| 949 |
| 950 /** |
| 951 * Test close 5 Incognito tabs by swiping in Overview Portrait mode. |
| 952 */ |
| 953 @Feature({"Android-TabSwitcher"}) |
| 954 @MediumTest |
| 955 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 956 public void testCloseFiveIncognitoTabPortrait() throws InterruptedException
{ |
| 957 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PO
RTRAIT); |
| 958 newIncognitoTabsFromMenu(5); |
| 959 |
| 960 showOverviewAndWaitForAnimation(); |
| 961 UiUtils.settleDownUI(getInstrumentation()); |
| 962 swipeToCloseNTabs(5, false, true, SWIPE_TO_LEFT_DIRECTION); |
| 963 } |
| 964 |
| 965 /** |
| 966 * Simple swipe gesture should not close tabs when two Tabstacks are open in
Overview mode. |
| 967 * Test in Portrait Mode. |
| 968 */ |
| 969 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 970 @MediumTest |
| 971 @Feature({"Android-TabSwitcher"}) |
| 972 public void testSwitchTabStackWithoutClosingTabsInPortrait() throws Interrup
tedException { |
| 973 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PO
RTRAIT); |
| 974 newIncognitoTabFromMenu(); |
| 975 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 976 |
| 977 showOverviewAndWaitForAnimation(); |
| 978 UiUtils.settleDownUI(getInstrumentation()); |
| 979 final int normalTabCount = getLayoutTabInStackCount(false); |
| 980 final int incognitoTabCount = getLayoutTabInStackCount(true); |
| 981 |
| 982 LayoutManagerChrome layoutManager = updateTabsViewSize(); |
| 983 |
| 984 // Swipe to Incognito Tabs. |
| 985 getInstrumentation().runOnMainSync(new SimulateTabSwipeOnMainThread(layo
utManager, |
| 986 mTabsViewWidthDp - 20 , mTabsViewHeightDp / 2 , |
| 987 SWIPE_TO_LEFT_DIRECTION * mTabsViewWidthDp, 0)); |
| 988 UiUtils.settleDownUI(getInstrumentation()); |
| 989 assertTrue("Tabs Stack should have been changed to incognito.", |
| 990 getActivity().getCurrentTabModel().isIncognito()); |
| 991 assertEquals("Normal tabs count should be unchanged while switching to i
ncognito tabs.", |
| 992 normalTabCount, getLayoutTabInStackCount(false)); |
| 993 |
| 994 // Swipe to regular Tabs. |
| 995 getInstrumentation().runOnMainSync(new SimulateTabSwipeOnMainThread(layo
utManager, |
| 996 20, mTabsViewHeightDp / 2, |
| 997 SWIPE_TO_RIGHT_DIRECTION * mTabsViewWidthDp, 0)); |
| 998 UiUtils.settleDownUI(getInstrumentation()); |
| 999 assertEquals("Incognito tabs count should be unchanged while switching b
ack to normal " |
| 1000 + "tab stack.", incognitoTabCount, getLayoutTabInStackCount(true
)); |
| 1001 assertFalse("Tabs Stack should have been changed to regular tabs.", |
| 1002 getActivity().getCurrentTabModel().isIncognito()); |
| 1003 assertEquals("Normal tabs count should be unchanged while switching back
to normal tabs.", |
| 1004 normalTabCount, getLayoutTabInStackCount(false)); |
| 1005 } |
| 1006 |
| 1007 /** |
| 1008 * Simple swipe gesture should not close tabs when two Tabstacks are open in
Overview mode. |
| 1009 * Test in Landscape Mode. |
| 1010 */ |
| 1011 /* |
| 1012 @MediumTest |
| 1013 @Feature({"Android-TabSwitcher"}) |
| 1014 Bug http://crbug.com/157259 |
| 1015 */ |
| 1016 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 1017 @DisabledTest |
| 1018 public void testSwitchTabStackWithoutClosingTabsInLandscape() throws Interru
ptedException { |
| 1019 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LA
NDSCAPE); |
| 1020 newIncognitoTabFromMenu(); |
| 1021 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 1022 |
| 1023 showOverviewAndWaitForAnimation(); |
| 1024 UiUtils.settleDownUI(getInstrumentation()); |
| 1025 final int normalTabCount = getLayoutTabInStackCount(false); |
| 1026 final int incognitoTabCount = getLayoutTabInStackCount(true); |
| 1027 |
| 1028 LayoutManagerChrome layoutManager = updateTabsViewSize(); |
| 1029 |
| 1030 // Swipe to Incognito Tabs. |
| 1031 getInstrumentation().runOnMainSync(new SimulateTabSwipeOnMainThread(layo
utManager, |
| 1032 mTabsViewWidthDp / 2 , mTabsViewHeightDp - 20 , |
| 1033 0, SWIPE_TO_LEFT_DIRECTION * mTabsViewWidthDp)); |
| 1034 UiUtils.settleDownUI(getInstrumentation()); |
| 1035 assertTrue("Tabs Stack should have been changed to incognito.", |
| 1036 getActivity().getCurrentTabModel().isIncognito()); |
| 1037 assertEquals("Normal tabs count should be unchanged while switching to i
ncognito tabs.", |
| 1038 normalTabCount, getLayoutTabInStackCount(false)); |
| 1039 |
| 1040 // Swipe to regular Tabs. |
| 1041 getInstrumentation().runOnMainSync(new SimulateTabSwipeOnMainThread(layo
utManager, |
| 1042 mTabsViewWidthDp / 2, 20, |
| 1043 0, SWIPE_TO_RIGHT_DIRECTION * mTabsViewWidthDp)); |
| 1044 UiUtils.settleDownUI(getInstrumentation()); |
| 1045 assertEquals("Incognito tabs count should be unchanged while switching b
ack to normal " |
| 1046 + "tab stack.", incognitoTabCount, getLayoutTabInStackCount(true
)); |
| 1047 assertFalse("Tabs Stack should have been changed to regular tabs.", |
| 1048 getActivity().getCurrentTabModel().isIncognito()); |
| 1049 assertEquals("Normal tabs count should be unchanged while switching back
to normal tabs.", |
| 1050 normalTabCount, getLayoutTabInStackCount(false)); |
| 1051 } |
| 1052 |
| 1053 /** |
| 1054 * Test close Incognito tab by swiping in Overview Landscape mode. |
| 1055 */ |
| 1056 @MediumTest |
| 1057 @Feature({"Android-TabSwitcher"}) |
| 1058 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 1059 public void testCloseIncognitoTabLandscape() throws InterruptedException { |
| 1060 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LA
NDSCAPE); |
| 1061 newIncognitoTabFromMenu(); |
| 1062 |
| 1063 showOverviewAndWaitForAnimation(); |
| 1064 UiUtils.settleDownUI(getInstrumentation()); |
| 1065 swipeToCloseTab(0, true, true, SWIPE_TO_LEFT_DIRECTION); |
| 1066 } |
| 1067 |
| 1068 /** |
| 1069 * Test close 5 Incognito tabs by swiping in Overview Landscape mode. |
| 1070 */ |
| 1071 @MediumTest |
| 1072 @Feature({"Android-TabSwitcher"}) |
| 1073 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 1074 public void testCloseFiveIncognitoTabLandscape() throws InterruptedException
{ |
| 1075 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LA
NDSCAPE); |
| 1076 newIncognitoTabsFromMenu(5); |
| 1077 |
| 1078 showOverviewAndWaitForAnimation(); |
| 1079 UiUtils.settleDownUI(getInstrumentation()); |
| 1080 swipeToCloseNTabs(5, true, true, SWIPE_TO_LEFT_DIRECTION); |
| 1081 } |
| 1082 |
| 1083 /** |
| 1084 * Test that we can safely close a tab during a fling (http://b/issue?id=536
4043) |
| 1085 */ |
| 1086 @SmallTest |
| 1087 @Feature({"Android-TabSwitcher"}) |
| 1088 public void testCloseTabDuringFling() throws InterruptedException { |
| 1089 loadUrlInNewTab(TestHttpServerClient.getUrl( |
| 1090 "chrome/test/data/android/tabstest/text_page.html")); |
| 1091 getInstrumentation().runOnMainSync(new Runnable() { |
| 1092 @Override |
| 1093 public void run() { |
| 1094 ContentViewCore view = getActivity().getActivityTab().getContent
ViewCore(); |
| 1095 view.flingForTest(SystemClock.uptimeMillis(), 0, 0, 0, -2000); |
| 1096 } |
| 1097 }); |
| 1098 ChromeTabUtils.closeCurrentTab(getInstrumentation(), getActivity()); |
| 1099 } |
| 1100 |
| 1101 /** |
| 1102 * Flaky on instrumentation-yakju-clankium-ics. See https://crbug.com/431296
. |
| 1103 * @Restriction(RESTRICTION_TYPE_PHONE) |
| 1104 * @MediumTest |
| 1105 * @Feature({"Android-TabSwitcher"}) |
| 1106 */ |
| 1107 @FlakyTest |
| 1108 public void testQuickSwitchBetweenTabAndSwitcherMode() throws InterruptedExc
eption { |
| 1109 final String[] urls = { |
| 1110 TestHttpServerClient.getUrl("chrome/test/data/android/navigate/o
ne.html"), |
| 1111 TestHttpServerClient.getUrl("chrome/test/data/android/navigate/t
wo.html"), |
| 1112 TestHttpServerClient.getUrl("chrome/test/data/android/navigate/t
hree.html")}; |
| 1113 |
| 1114 for (String url : urls) { |
| 1115 loadUrlInNewTab(url); |
| 1116 } |
| 1117 |
| 1118 int lastUrlIndex = urls.length - 1; |
| 1119 |
| 1120 View button = getActivity().findViewById(R.id.tab_switcher_button); |
| 1121 assertNotNull("Could not find 'tab_switcher_button'", button); |
| 1122 |
| 1123 for (int i = 0; i < 15; i++) { |
| 1124 singleClickView(button, button.getWidth() / 2, button.getHeight() /
2); |
| 1125 // Switch back to the tab view from the tab-switcher mode. |
| 1126 singleClickView(button, button.getWidth() / 2, button.getHeight() /
2); |
| 1127 |
| 1128 assertEquals("URL mismatch after switching back to the tab from tab-
switch mode", |
| 1129 urls[lastUrlIndex], getActivity().getActivityTab().getUrl())
; |
| 1130 } |
| 1131 } |
| 1132 |
| 1133 /** |
| 1134 * Open an incognito tab from menu and verify its property. |
| 1135 */ |
| 1136 @MediumTest |
| 1137 @Feature({"Android-TabSwitcher"}) |
| 1138 public void testOpenIncognitoTab() throws InterruptedException { |
| 1139 newIncognitoTabFromMenu(); |
| 1140 |
| 1141 assertTrue("Current Tab should be an incognito tab.", |
| 1142 getActivity().getActivityTab().isIncognito()); |
| 1143 } |
| 1144 |
| 1145 /** |
| 1146 * Test NewTab button on the browser toolbar. |
| 1147 * Restricted to phones due crbug.com/429671. |
| 1148 */ |
| 1149 @MediumTest |
| 1150 @Feature({"Android-TabSwitcher"}) |
| 1151 @Restriction(RESTRICTION_TYPE_PHONE) |
| 1152 public void testNewTabButton() throws InterruptedException { |
| 1153 MenuUtils.invokeCustomMenuActionSync(getInstrumentation(), getActivity()
, |
| 1154 R.id.close_all_tabs_menu_id); |
| 1155 UiUtils.settleDownUI(getInstrumentation()); |
| 1156 |
| 1157 assertTrue("Should be in overview mode", CriteriaHelper.pollForCriteria(
new Criteria() { |
| 1158 @Override |
| 1159 public boolean isSatisfied() { |
| 1160 return getActivity().isInOverviewMode(); |
| 1161 } |
| 1162 })); |
| 1163 |
| 1164 int initialTabCount = getActivity().getCurrentTabModel().getCount(); |
| 1165 assertEquals("Tab count is expected to be 0 after closing all the tabs", |
| 1166 0, initialTabCount); |
| 1167 |
| 1168 ChromeTabUtils.clickNewTabButton(this, this); |
| 1169 |
| 1170 int newTabCount = getActivity().getCurrentTabModel().getCount(); |
| 1171 assertEquals("Tab count is expected to be 1 after clicking Newtab button
", |
| 1172 1, newTabCount); |
| 1173 } |
| 1174 |
| 1175 @MediumTest |
| 1176 @Feature({"Android-TabSwitcher"}) |
| 1177 @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) |
| 1178 public void testToolbarSwipeOnlyTab() throws InterruptedException { |
| 1179 final TabModel tabModel = getActivity().getTabModelSelector().getModel(f
alse); |
| 1180 |
| 1181 assertEquals("Incorrect starting index", 0, tabModel.index()); |
| 1182 runToolbarSideSwipeTestOnCurrentModel(ScrollDirection.RIGHT, 0, false); |
| 1183 runToolbarSideSwipeTestOnCurrentModel(ScrollDirection.LEFT, 0, false); |
| 1184 } |
| 1185 |
| 1186 @MediumTest |
| 1187 @Feature({"Android-TabSwitcher"}) |
| 1188 @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) |
| 1189 public void testToolbarSwipePrevTab() throws InterruptedException { |
| 1190 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 1191 UiUtils.settleDownUI(getInstrumentation()); |
| 1192 |
| 1193 final TabModel tabModel = getActivity().getTabModelSelector().getModel(f
alse); |
| 1194 |
| 1195 assertEquals("Incorrect starting index", 1, tabModel.index()); |
| 1196 runToolbarSideSwipeTestOnCurrentModel(ScrollDirection.RIGHT, 0, true); |
| 1197 } |
| 1198 |
| 1199 @MediumTest |
| 1200 @Feature({"Android-TabSwitcher"}) |
| 1201 @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) |
| 1202 public void testToolbarSwipeNextTab() throws InterruptedException { |
| 1203 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 1204 ChromeTabUtils.switchTabInCurrentTabModel(getActivity(), 0); |
| 1205 UiUtils.settleDownUI(getInstrumentation()); |
| 1206 |
| 1207 final TabModel tabModel = getActivity().getTabModelSelector().getModel(f
alse); |
| 1208 |
| 1209 assertEquals("Incorrect starting index", 0, tabModel.index()); |
| 1210 runToolbarSideSwipeTestOnCurrentModel(ScrollDirection.LEFT, 1, true); |
| 1211 } |
| 1212 |
| 1213 @MediumTest |
| 1214 @Feature({"Android-TabSwitcher"}) |
| 1215 @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) |
| 1216 public void testToolbarSwipePrevTabNone() throws InterruptedException { |
| 1217 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 1218 ChromeTabUtils.switchTabInCurrentTabModel(getActivity(), 0); |
| 1219 UiUtils.settleDownUI(getInstrumentation()); |
| 1220 |
| 1221 final TabModel tabModel = getActivity().getTabModelSelector().getModel(f
alse); |
| 1222 |
| 1223 assertEquals("Incorrect starting index", 0, tabModel.index()); |
| 1224 runToolbarSideSwipeTestOnCurrentModel(ScrollDirection.RIGHT, 0, false); |
| 1225 } |
| 1226 |
| 1227 @MediumTest |
| 1228 @Feature({"Android-TabSwitcher"}) |
| 1229 @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) |
| 1230 public void testToolbarSwipeNextTabNone() throws InterruptedException { |
| 1231 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 1232 UiUtils.settleDownUI(getInstrumentation()); |
| 1233 |
| 1234 final TabModel tabModel = getActivity().getTabModelSelector().getModel(f
alse); |
| 1235 |
| 1236 assertEquals("Incorrect starting index", 1, tabModel.index()); |
| 1237 runToolbarSideSwipeTestOnCurrentModel(ScrollDirection.LEFT, 1, false); |
| 1238 } |
| 1239 |
| 1240 /** |
| 1241 * Bug: crbug.com/392656 |
| 1242 * @MediumTest |
| 1243 * @Feature({"Android-TabSwitcher"}) |
| 1244 * @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) |
| 1245 */ |
| 1246 @DisabledTest |
| 1247 public void testToolbarSwipeNextThenPrevTab() throws InterruptedException { |
| 1248 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 1249 ChromeTabUtils.switchTabInCurrentTabModel(getActivity(), 0); |
| 1250 UiUtils.settleDownUI(getInstrumentation()); |
| 1251 |
| 1252 final TabModel tabModel = getActivity().getTabModelSelector().getModel(f
alse); |
| 1253 |
| 1254 assertEquals("Incorrect starting index", 0, tabModel.index()); |
| 1255 runToolbarSideSwipeTestOnCurrentModel(ScrollDirection.LEFT, 1, true); |
| 1256 |
| 1257 assertEquals("Incorrect starting index", 1, tabModel.index()); |
| 1258 runToolbarSideSwipeTestOnCurrentModel(ScrollDirection.RIGHT, 0, true); |
| 1259 } |
| 1260 |
| 1261 /** |
| 1262 * Bug: crbug.com/392656 |
| 1263 * @MediumTest |
| 1264 * @Feature({"Android-TabSwitcher"}) |
| 1265 * @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) |
| 1266 */ |
| 1267 @DisabledTest |
| 1268 public void testToolbarSwipeNextThenPrevTabIncognito() throws InterruptedExc
eption { |
| 1269 newIncognitoTabFromMenu(); |
| 1270 newIncognitoTabFromMenu(); |
| 1271 ChromeTabUtils.switchTabInCurrentTabModel(getActivity(), 0); |
| 1272 UiUtils.settleDownUI(getInstrumentation()); |
| 1273 |
| 1274 final TabModel tabModel = getActivity().getTabModelSelector().getModel(t
rue); |
| 1275 |
| 1276 assertEquals("Incorrect starting index", 0, tabModel.index()); |
| 1277 runToolbarSideSwipeTestOnCurrentModel(ScrollDirection.LEFT, 1, true); |
| 1278 |
| 1279 assertEquals("Incorrect starting index", 1, tabModel.index()); |
| 1280 runToolbarSideSwipeTestOnCurrentModel(ScrollDirection.RIGHT, 0, true); |
| 1281 } |
| 1282 |
| 1283 private void runToolbarSideSwipeTestOnCurrentModel(ScrollDirection direction
, int finalIndex, |
| 1284 boolean expectsSelection) throws InterruptedException { |
| 1285 final CallbackHelper selectCallback = new CallbackHelper(); |
| 1286 final int id = getActivity().getCurrentTabModel().getTabAt(finalIndex).g
etId(); |
| 1287 final TabModelObserver observer = new EmptyTabModelObserver() { |
| 1288 @Override |
| 1289 public void didSelectTab(Tab tab, TabSelectionType type, int lastId)
{ |
| 1290 if (tab.getId() == id) selectCallback.notifyCalled(); |
| 1291 } |
| 1292 }; |
| 1293 |
| 1294 if (expectsSelection) { |
| 1295 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 1296 @Override |
| 1297 public void run() { |
| 1298 TabModelSelector selector = getActivity().getTabModelSelecto
r(); |
| 1299 for (TabModel tabModel : selector.getModels()) { |
| 1300 tabModel.addObserver(observer); |
| 1301 } |
| 1302 } |
| 1303 }); |
| 1304 } |
| 1305 |
| 1306 performToolbarSideSwipe(direction); |
| 1307 waitForStaticLayout(); |
| 1308 assertEquals("Index after toolbar side swipe is incorrect", finalIndex, |
| 1309 getActivity().getCurrentTabModel().index()); |
| 1310 |
| 1311 if (expectsSelection) { |
| 1312 try { |
| 1313 selectCallback.waitForCallback(0); |
| 1314 } catch (TimeoutException e) { |
| 1315 Assert.fail("Tab selected event was never received"); |
| 1316 } |
| 1317 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 1318 @Override |
| 1319 public void run() { |
| 1320 TabModelSelector selector = getActivity().getTabModelSelecto
r(); |
| 1321 for (TabModel tabModel : selector.getModels()) { |
| 1322 tabModel.removeObserver(observer); |
| 1323 } |
| 1324 } |
| 1325 }); |
| 1326 } |
| 1327 } |
| 1328 |
| 1329 |
| 1330 private void performToolbarSideSwipe(ScrollDirection direction) { |
| 1331 assertTrue("Unexpected direction for side swipe " + direction, |
| 1332 direction == ScrollDirection.LEFT || direction == ScrollDirectio
n.RIGHT); |
| 1333 final View toolbar = getActivity().findViewById(R.id.toolbar); |
| 1334 int[] toolbarPos = new int[2]; |
| 1335 toolbar.getLocationOnScreen(toolbarPos); |
| 1336 final int width = toolbar.getWidth(); |
| 1337 final int height = toolbar.getHeight(); |
| 1338 |
| 1339 final int fromX = toolbarPos[0] + width / 2; |
| 1340 final int toX = toolbarPos[0] + (direction == ScrollDirection.LEFT ? 0 :
width); |
| 1341 final int y = toolbarPos[1] + height / 2; |
| 1342 final int stepCount = 10; |
| 1343 |
| 1344 long downTime = SystemClock.uptimeMillis(); |
| 1345 dragStart(fromX, y, downTime); |
| 1346 dragTo(fromX, toX, y, y, stepCount, downTime); |
| 1347 dragEnd(toX, y, downTime); |
| 1348 } |
| 1349 |
| 1350 private void waitForStaticLayout() throws InterruptedException { |
| 1351 final Callable<Boolean> callable = new Callable<Boolean>() { |
| 1352 @Override |
| 1353 public Boolean call() throws Exception { |
| 1354 CompositorViewHolder compositorViewHolder = (CompositorViewHolde
r) |
| 1355 getActivity().findViewById(R.id.compositor_view_holder); |
| 1356 LayoutManager layoutManager = compositorViewHolder.getLayoutMana
ger(); |
| 1357 |
| 1358 return layoutManager.getActiveLayout() instanceof StaticLayout; |
| 1359 } |
| 1360 }; |
| 1361 |
| 1362 assertTrue("Static Layout never selected after side swipe", |
| 1363 CriteriaHelper.pollForCriteria(new Criteria() { |
| 1364 @Override |
| 1365 public boolean isSatisfied() { |
| 1366 return ThreadUtils.runOnUiThreadBlockingNoException(call
able); |
| 1367 } |
| 1368 })); |
| 1369 } |
| 1370 |
| 1371 /** |
| 1372 * Test that swipes and tab transitions are not causing URL bar to be focuse
d. |
| 1373 */ |
| 1374 @MediumTest |
| 1375 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 1376 @Feature({"Android-TabSwitcher"}) |
| 1377 public void testOSKIsNotShownDuringSwipe() throws InterruptedException { |
| 1378 final View urlBar = getActivity().findViewById(R.id.url_bar); |
| 1379 final LayoutManagerChrome layoutManager = updateTabsViewSize(); |
| 1380 final EdgeSwipeHandler edgeSwipeHandler = layoutManager.getTopSwipeHandl
er(); |
| 1381 |
| 1382 UiUtils.settleDownUI(getInstrumentation()); |
| 1383 getInstrumentation().runOnMainSync( |
| 1384 new Runnable() { |
| 1385 @Override |
| 1386 public void run() { |
| 1387 urlBar.requestFocus(); |
| 1388 } |
| 1389 }); |
| 1390 UiUtils.settleDownUI(getInstrumentation()); |
| 1391 |
| 1392 getInstrumentation().runOnMainSync( |
| 1393 new Runnable() { |
| 1394 @Override |
| 1395 public void run() { |
| 1396 urlBar.clearFocus(); |
| 1397 } |
| 1398 }); |
| 1399 UiUtils.settleDownUI(getInstrumentation()); |
| 1400 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 1401 UiUtils.settleDownUI(getInstrumentation()); |
| 1402 |
| 1403 assertFalse("Keyboard somehow got shown", |
| 1404 org.chromium.ui.UiUtils.isKeyboardShowing(getActivity(), urlBar)
); |
| 1405 |
| 1406 ThreadUtils.runOnUiThread(new Runnable() { |
| 1407 @Override |
| 1408 public void run() { |
| 1409 edgeSwipeHandler.swipeStarted(ScrollDirection.RIGHT, 0, 0); |
| 1410 float swipeXChange = mTabsViewWidthDp / 2.f; |
| 1411 edgeSwipeHandler.swipeUpdated( |
| 1412 swipeXChange, 0.f, swipeXChange, 0.f, swipeXChange, 0.f)
; |
| 1413 } |
| 1414 }); |
| 1415 |
| 1416 assertTrue("Layout still requesting Tab Android view be attached", |
| 1417 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 1418 @Override |
| 1419 public boolean isSatisfied() { |
| 1420 LayoutManager driver = getActivity().getLayoutManager(); |
| 1421 return !driver.getActiveLayout().shouldDisplayContentOve
rlay(); |
| 1422 } |
| 1423 })); |
| 1424 |
| 1425 ThreadUtils.runOnUiThread(new Runnable() { |
| 1426 @Override |
| 1427 public void run() { |
| 1428 assertFalse("Keyboard should be hidden while swiping", |
| 1429 org.chromium.ui.UiUtils.isKeyboardShowing(getActivity(),
urlBar)); |
| 1430 edgeSwipeHandler.swipeFinished(); |
| 1431 } |
| 1432 }); |
| 1433 |
| 1434 assertTrue("Layout not requesting Tab Android view be attached", |
| 1435 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 1436 @Override |
| 1437 public boolean isSatisfied() { |
| 1438 LayoutManager driver = getActivity().getLayoutManager(); |
| 1439 return driver.getActiveLayout().shouldDisplayContentOver
lay(); |
| 1440 } |
| 1441 })); |
| 1442 |
| 1443 assertFalse("Keyboard should not be shown", |
| 1444 org.chromium.ui.UiUtils.isKeyboardShowing(getActivity(), urlBar)
); |
| 1445 } |
| 1446 |
| 1447 /** |
| 1448 * Test that orientation changes cause the live tab reflow. |
| 1449 */ |
| 1450 @MediumTest |
| 1451 @Feature({"Android-TabSwitcher"}) |
| 1452 @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) |
| 1453 public void testOrientationChangeCausesLiveTabReflowInNormalView() |
| 1454 throws InterruptedException, TimeoutException { |
| 1455 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PO
RTRAIT); |
| 1456 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 1457 loadUrl(RESIZE_TEST_URL); |
| 1458 final WebContents webContents = |
| 1459 getActivity().getActivityTab().getWebContents(); |
| 1460 |
| 1461 JavaScriptUtils.executeJavaScriptAndWaitForResult(webContents, |
| 1462 "resizeHappened = fals
e;"); |
| 1463 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LA
NDSCAPE); |
| 1464 UiUtils.settleDownUI(getInstrumentation()); |
| 1465 assertEquals("onresize event wasn't received by the tab (normal view)", |
| 1466 "true", |
| 1467 JavaScriptUtils.executeJavaScriptAndWaitForResult( |
| 1468 webContents, "resizeHappened", |
| 1469 WAIT_RESIZE_TIMEOUT_MS, TimeUnit.MILLISECONDS)); |
| 1470 } |
| 1471 |
| 1472 /** |
| 1473 * Test that orientation changes cause the live tab reflow. |
| 1474 */ |
| 1475 @MediumTest |
| 1476 @Feature({"Android-TabSwitcher"}) |
| 1477 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| 1478 public void testOrientationChangeCausesLiveTabReflowInTabSwitcher() |
| 1479 throws InterruptedException, TimeoutException { |
| 1480 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PO
RTRAIT); |
| 1481 ChromeTabUtils.newTabFromMenu(getInstrumentation(), getActivity()); |
| 1482 loadUrl(RESIZE_TEST_URL); |
| 1483 final WebContents webContents = |
| 1484 getActivity().getActivityTab().getWebContents(); |
| 1485 |
| 1486 showOverviewAndWaitForAnimation(); |
| 1487 JavaScriptUtils.executeJavaScriptAndWaitForResult(webContents, |
| 1488 "resizeHappened = fals
e;"); |
| 1489 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LA
NDSCAPE); |
| 1490 UiUtils.settleDownUI(getInstrumentation()); |
| 1491 assertEquals("onresize event wasn't received by the live tab (tabswitche
r, to Landscape)", |
| 1492 "true", |
| 1493 JavaScriptUtils.executeJavaScriptAndWaitForResult( |
| 1494 webContents, "resizeHappened", |
| 1495 WAIT_RESIZE_TIMEOUT_MS, TimeUnit.MILLISECONDS)); |
| 1496 |
| 1497 JavaScriptUtils.executeJavaScriptAndWaitForResult(webContents, |
| 1498 "resizeHappened = fals
e;"); |
| 1499 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PO
RTRAIT); |
| 1500 UiUtils.settleDownUI(getInstrumentation()); |
| 1501 assertEquals("onresize event wasn't received by the live tab (tabswitche
r, to Portrait)", |
| 1502 "true", |
| 1503 JavaScriptUtils.executeJavaScriptAndWaitForResult(webContents, |
| 1504 "resizeHappened", WAIT_RESIZE_TIMEOUT_MS, TimeUnit.MILLI
SECONDS)); |
| 1505 } |
| 1506 |
| 1507 @MediumTest |
| 1508 @Feature({"Android-TabSwitcher"}) |
| 1509 public void testLastClosedUndoableTabGetsHidden() { |
| 1510 final TabModel model = getActivity().getTabModelSelector().getCurrentMod
el(); |
| 1511 final Tab tab = TabModelUtils.getCurrentTab(model); |
| 1512 |
| 1513 assertEquals("Too many tabs at startup", 1, model.getCount()); |
| 1514 |
| 1515 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 1516 @Override |
| 1517 public void run() { |
| 1518 model.closeTab(tab, false, false, true); |
| 1519 } |
| 1520 }); |
| 1521 |
| 1522 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 1523 @Override |
| 1524 public void run() { |
| 1525 assertTrue("Tab close is not undoable", model.isClosurePending(t
ab.getId())); |
| 1526 assertTrue("Tab was not hidden", tab.isHidden()); |
| 1527 } |
| 1528 }); |
| 1529 } |
| 1530 |
| 1531 @MediumTest |
| 1532 @Feature({"Android-TabSwitcher"}) |
| 1533 public void testLastClosedTabTriggersNotifyChangedCall() { |
| 1534 final TabModel model = getActivity().getTabModelSelector().getCurrentMod
el(); |
| 1535 final Tab tab = TabModelUtils.getCurrentTab(model); |
| 1536 final TabModelSelector selector = getActivity().getTabModelSelector(); |
| 1537 mNotifyChangedCalled = false; |
| 1538 |
| 1539 selector.addObserver(new EmptyTabModelSelectorObserver() { |
| 1540 @Override |
| 1541 public void onChange() { |
| 1542 mNotifyChangedCalled = true; |
| 1543 } |
| 1544 }); |
| 1545 |
| 1546 assertEquals("Too many tabs at startup", 1, model.getCount()); |
| 1547 |
| 1548 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 1549 @Override |
| 1550 public void run() { |
| 1551 model.closeTab(tab, false, false, true); |
| 1552 } |
| 1553 }); |
| 1554 |
| 1555 assertTrue("notifyChanged() was not called", mNotifyChangedCalled); |
| 1556 } |
| 1557 |
| 1558 @Smoke |
| 1559 @Feature({"Android-TabSwitcher"}) |
| 1560 public void testTabsAreDestroyedOnModelDestruction() throws InterruptedExcep
tion { |
| 1561 startMainActivityOnBlankPage(); |
| 1562 final TabModelSelectorImpl selector = |
| 1563 (TabModelSelectorImpl) getActivity().getTabModelSelector(); |
| 1564 final Tab tab = getActivity().getActivityTab(); |
| 1565 |
| 1566 final AtomicBoolean webContentsDestroyCalled = new AtomicBoolean(); |
| 1567 |
| 1568 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 1569 @Override |
| 1570 @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") |
| 1571 public void run() { |
| 1572 @SuppressWarnings("unused") // Avoid GC of observer |
| 1573 WebContentsObserver observer = new WebContentsObserver(tab.getWe
bContents()) { |
| 1574 @Override |
| 1575 public void destroy() { |
| 1576 super.destroy(); |
| 1577 webContentsDestroyCalled.set(true); |
| 1578 } |
| 1579 }; |
| 1580 |
| 1581 assertNotNull("No initial tab at startup", tab); |
| 1582 assertNotNull("Tab does not have a web contents", tab.getWebCont
ents()); |
| 1583 assertTrue("Tab is destroyed", tab.isInitialized()); |
| 1584 |
| 1585 selector.destroy(); |
| 1586 |
| 1587 assertNull("Tab still has a web contents", tab.getWebContents())
; |
| 1588 assertFalse("Tab was not destroyed", tab.isInitialized()); |
| 1589 } |
| 1590 }); |
| 1591 |
| 1592 assertTrue("WebContentsObserver was never destroyed", webContentsDestroy
Called.get()); |
| 1593 } |
| 1594 |
| 1595 @Override |
| 1596 public void startMainActivity() throws InterruptedException { |
| 1597 float dpToPx = getInstrumentation().getContext().getResources().getDispl
ayMetrics().density; |
| 1598 mPxToDp = 1.0f / dpToPx; |
| 1599 |
| 1600 // Exclude the tests that can launch directly to a page other than the N
TP. |
| 1601 if (getName().equals("testOpenAndCloseNewTabButton") |
| 1602 || getName().equals("testSwitchToTabThatDoesNotHaveThumbnail") |
| 1603 || getName().equals("testCloseTabPortrait") |
| 1604 || getName().equals("testCloseTabLandscape") |
| 1605 || getName().equals("testTabsAreDestroyedOnModelDestruction")) { |
| 1606 return; |
| 1607 } |
| 1608 |
| 1609 if (getName().equals("testSpawnPopupOnBackgroundTab")) { |
| 1610 CommandLine.getInstance().appendSwitch(ContentSwitches.DISABLE_POPUP
_BLOCKING); |
| 1611 } |
| 1612 startMainActivityFromLauncher(); |
| 1613 } |
| 1614 } |
OLD | NEW |