OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser.tabmodel; | 5 package org.chromium.chrome.browser.tabmodel; |
6 | 6 |
7 import android.annotation.TargetApi; | |
8 import android.os.Build; | |
7 import android.test.suitebuilder.annotation.MediumTest; | 9 import android.test.suitebuilder.annotation.MediumTest; |
8 | 10 |
11 import org.chromium.base.ApplicationStatus; | |
9 import org.chromium.base.ThreadUtils; | 12 import org.chromium.base.ThreadUtils; |
10 import org.chromium.base.test.util.FlakyTest; | 13 import org.chromium.base.test.util.FlakyTest; |
14 import org.chromium.base.test.util.MinAndroidSdkLevel; | |
11 import org.chromium.base.test.util.Restriction; | 15 import org.chromium.base.test.util.Restriction; |
16 import org.chromium.base.test.util.UrlUtils; | |
17 import org.chromium.chrome.browser.ChromeTabbedActivity; | |
18 import org.chromium.chrome.browser.ChromeTabbedActivity2; | |
19 import org.chromium.chrome.browser.multiwindow.MultiWindowUtilsTest; | |
20 import org.chromium.chrome.browser.tab.EmptyTabObserver; | |
12 import org.chromium.chrome.browser.tab.Tab; | 21 import org.chromium.chrome.browser.tab.Tab; |
13 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; | 22 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; |
14 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; | 23 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; |
15 import org.chromium.chrome.test.ChromeTabbedActivityTestBase; | 24 import org.chromium.chrome.test.ChromeTabbedActivityTestBase; |
16 import org.chromium.chrome.test.util.ChromeRestriction; | 25 import org.chromium.chrome.test.util.ChromeRestriction; |
17 import org.chromium.content.browser.test.util.CallbackHelper; | 26 import org.chromium.content.browser.test.util.CallbackHelper; |
27 import org.chromium.content.browser.test.util.Criteria; | |
28 import org.chromium.content.browser.test.util.CriteriaHelper; | |
18 import org.chromium.content_public.browser.LoadUrlParams; | 29 import org.chromium.content_public.browser.LoadUrlParams; |
19 | 30 |
31 import java.util.concurrent.Callable; | |
20 import java.util.concurrent.TimeoutException; | 32 import java.util.concurrent.TimeoutException; |
21 | 33 |
22 /** | 34 /** |
23 * Tests undo and restoring of tabs in a {@link TabModel}. | 35 * Tests undo and restoring of tabs in a {@link TabModel}. |
24 */ | 36 */ |
25 public class UndoTabModelTest extends ChromeTabbedActivityTestBase { | 37 public class UndoTabModelTest extends ChromeTabbedActivityTestBase { |
26 private static final Tab[] EMPTY = new Tab[] { }; | 38 private static final Tab[] EMPTY = new Tab[] { }; |
39 private static final String TEST_URL_0 = UrlUtils.encodeHtmlDataUri("<html>t est_url_0.</html>"); | |
40 private static final String TEST_URL_1 = UrlUtils.encodeHtmlDataUri("<html>t est_url_1</html>"); | |
27 | 41 |
28 @Override | 42 @Override |
29 public void startMainActivity() throws InterruptedException { | 43 public void startMainActivity() throws InterruptedException { |
30 startMainActivityOnBlankPage(); | 44 startMainActivityOnBlankPage(); |
31 } | 45 } |
32 | 46 |
33 private void checkState( | 47 private void checkState( |
34 final TabModel model, final Tab[] tabsList, final Tab selectedTab, | 48 final TabModel model, final Tab[] tabsList, final Tab selectedTab, |
35 final Tab[] closingTabs, final Tab[] fullTabsList, | 49 final Tab[] closingTabs, final Tab[] fullTabsList, |
36 final Tab fullSelectedTab) { | 50 final Tab fullSelectedTab) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
68 private void createTabOnUiThread(final ChromeTabCreator tabCreator) { | 82 private void createTabOnUiThread(final ChromeTabCreator tabCreator) { |
69 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 83 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
70 @Override | 84 @Override |
71 public void run() { | 85 public void run() { |
72 tabCreator.createNewTab(new LoadUrlParams("about:blank"), | 86 tabCreator.createNewTab(new LoadUrlParams("about:blank"), |
73 TabLaunchType.FROM_CHROME_UI, null); | 87 TabLaunchType.FROM_CHROME_UI, null); |
74 } | 88 } |
75 }); | 89 }); |
76 } | 90 } |
77 | 91 |
92 private void createFullyLoadedTabOnUiThread(final ChromeTabbedActivity activ ity, | |
93 final String url) { | |
94 final CallbackHelper tabCallbackHelper = new CallbackHelper(); | |
95 final TabLoadedObserver observer = new TabLoadedObserver(tabCallbackHelp er); | |
96 | |
97 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | |
98 @Override | |
99 public void run() { | |
100 activity.getTabCreator(false).createNewTab(new LoadUrlParams(url ), | |
101 TabLaunchType.FROM_CHROME_UI, null).addObserver(observer ); | |
102 } | |
103 }); | |
104 | |
105 // Must wait for the page to be fully loaded. | |
106 try { | |
107 tabCallbackHelper.waitForCallback(0); | |
108 } catch (TimeoutException | InterruptedException e) { | |
109 fail("Failed to load the tab."); | |
110 } | |
111 } | |
112 | |
78 private void selectTabOnUiThread(final TabModel model, final Tab tab) { | 113 private void selectTabOnUiThread(final TabModel model, final Tab tab) { |
79 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 114 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
80 @Override | 115 @Override |
81 public void run() { | 116 public void run() { |
82 model.setIndex(model.indexOf(tab), TabSelectionType.FROM_USER); | 117 model.setIndex(model.indexOf(tab), TabSelectionType.FROM_USER); |
83 } | 118 } |
84 }); | 119 }); |
85 } | 120 } |
86 | 121 |
87 private void closeTabOnUiThread( | 122 private void closeTabOnUiThread( |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
314 }); | 349 }); |
315 | 350 |
316 for (int i = 0; i < selector.getModels().size(); i++) { | 351 for (int i = 0; i < selector.getModels().size(); i++) { |
317 TabList tabs = selector.getModelAt(i).getComprehensiveModel(); | 352 TabList tabs = selector.getModelAt(i).getComprehensiveModel(); |
318 for (int j = 0; j < tabs.getCount(); j++) { | 353 for (int j = 0; j < tabs.getCount(); j++) { |
319 assertFalse(tabs.isClosurePending(tabs.getTabAt(j).getId())); | 354 assertFalse(tabs.isClosurePending(tabs.getTabAt(j).getId())); |
320 } | 355 } |
321 } | 356 } |
322 } | 357 } |
323 | 358 |
359 private void openMostRecentlyClosedTabOnUiThread(final TabModelSelector sele ctor) { | |
360 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | |
361 @Override | |
362 public void run() { | |
363 selector.getCurrentModel().openMostRecentlyClosedTab(); | |
364 } | |
365 }); | |
366 } | |
367 | |
368 // Helper class that notifies when a page load is finished. | |
369 private static class TabLoadedObserver extends EmptyTabObserver { | |
370 private CallbackHelper mLoadedCallback; | |
371 | |
372 public TabLoadedObserver(CallbackHelper loadCallback) { | |
373 super(); | |
374 mLoadedCallback = loadCallback; | |
375 } | |
376 @Override | |
377 public void onPageLoadFinished(Tab tab) { | |
378 mLoadedCallback.notifyCalled(); | |
379 } | |
380 } | |
381 | |
382 // Helper class that notifies after the tab is closed, and a tab restore ser vice entry has been | |
383 // created in tab restore service. | |
384 private static class TabClosedObserver extends EmptyTabModelObserver { | |
385 private CallbackHelper mTabClosedCallback; | |
386 | |
387 public TabClosedObserver(CallbackHelper closedCallback) { | |
388 mTabClosedCallback = closedCallback; | |
389 } | |
390 | |
391 @Override | |
392 public void didCloseTab(int tabId, boolean incognito) { | |
393 mTabClosedCallback.notifyCalled(); | |
394 } | |
395 } | |
396 | |
324 /** | 397 /** |
325 * Test undo with a single tab with the following actions/expected states: | 398 * Test undo with a single tab with the following actions/expected states: |
326 * Action Model List Close List Compr ehensive List | 399 * Action Model List Close List Compr ehensive List |
327 * 1. Initial State [ 0s ] - [ 0s ] | 400 * 1. Initial State [ 0s ] - [ 0s ] |
328 * 2. CloseTab(0, allow undo) - [ 0 ] [ 0s ] | 401 * 2. CloseTab(0, allow undo) - [ 0 ] [ 0s ] |
329 * 3. CancelClose(0) [ 0s ] - [ 0s ] | 402 * 3. CancelClose(0) [ 0s ] - [ 0s ] |
330 * 4. CloseTab(0, allow undo) - [ 0 ] [ 0s ] | 403 * 4. CloseTab(0, allow undo) - [ 0 ] [ 0s ] |
331 * 5. CommitClose(0) - - - | 404 * 5. CommitClose(0) - - - |
332 * 6. CreateTab(0) [ 0s ] - [ 0s ] | 405 * 6. CreateTab(0) [ 0s ] - [ 0s ] |
333 * 7. CloseTab(0, allow undo) - [ 0 ] [ 0s ] | 406 * 7. CloseTab(0, allow undo) - [ 0 ] [ 0s ] |
(...skipping 1069 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1403 closeTabOnUiThread(model, tab0, true); | 1476 closeTabOnUiThread(model, tab0, true); |
1404 checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1); | 1477 checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1); |
1405 | 1478 |
1406 // 3. | 1479 // 3. |
1407 saveStateOnUiThread(selector); | 1480 saveStateOnUiThread(selector); |
1408 fullList = new Tab[] { tab1 }; | 1481 fullList = new Tab[] { tab1 }; |
1409 checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1); | 1482 checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1); |
1410 assertTrue(tab0.isClosing()); | 1483 assertTrue(tab0.isClosing()); |
1411 assertFalse(tab0.isInitialized()); | 1484 assertFalse(tab0.isInitialized()); |
1412 } | 1485 } |
1486 | |
1487 /** | |
1488 * Test opening recently closed tabs using the rewound list in Java. | |
1489 * @throws InterruptedException | |
1490 */ | |
1491 @MediumTest | |
1492 public void testOpenRecentlyClosedTab() throws InterruptedException { | |
1493 TabModelSelector selector = getActivity().getTabModelSelector(); | |
1494 TabModel model = selector.getModel(false); | |
1495 ChromeTabCreator tabCreator = getActivity().getTabCreator(false); | |
1496 | |
1497 createTabOnUiThread(tabCreator); | |
1498 | |
1499 Tab tab0 = model.getTabAt(0); | |
1500 Tab tab1 = model.getTabAt(1); | |
1501 Tab[] allTabs = new Tab[]{tab0, tab1}; | |
1502 | |
1503 closeTabOnUiThread(model, tab1, true); | |
1504 checkState(model, new Tab[]{tab0}, tab0, new Tab[]{tab1}, allTabs, tab0) ; | |
1505 | |
1506 // Ensure tab recovery, ordering, and reuse of {@link Tab} objects in Ja va. | |
1507 openMostRecentlyClosedTabOnUiThread(selector); | |
1508 checkState(model, allTabs, tab0, EMPTY, allTabs, tab0); | |
1509 } | |
1510 | |
1511 /** | |
1512 * Test opening recently closed tab using native tab restore service. | |
1513 * @throws InterruptedException | |
1514 */ | |
1515 @MediumTest | |
1516 public void testOpenRecentlyClosedTabNative() throws InterruptedException { | |
1517 final TabModelSelector selector = getActivity().getTabModelSelector(); | |
1518 final TabModel model = selector.getModel(false); | |
1519 | |
1520 // Create new tab and attach observer to listen to loaded event. | |
1521 // Native can only successfully recover the tab after a page load has fi nished and | |
1522 // it has navigation history. | |
1523 createFullyLoadedTabOnUiThread(getActivity(), TEST_URL_0); | |
1524 | |
1525 // Close the tab, and commit pending closure. | |
1526 assertEquals(model.getCount(), 2); | |
1527 closeTabOnUiThread(model, model.getTabAt(1), false); | |
1528 assertEquals(1, model.getCount()); | |
1529 Tab[] tabs = new Tab[]{model.getTabAt(0)}; | |
1530 checkState(model, tabs, tabs[0], EMPTY, tabs, tabs[0]); | |
1531 | |
1532 // Recover the page. | |
1533 openMostRecentlyClosedTabOnUiThread(selector); | |
1534 | |
1535 assertEquals(2, model.getCount()); | |
1536 tabs = new Tab[]{model.getTabAt(0), model.getTabAt(1)}; | |
1537 assertEquals(TEST_URL_0, tabs[1].getUrl()); | |
1538 checkState(model, tabs, tabs[0], EMPTY, tabs, tabs[0]); | |
1539 } | |
1540 | |
1541 /** | |
1542 * Test opening recently closed tab when we have multiple windows. | |
1543 * | Action | Result | |
1544 * 1. Create second window. | | |
1545 * 2. Open tab in window 1. | | |
1546 * 3. Open tab in window 2. | | |
1547 * 4. Close tab in window 1. | | |
1548 * 5. Close tab in window 2. | | |
1549 * 6. Restore tab. | Tab restored in window 2. | |
1550 * 7. Restore tab. | Tab restored in window 1. | |
1551 * @throws InterruptedException | |
1552 */ | |
1553 @MediumTest | |
1554 @MinAndroidSdkLevel(24) | |
1555 @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
1556 public void testOpenRecentlyClosedTabMultiWindow() throws InterruptedExcepti on { | |
1557 final ChromeTabbedActivity2 secondActivity = | |
1558 MultiWindowUtilsTest.createSecondChromeTabbedActivity(getActivit y()); | |
1559 | |
1560 // Wait for the second window to be fully initialized. | |
1561 CriteriaHelper.pollUiThread(new Criteria() { | |
1562 @Override | |
1563 public boolean isSatisfied() { | |
1564 return secondActivity.getTabModelSelector().isTabStateInitialize d(); | |
1565 } | |
1566 }); | |
1567 // First window context. | |
1568 final TabModelSelector firstSelector = getActivity().getTabModelSelector (); | |
1569 final TabModel firstModel = firstSelector.getModel(false); | |
1570 | |
1571 // Second window context. | |
1572 final TabModel secondModel = secondActivity.getTabModelSelector().getMod el(false); | |
1573 | |
1574 // Create tabs. | |
1575 createFullyLoadedTabOnUiThread(getActivity(), TEST_URL_0); | |
1576 createFullyLoadedTabOnUiThread(secondActivity, TEST_URL_1); | |
1577 | |
1578 assertEquals("Unexpected number of tabs in first window.", 2, firstModel .getCount()); | |
1579 assertEquals("Unexpected number of tabs in second window.", 2, secondMod el.getCount()); | |
1580 | |
1581 // Close one tab in the first window. | |
1582 closeTabOnUiThread(firstModel, firstModel.getTabAt(1), false); | |
1583 assertEquals("Unexpected number of tabs in first window.", 1, firstModel .getCount()); | |
1584 assertEquals("Unexpected number of tabs in second window.", 2, secondMod el.getCount()); | |
1585 | |
1586 // Close one tab in the second window. | |
1587 closeTabOnUiThread(secondModel, secondModel.getTabAt(1), false); | |
1588 assertEquals("Unexpected number of tabs in first window.", 1, firstModel .getCount()); | |
1589 assertEquals("Unexpected number of tabs in second window.", 1, secondMod el.getCount()); | |
1590 | |
1591 // Restore one tab. | |
1592 openMostRecentlyClosedTabOnUiThread(firstSelector); | |
1593 assertEquals("Unexpected number of tabs in first window.", 1, firstModel .getCount()); | |
1594 assertEquals("Unexpected number of tabs in second window.", 2, secondMod el.getCount()); | |
1595 | |
1596 // Restore one more tab. | |
1597 openMostRecentlyClosedTabOnUiThread(firstSelector); | |
1598 | |
1599 // Check final states of both windows. | |
1600 Tab firstModelTab = firstModel.getTabAt(0); | |
Theresa
2016/07/12 17:25:17
nit: move this above line 1582, before any tabs ar
| |
1601 Tab secondModelTab = secondModel.getTabAt(0); | |
1602 Tab[] firstWindowTabs = new Tab[]{firstModelTab, firstModel.getTabAt(1)} ; | |
1603 Tab[] secondWindowTabs = new Tab[]{secondModelTab, secondModel.getTabAt( 1)}; | |
1604 checkState(firstModel, firstWindowTabs, firstModelTab, EMPTY, firstWindo wTabs, | |
1605 firstModelTab); | |
1606 checkState(secondModel, secondWindowTabs, secondModelTab, EMPTY, secondW indowTabs, | |
1607 secondModelTab); | |
1608 assertEquals(TEST_URL_0, firstWindowTabs[1].getUrl()); | |
1609 assertEquals(TEST_URL_1, secondWindowTabs[1].getUrl()); | |
1610 | |
1611 secondActivity.finishAndRemoveTask(); | |
1612 } | |
1613 | |
1614 /** | |
1615 * Test restoring closed tab from a closed window. | |
1616 * | Action | Result | |
1617 * 1. Create second window. | | |
1618 * 2. Open tab in window 2. | | |
1619 * 3. Close tab in window 2. | | |
1620 * 4. Close second window. | | |
1621 * 5. Restore tab. | Tab restored in first window. | |
1622 * @throws InterruptedException | |
1623 */ | |
1624 @MediumTest | |
1625 @MinAndroidSdkLevel(24) | |
1626 @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
1627 public void testOpenRecentlyClosedTabMultiWindowFallback() throws Interrupte dException { | |
1628 final ChromeTabbedActivity2 secondActivity = | |
1629 MultiWindowUtilsTest.createSecondChromeTabbedActivity(getActivit y()); | |
1630 // Wait for the second window to be fully initialized. | |
1631 CriteriaHelper.pollUiThread(new Criteria() { | |
1632 @Override | |
1633 public boolean isSatisfied() { | |
1634 return secondActivity.getTabModelSelector().isTabStateInitialize d(); | |
1635 } | |
1636 }); | |
1637 | |
1638 // First window context. | |
1639 final TabModelSelector firstSelector = getActivity().getTabModelSelector (); | |
1640 final TabModel firstModel = firstSelector.getModel(false); | |
1641 | |
1642 // Second window context. | |
1643 final TabModel secondModel = secondActivity.getTabModelSelector().getMod el(false); | |
1644 | |
1645 // Create tab on second window. | |
1646 createFullyLoadedTabOnUiThread(secondActivity, TEST_URL_1); | |
1647 assertEquals("Window 2 should have 2 tab.", 2, secondModel.getCount()); | |
1648 | |
1649 // Close tab in second window, wait until tab restore service history is created. | |
1650 CallbackHelper closedCallback = new CallbackHelper(); | |
1651 secondModel.addObserver(new TabClosedObserver(closedCallback)); | |
1652 closeTabOnUiThread(secondModel, secondModel.getTabAt(1), false); | |
1653 | |
1654 try { | |
1655 closedCallback.waitForCallback(0); | |
1656 } catch (TimeoutException | InterruptedException e) { | |
1657 fail("Failed to close the tab on the second window."); | |
1658 } | |
1659 | |
1660 assertEquals("Window 2 should have 1 tab.", 1, secondModel.getCount()); | |
1661 | |
1662 // Closed the second window. Must wait until it's totally closed. | |
1663 int numExpectedActivities = ApplicationStatus.getRunningActivities().siz e() - 1; | |
1664 secondActivity.finishAndRemoveTask(); | |
1665 CriteriaHelper.pollUiThread(Criteria.equals(numExpectedActivities, new C allable<Integer>() { | |
1666 @Override | |
1667 public Integer call() { | |
1668 return ApplicationStatus.getRunningActivities().size(); | |
1669 } | |
1670 })); | |
1671 assertEquals("Window 1 should have 1 tab.", 1, firstModel.getCount()); | |
1672 | |
1673 // Restore closed tab from second window. It should be created in first window. | |
1674 openMostRecentlyClosedTabOnUiThread(firstSelector); | |
1675 assertEquals("Closed tab in second window should be restored in the firs t window.", 2, | |
1676 firstModel.getCount()); | |
1677 Tab[] firstWindowTabs = new Tab[]{firstModel.getTabAt(0), firstModel.get TabAt(1)}; | |
1678 checkState(firstModel, firstWindowTabs, firstWindowTabs[0], EMPTY, first WindowTabs, | |
1679 firstWindowTabs[0]); | |
1680 assertEquals(TEST_URL_1, firstWindowTabs[1].getUrl()); | |
1681 } | |
1413 } | 1682 } |
OLD | NEW |