Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4072)

Unified Diff: chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..85a0de8a6aee9b11dae741e73d5625f33d9995a1
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
@@ -0,0 +1,1414 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tabmodel;
+
+import android.test.FlakyTest;
+import android.test.suitebuilder.annotation.MediumTest;
+
+
+import org.chromium.base.CommandLine;
+import org.chromium.base.ThreadUtils;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
+import org.chromium.chrome.test.ChromeTabbedActivityTestBase;
+import org.chromium.content.browser.test.util.CallbackHelper;
+import org.chromium.content_public.browser.LoadUrlParams;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Tests undo and restoring of tabs in a {@link TabModel}.
+ */
+public class UndoTabModelTest extends ChromeTabbedActivityTestBase {
+ private static final Tab[] EMPTY = new Tab[] { };
+
+ @Override
+ public void startMainActivity() throws InterruptedException {
+ CommandLine.getInstance().appendSwitch(ChromeSwitches.ENABLE_HIGH_END_UI_UNDO);
+ startMainActivityOnBlankPage();
+ }
+
+ private void checkState(
+ final TabModel model, final Tab[] tabsList, final Tab selectedTab,
+ final Tab[] closingTabs, final Tab[] fullTabsList,
+ final Tab fullSelectedTab) {
+ // Keeping these checks on the test thread so the stacks are useful for identifying
+ // failures.
+
+ // Check the selected tab.
+ assertEquals("Wrong selected tab", selectedTab, TabModelUtils.getCurrentTab(model));
+
+ // Check the list of tabs.
+ assertEquals("Incorrect number of tabs", tabsList.length, model.getCount());
+ for (int i = 0; i < tabsList.length; i++) {
+ assertEquals("Unexpected tab at " + i, tabsList[i].getId(), model.getTabAt(i).getId());
+ }
+
+ // Check the list of tabs we expect to be closing.
+ for (int i = 0; i < closingTabs.length; i++) {
+ int id = closingTabs[i].getId();
+ assertTrue("Tab " + id + " not in closing list", model.isClosurePending(id));
+ }
+
+ TabList fullModel = model.getComprehensiveModel();
+
+ // Check the comprehensive selected tab.
+ assertEquals("Wrong selected tab", fullSelectedTab, TabModelUtils.getCurrentTab(fullModel));
+
+ // Check the comprehensive list of tabs.
+ assertEquals("Incorrect number of tabs", fullTabsList.length, fullModel.getCount());
+ for (int i = 0; i < fullModel.getCount(); i++) {
+ int id = fullModel.getTabAt(i).getId();
+ assertEquals("Unexpected tab at " + i, fullTabsList[i].getId(), id);
+ }
+ }
+
+ private void createTabOnUiThread(final ChromeTabCreator tabCreator) {
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ tabCreator.createNewTab(new LoadUrlParams("about:blank"),
+ TabLaunchType.FROM_MENU_OR_OVERVIEW, null);
+ }
+ });
+ }
+
+ private void selectTabOnUiThread(final TabModel model, final Tab tab) {
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ model.setIndex(model.indexOf(tab), TabSelectionType.FROM_USER);
+ }
+ });
+ }
+
+ private void closeTabOnUiThread(
+ final TabModel model, final Tab tab, final boolean undoable)
+ throws InterruptedException {
+ // Check preconditions.
+ assertFalse(tab.isClosing());
+ assertTrue(tab.isInitialized());
+ assertFalse(model.isClosurePending(tab.getId()));
+ assertNotNull(TabModelUtils.getTabById(model, tab.getId()));
+
+ final CallbackHelper didReceivePendingClosureHelper = new CallbackHelper();
+ model.addObserver(new EmptyTabModelObserver() {
+ @Override
+ public void tabPendingClosure(Tab tab) {
+ didReceivePendingClosureHelper.notifyCalled();
+ }
+ });
+
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ // Take action.
+ model.closeTab(tab, true, false, undoable);
+ }
+ });
+
+ boolean didUndo = undoable && model.supportsPendingClosures();
+
+ // Make sure the TabModel throws a tabPendingClosure callback if necessary.
+ if (didUndo) {
+ try {
+ didReceivePendingClosureHelper.waitForCallback(0);
+ } catch (TimeoutException e) {
+ fail();
+ }
+ }
+
+ // Check post conditions
+ assertEquals(didUndo, model.isClosurePending(tab.getId()));
+ assertNull(TabModelUtils.getTabById(model, tab.getId()));
+ assertTrue(tab.isClosing());
+ assertEquals(didUndo, tab.isInitialized());
+ }
+
+ private void closeAllTabsOnUiThread(final TabModel model) {
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ model.closeAllTabs();
+ }
+ });
+ }
+
+ private void moveTabOnUiThread(final TabModel model, final Tab tab, final int newIndex) {
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ model.moveTab(tab.getId(), newIndex);
+ }
+ });
+ }
+
+ private void cancelTabClosureOnUiThread(final TabModel model, final Tab tab)
+ throws InterruptedException {
+ // Check preconditions.
+ assertTrue(tab.isClosing());
+ assertTrue(tab.isInitialized());
+ assertTrue(model.isClosurePending(tab.getId()));
+ assertNull(TabModelUtils.getTabById(model, tab.getId()));
+
+ final CallbackHelper didReceiveClosureCancelledHelper = new CallbackHelper();
+ model.addObserver(new EmptyTabModelObserver() {
+ @Override
+ public void tabClosureUndone(Tab tab) {
+ didReceiveClosureCancelledHelper.notifyCalled();
+ }
+ });
+
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ // Take action.
+ model.cancelTabClosure(tab.getId());
+ }
+ });
+
+ // Make sure the TabModel throws a tabClosureUndone.
+ try {
+ didReceiveClosureCancelledHelper.waitForCallback(0);
+ } catch (TimeoutException e) {
+ fail();
+ }
+
+ // Check post conditions.
+ assertFalse(model.isClosurePending(tab.getId()));
+ assertNotNull(TabModelUtils.getTabById(model, tab.getId()));
+ assertFalse(tab.isClosing());
+ assertTrue(tab.isInitialized());
+ }
+
+ private void cancelAllTabClosuresOnUiThread(final TabModel model, final Tab[] expectedToClose)
+ throws InterruptedException {
+ final CallbackHelper tabClosureUndoneHelper = new CallbackHelper();
+
+ for (int i = 0; i < expectedToClose.length; i++) {
+ Tab tab = expectedToClose[i];
+ assertTrue(tab.isClosing());
+ assertTrue(tab.isInitialized());
+ assertTrue(model.isClosurePending(tab.getId()));
+ assertNull(TabModelUtils.getTabById(model, tab.getId()));
+
+ // Make sure that this TabModel throws the right events.
+ model.addObserver(new EmptyTabModelObserver() {
+ @Override
+ public void tabClosureUndone(Tab currentTab) {
+ tabClosureUndoneHelper.notifyCalled();
+ }
+ });
+ }
+
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < expectedToClose.length; i++) {
+ Tab tab = expectedToClose[i];
+ model.cancelTabClosure(tab.getId());
+ }
+ }
+ });
+
+ try {
+ tabClosureUndoneHelper.waitForCallback(0, expectedToClose.length);
+ } catch (TimeoutException e) {
+ fail();
+ }
+
+ for (int i = 0; i < expectedToClose.length; i++) {
+ final Tab tab = expectedToClose[i];
+ assertFalse(model.isClosurePending(tab.getId()));
+ assertNotNull(TabModelUtils.getTabById(model, tab.getId()));
+ assertFalse(tab.isClosing());
+ assertTrue(tab.isInitialized());
+ }
+ }
+
+ private void commitTabClosureOnUiThread(final TabModel model, final Tab tab)
+ throws InterruptedException {
+ // Check preconditions.
+ assertTrue(tab.isClosing());
+ assertTrue(tab.isInitialized());
+ assertTrue(model.isClosurePending(tab.getId()));
+ assertNull(TabModelUtils.getTabById(model, tab.getId()));
+
+ final CallbackHelper didReceiveClosureCommittedHelper = new CallbackHelper();
+ model.addObserver(new EmptyTabModelObserver() {
+ @Override
+ public void tabClosureCommitted(Tab tab) {
+ didReceiveClosureCommittedHelper.notifyCalled();
+ }
+ });
+
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ // Take action.
+ model.commitTabClosure(tab.getId());
+ }
+ });
+
+ // Make sure the TabModel throws a tabClosureCommitted.
+ try {
+ didReceiveClosureCommittedHelper.waitForCallback(0);
+ } catch (TimeoutException e) {
+ fail();
+ }
+
+ // Check post conditions
+ assertFalse(model.isClosurePending(tab.getId()));
+ assertNull(TabModelUtils.getTabById(model, tab.getId()));
+ assertTrue(tab.isClosing());
+ assertFalse(tab.isInitialized());
+ }
+
+ private void commitAllTabClosuresOnUiThread(final TabModel model, Tab[] expectedToClose)
+ throws InterruptedException {
+ final CallbackHelper tabClosureCommittedHelper = new CallbackHelper();
+
+ for (int i = 0; i < expectedToClose.length; i++) {
+ Tab tab = expectedToClose[i];
+ assertTrue(tab.isClosing());
+ assertTrue(tab.isInitialized());
+ assertTrue(model.isClosurePending(tab.getId()));
+
+ // Make sure that this TabModel throws the right events.
+ model.addObserver(new EmptyTabModelObserver() {
+ @Override
+ public void tabClosureCommitted(Tab currentTab) {
+ tabClosureCommittedHelper.notifyCalled();
+ }
+ });
+ }
+
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ model.commitAllTabClosures();
+ }
+ });
+
+ try {
+ tabClosureCommittedHelper.waitForCallback(0, expectedToClose.length);
+ } catch (TimeoutException e) {
+ fail();
+ }
+ for (int i = 0; i < expectedToClose.length; i++) {
+ final Tab tab = expectedToClose[i];
+ assertTrue(tab.isClosing());
+ assertFalse(tab.isInitialized());
+ assertFalse(model.isClosurePending(tab.getId()));
+ }
+ }
+
+ private void saveStateOnUiThread(final TabModelSelector selector) {
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ ((TabModelSelectorImpl) selector).saveState();
+ }
+ });
+
+ for (int i = 0; i < selector.getModels().size(); i++) {
+ TabList tabs = selector.getModelAt(i).getComprehensiveModel();
+ for (int j = 0; j < tabs.getCount(); j++) {
+ assertFalse(tabs.isClosurePending(tabs.getTabAt(j).getId()));
+ }
+ }
+ }
+
+ /**
+ * Test undo with a single tab with the following actions/expected states:
+ * Action Model List Close List Comprehensive List
+ * 1. Initial State [ 0s ] - [ 0s ]
+ * 2. CloseTab(0, allow undo) - [ 0 ] [ 0s ]
+ * 3. CancelClose(0) [ 0s ] - [ 0s ]
+ * 4. CloseTab(0, allow undo) - [ 0 ] [ 0s ]
+ * 5. CommitClose(0) - - -
+ * 6. CreateTab(0) [ 0s ] - [ 0s ]
+ * 7. CloseTab(0, allow undo) - [ 0 ] [ 0s ]
+ * 8. CommitAllClose - - -
+ * 9. CreateTab(0) [ 0s ] - [ 0s ]
+ * 10. CloseTab(0, disallow undo) - - -
+ *
+ * @throws InterruptedException
+ */
+ @MediumTest
+ public void testSingleTab() throws InterruptedException {
+ TabModel model = getActivity().getTabModelSelector().getModel(false);
+ ChromeTabCreator tabCreator = getActivity().getTabCreator(false);
+
+ Tab tab0 = model.getTabAt(0);
+
+ Tab[] fullList = new Tab[] { tab0 };
+
+ // 1.
+ checkState(model, new Tab[] { tab0 }, tab0, EMPTY, fullList, tab0);
+
+ // 2.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, EMPTY, null, new Tab[] { tab0 }, fullList, tab0);
+
+ // 3.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0 }, tab0, EMPTY, fullList, tab0);
+
+ // 4.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, EMPTY, null, new Tab[] { tab0 }, fullList, tab0);
+
+ // 5.
+ commitTabClosureOnUiThread(model, tab0);
+ fullList = EMPTY;
+ checkState(model, EMPTY, null, EMPTY, EMPTY, null);
+
+ // 6.
+ createTabOnUiThread(tabCreator);
+ tab0 = model.getTabAt(0);
+ fullList = new Tab[] { tab0 };
+ checkState(model, new Tab[] { tab0 }, tab0, EMPTY, fullList, tab0);
+
+ // 7.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, EMPTY, null, new Tab[] { tab0 }, fullList, tab0);
+
+ // 8.
+ commitAllTabClosuresOnUiThread(model, new Tab[] { tab0 });
+ fullList = EMPTY;
+ checkState(model, EMPTY, null, EMPTY, EMPTY, null);
+
+ // 9.
+ createTabOnUiThread(tabCreator);
+ tab0 = model.getTabAt(0);
+ fullList = new Tab[] { tab0 };
+ checkState(model, new Tab[] { tab0 }, tab0, EMPTY, fullList, tab0);
+
+ // 10.
+ closeTabOnUiThread(model, tab0, false);
+ fullList = EMPTY;
+ checkState(model, EMPTY, null, EMPTY, fullList, null);
+ assertTrue(tab0.isClosing());
+ assertFalse(tab0.isInitialized());
+ }
+
+ /**
+ * Flaky on instrumentation-nakasi-clankium. See http://crbug.com/417720.
+ *
+ * Test undo with two tabs with the following actions/expected states:
+ * Action Model List Close List Comprehensive List
+ * 1. Initial State [ 0 1s ] - [ 0 1s ]
+ * 2. CloseTab(0, allow undo) [ 1s ] [ 0 ] [ 0 1s ]
+ * 3. CancelClose(0) [ 0s 1 ] - [ 0s 1 ]
+ * 4. CloseTab(0, allow undo) [ 1s ] [ 0 ] [ 0 1s ]
+ * 5. CloseTab(1, allow undo) - [ 1 0 ] [ 0 1s ]
+ * 6. CancelClose(1) [ 1s ] [ 0 ] [ 0s 1 ]
+ * 7. CancelClose(0) [ 0s 1 ] - [ 0s 1 ]
+ * 8. CloseTab(1, allow undo) [ 0s ] [ 1 ] [ 0s 1 ]
+ * 9. CloseTab(0, allow undo) - [ 0 1 ] [ 0s 1 ]
+ * 10. CancelClose(1) [ 1s ] [ 0 ] [ 0 1s ]
+ * 11. CancelClose(0) [ 0s 1 ] - [ 0s 1 ]
+ * 12. CloseTab(1, allow undo) [ 0s ] [ 1 ] [ 0s 1 ]
+ * 13. CloseTab(0, allow undo) - [ 0 1 ] [ 0s 1 ]
+ * 14. CancelClose(0) [ 0s ] [ 1 ] [ 0s 1 ]
+ * 15. CloseTab(0, allow undo) - [ 0 1 ] [ 0s 1 ]
+ * 16. CancelClose(0) [ 0s ] [ 1 ] [ 0s 1 ]
+ * 17. CancelClose(1) [ 0 1s ] - [ 0 1s ]
+ * 18. CloseTab(0, disallow undo) [ 1s ] - [ 1s ]
+ * 19. CreateTab(0) [ 1 0s ] - [ 1 0s ]
+ * 20. CloseTab(0, allow undo) [ 1s ] [ 0 ] [ 1s 0 ]
+ * 21. CommitClose(0) [ 1s ] - [ 1s ]
+ * 22. CreateTab(0) [ 1 0s ] - [ 1 0s ]
+ * 23. CloseTab(0, allow undo) [ 1s ] [ 0 ] [ 1s 0 ]
+ * 24. CloseTab(1, allow undo) - [ 1 0 ] [ 1s 0 ]
+ * 25. CommitAllClose - - -
+ *
+ * @throws InterruptedException
+ * @MediumTest
+ */
+ @FlakyTest
+ public void testTwoTabs() throws InterruptedException {
+ TabModel model = getActivity().getTabModelSelector().getModel(false);
+ ChromeTabCreator tabCreator = getActivity().getTabCreator(false);
+ createTabOnUiThread(tabCreator);
+
+ Tab tab0 = model.getTabAt(0);
+ Tab tab1 = model.getTabAt(1);
+
+ Tab[] fullList = new Tab[] { tab0, tab1 };
+
+ // 1.
+ checkState(model, new Tab[] { tab0, tab1 }, tab1, EMPTY, fullList, tab1);
+
+ // 2.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab1 }, tab1, new Tab[] { tab0 }, fullList, tab1);
+
+ // 3.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0, tab1 }, tab0, EMPTY, fullList, tab0);
+
+ // 4.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab1 }, tab1, new Tab[] { tab0 }, fullList, tab1);
+
+ // 5.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, EMPTY, null, new Tab[] { tab0, tab1 }, fullList, tab0);
+
+ // 6.
+ cancelTabClosureOnUiThread(model, tab1);
+ checkState(model, new Tab[] { tab1 }, tab1, new Tab[] { tab0 }, fullList, tab1);
+
+ // 7.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0, tab1 }, tab0, EMPTY, fullList, tab0);
+
+ // 8.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab0 }, tab0, new Tab[] { tab1 }, fullList, tab0);
+
+ // 9.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, EMPTY, null, new Tab[] { tab0, tab1 }, fullList, tab0);
+
+ // 10.
+ cancelTabClosureOnUiThread(model, tab1);
+ checkState(model, new Tab[] { tab1 }, tab1, new Tab[] { tab0 }, fullList, tab1);
+
+ // 11.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0, tab1 }, tab0, EMPTY, fullList, tab0);
+
+ // 12.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab0 }, tab0, new Tab[] { tab1 }, fullList, tab0);
+
+ // 13.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, EMPTY, null, new Tab[] { tab0, tab1 }, fullList, tab0);
+
+ // 14.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0 }, tab0, new Tab[] { tab1 }, fullList, tab0);
+
+ // 15.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, EMPTY, null, new Tab[] { tab0, tab1 }, fullList, tab0);
+
+ // 16.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0 }, tab0, new Tab[] { tab1 }, fullList, tab0);
+
+ // 17.
+ cancelTabClosureOnUiThread(model, tab1);
+ checkState(model, new Tab[] { tab0, tab1 }, tab1, EMPTY, fullList, tab1);
+
+ // 18.
+ closeTabOnUiThread(model, tab0, false);
+ fullList = new Tab[] { tab1 };
+ checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1);
+
+ // 19.
+ createTabOnUiThread(tabCreator);
+ tab0 = model.getTabAt(1);
+ fullList = new Tab[] { tab1, tab0 };
+ checkState(model, new Tab[] { tab1, tab0 }, tab0, EMPTY, fullList, tab0);
+
+ // 20.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab1 }, tab1, new Tab[] { tab0 }, fullList, tab1);
+
+ // 21.
+ commitTabClosureOnUiThread(model, tab0);
+ fullList = new Tab[] { tab1 };
+ checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1);
+
+ // 22.
+ createTabOnUiThread(tabCreator);
+ tab0 = model.getTabAt(1);
+ fullList = new Tab[] { tab1, tab0 };
+ checkState(model, new Tab[] { tab1, tab0 }, tab0, EMPTY, fullList, tab0);
+
+ // 23.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab1 }, tab1, new Tab[] { tab0 }, fullList, tab1);
+
+ // 24.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, EMPTY, null, new Tab[] { tab1, tab0 }, fullList, tab1);
+
+ // 25.
+ commitAllTabClosuresOnUiThread(model, new Tab[] { tab1, tab0 });
+ checkState(model, EMPTY, null, EMPTY, EMPTY, null);
+ }
+
+ /**
+ * Test restoring in the same order of closing with the following actions/expected states:
+ * Action Model List Close List Comprehensive List
+ * 1. Initial State [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 2. CloseTab(0, allow undo) [ 1 2 3s ] [ 0 ] [ 0 1 2 3s ]
+ * 3. CloseTab(1, allow undo) [ 2 3s ] [ 1 0 ] [ 0 1 2 3s ]
+ * 4. CloseTab(2, allow undo) [ 3s ] [ 2 1 0 ] [ 0 1 2 3s ]
+ * 5. CloseTab(3, allow undo) - [ 3 2 1 0 ] [ 0s 1 2 3 ]
+ * 6. CancelClose(3) [ 3s ] [ 2 1 0 ] [ 0 1 2 3s ]
+ * 7. CancelClose(2) [ 2s 3 ] [ 1 0 ] [ 0 1 2s 3 ]
+ * 8. CancelClose(1) [ 1s 2 3 ] [ 0 ] [ 0 1s 2 3 ]
+ * 9. CancelClose(0) [ 0s 1 2 3 ] - [ 0s 1 2 3 ]
+ * 10. SelectTab(3) [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 11. CloseTab(3, allow undo) [ 0 1 2s ] [ 3 ] [ 0 1 2s 3 ]
+ * 12. CloseTab(2, allow undo) [ 0 1s ] [ 2 3 ] [ 0 1s 2 3 ]
+ * 13. CloseTab(1, allow undo) [ 0s ] [ 1 2 3 ] [ 0s 1 2 3 ]
+ * 14. CloseTab(0, allow undo) - [ 0 1 2 3 ] [ 0s 1 2 3 ]
+ * 15. CancelClose(0) [ 0s ] [ 1 2 3 ] [ 0s 1 2 3 ]
+ * 16. CancelClose(1) [ 0 1s ] [ 2 3 ] [ 0 1s 2 3 ]
+ * 17. CancelClose(2) [ 0 1 2s ] [ 3 ] [ 0 1 2s 3 ]
+ * 18. CancelClose(3) [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 19. CloseTab(2, allow undo) [ 0 1 3s ] [ 2 ] [ 0 1 2 3s ]
+ * 20. CloseTab(0, allow undo) [ 1 3s ] [ 0 2 ] [ 0 1 2 3s ]
+ * 21. CloseTab(3, allow undo) [ 1s ] [ 3 0 2 ] [ 0 1s 2 3 ]
+ * 22. CancelClose(3) [ 1 3s ] [ 0 2 ] [ 0 1 2 3s ]
+ * 23. CancelClose(0) [ 0s 1 3 ] [ 2 ] [ 0s 1 2 3 ]
+ * 24. CancelClose(2) [ 0 1 2s 3 ] - [ 0 1 2s 3 ]
+ *
+ * @throws InterruptedException
+ */
+ @MediumTest
+ public void testInOrderRestore() throws InterruptedException {
+ TabModel model = getActivity().getTabModelSelector().getModel(false);
+ ChromeTabCreator tabCreator = getActivity().getTabCreator(false);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+
+ Tab tab0 = model.getTabAt(0);
+ Tab tab1 = model.getTabAt(1);
+ Tab tab2 = model.getTabAt(2);
+ Tab tab3 = model.getTabAt(3);
+
+ final Tab[] fullList = new Tab[] { tab0, tab1, tab2, tab3 };
+
+ // 1.
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 2.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab1, tab2, tab3 }, tab3, new Tab[] { tab0 },
+ fullList, tab3);
+
+ // 3.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab2, tab3 }, tab3, new Tab[] { tab1, tab0 },
+ fullList, tab3);
+
+ // 4.
+ closeTabOnUiThread(model, tab2, true);
+ checkState(model, new Tab[] { tab3 }, tab3, new Tab[] { tab2, tab1, tab0 },
+ fullList, tab3);
+
+ // 5.
+ closeTabOnUiThread(model, tab3, true);
+ checkState(model, EMPTY, null, new Tab[] { tab3, tab2, tab1, tab0 }, fullList, tab0);
+
+ // 6.
+ cancelTabClosureOnUiThread(model, tab3);
+ checkState(model, new Tab[] { tab3 }, tab3, new Tab[] { tab2, tab1, tab0 },
+ fullList, tab3);
+
+ // 7.
+ cancelTabClosureOnUiThread(model, tab2);
+ checkState(model, new Tab[] { tab2, tab3 }, tab2, new Tab[] { tab1, tab0 },
+ fullList, tab2);
+
+ // 8.
+ cancelTabClosureOnUiThread(model, tab1);
+ checkState(model, new Tab[] { tab1, tab2, tab3 }, tab1, new Tab[] { tab0 },
+ fullList, tab1);
+
+ // 9.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab0, EMPTY, fullList, tab0);
+
+ // 10.
+ selectTabOnUiThread(model, tab3);
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 11.
+ closeTabOnUiThread(model, tab3, true);
+ checkState(model, new Tab[] { tab0, tab1, tab2 }, tab2, new Tab[] { tab3 },
+ fullList, tab2);
+
+ // 12.
+ closeTabOnUiThread(model, tab2, true);
+ checkState(model, new Tab[] { tab0, tab1 }, tab1, new Tab[] { tab2, tab3 },
+ fullList, tab1);
+
+ // 13.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab0 }, tab0, new Tab[] { tab1, tab2, tab3 },
+ fullList, tab0);
+
+ // 14.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, EMPTY, null, new Tab[] { tab0, tab1, tab2, tab3 }, fullList, tab0);
+
+ // 15.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0 }, tab0, new Tab[] { tab1, tab2, tab3 },
+ fullList, tab0);
+
+ // 16.
+ cancelTabClosureOnUiThread(model, tab1);
+ checkState(model, new Tab[] { tab0, tab1 }, tab1, new Tab[] { tab2, tab3 },
+ fullList, tab1);
+
+ // 17.
+ cancelTabClosureOnUiThread(model, tab2);
+ checkState(model, new Tab[] { tab0, tab1, tab2 }, tab2, new Tab[] { tab3 },
+ fullList, tab2);
+
+ // 18.
+ cancelTabClosureOnUiThread(model, tab3);
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 19.
+ closeTabOnUiThread(model, tab2, true);
+ checkState(model, new Tab[] { tab0, tab1, tab3 }, tab3, new Tab[] { tab2 },
+ fullList, tab3);
+
+ // 20.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab1, tab3 }, tab3, new Tab[] { tab0, tab2 },
+ fullList, tab3);
+
+ // 21.
+ closeTabOnUiThread(model, tab3, true);
+ checkState(model, new Tab[] { tab1 }, tab1, new Tab[] { tab3, tab0, tab2 },
+ fullList, tab1);
+
+ // 22.
+ cancelTabClosureOnUiThread(model, tab3);
+ checkState(model, new Tab[] { tab1, tab3 }, tab3, new Tab[] { tab0, tab2 },
+ fullList, tab3);
+
+ // 23.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0, tab1, tab3 }, tab0, new Tab[] { tab2 },
+ fullList, tab0);
+
+ // 24.
+ cancelTabClosureOnUiThread(model, tab2);
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab2, EMPTY, fullList, tab2);
+ }
+
+
+ /**
+ * Test restoring in the reverse of closing with the following actions/expected states:
+ * Action Model List Close List Comprehensive List
+ * 1. Initial State [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 2. CloseTab(0, allow undo) [ 1 2 3s ] [ 0 ] [ 0 1 2 3s ]
+ * 3. CloseTab(1, allow undo) [ 2 3s ] [ 1 0 ] [ 0 1 2 3s ]
+ * 4. CloseTab(2, allow undo) [ 3s ] [ 2 1 0 ] [ 0 1 2 3s ]
+ * 5. CloseTab(3, allow undo) - [ 3 2 1 0 ] [ 0s 1 2 3 ]
+ * 6. CancelClose(0) [ 0s ] [ 3 2 1 ] [ 0s 1 2 3 ]
+ * 7. CancelClose(1) [ 0 1s ] [ 3 2 ] [ 0 1s 2 3 ]
+ * 8. CancelClose(2) [ 0 1 2s ] [ 3 ] [ 0 1 2s 3 ]
+ * 9. CancelClose(3) [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 10. CloseTab(3, allow undo) [ 0 1 2s ] [ 3 ] [ 0 1 2s 3 ]
+ * 11. CloseTab(2, allow undo) [ 0 1s ] [ 2 3 ] [ 0 1s 2 3 ]
+ * 12. CloseTab(1, allow undo) [ 0s ] [ 1 2 3 ] [ 0s 1 2 3 ]
+ * 13. CloseTab(0, allow undo) - [ 0 1 2 3 ] [ 0s 1 2 3 ]
+ * 14. CancelClose(3) [ 3s ] [ 0 1 2 ] [ 0 1 2 3s ]
+ * 15. CancelClose(2) [ 2s 3 ] [ 0 1 ] [ 0 1 2s 3 ]
+ * 16. CancelClose(1) [ 1s 2 3 ] [ 0 ] [ 0 1s 2 3 ]
+ * 17. CancelClose(0) [ 0s 1 2 3 ] - [ 0s 1 2 3 ]
+ * 18. SelectTab(3) [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 19. CloseTab(2, allow undo) [ 0 1 3s ] [ 2 ] [ 0 1 2 3s ]
+ * 20. CloseTab(0, allow undo) [ 1 3s ] [ 0 2 ] [ 0 1 2 3s ]
+ * 21. CloseTab(3, allow undo) [ 1s ] [ 3 0 2 ] [ 0 1s 2 3 ]
+ * 22. CancelClose(2) [ 1 2s ] [ 3 0 ] [ 0 1 2s 3 ]
+ * 23. CancelClose(0) [ 0s 1 2 ] [ 3 ] [ 0s 1 2 3 ]
+ * 24. CancelClose(3) [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ *
+ * @throws InterruptedException
+ */
+ @MediumTest
+ public void testReverseOrderRestore() throws InterruptedException {
+ TabModel model = getActivity().getTabModelSelector().getModel(false);
+ ChromeTabCreator tabCreator = getActivity().getTabCreator(false);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+
+ Tab tab0 = model.getTabAt(0);
+ Tab tab1 = model.getTabAt(1);
+ Tab tab2 = model.getTabAt(2);
+ Tab tab3 = model.getTabAt(3);
+
+ Tab[] fullList = new Tab[] { tab0, tab1, tab2, tab3 };
+
+ // 1.
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 2.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab1, tab2, tab3 }, tab3, new Tab[] { tab0 },
+ fullList, tab3);
+
+ // 3.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab2, tab3 }, tab3, new Tab[] { tab1, tab0 },
+ fullList, tab3);
+
+ // 4.
+ closeTabOnUiThread(model, tab2, true);
+ checkState(model, new Tab[] { tab3 }, tab3, new Tab[] { tab2, tab1, tab0 },
+ fullList, tab3);
+
+ // 5.
+ closeTabOnUiThread(model, tab3, true);
+ checkState(model, EMPTY, null, new Tab[] { tab3, tab2, tab1, tab0 }, fullList, tab0);
+
+ // 6.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0 }, tab0, new Tab[] { tab3, tab2, tab1 },
+ fullList, tab0);
+
+ // 7.
+ cancelTabClosureOnUiThread(model, tab1);
+ checkState(model, new Tab[] { tab0, tab1 }, tab1, new Tab[] { tab3, tab2 },
+ fullList, tab1);
+
+ // 8.
+ cancelTabClosureOnUiThread(model, tab2);
+ checkState(model, new Tab[] { tab0, tab1, tab2 }, tab2, new Tab[] { tab3 },
+ fullList, tab2);
+
+ // 9.
+ cancelTabClosureOnUiThread(model, tab3);
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 10.
+ closeTabOnUiThread(model, tab3, true);
+ checkState(model, new Tab[] { tab0, tab1, tab2 }, tab2, new Tab[] { tab3 },
+ fullList, tab2);
+
+ // 11.
+ closeTabOnUiThread(model, tab2, true);
+ checkState(model, new Tab[] { tab0, tab1 }, tab1, new Tab[] { tab2, tab3 },
+ fullList, tab1);
+
+ // 12.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab0 }, tab0, new Tab[] { tab1, tab2, tab3 },
+ fullList, tab0);
+
+ // 13.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, EMPTY, null, new Tab[] { tab0, tab1, tab2, tab3 }, fullList, tab0);
+
+ // 14.
+ cancelTabClosureOnUiThread(model, tab3);
+ checkState(model, new Tab[] { tab3 }, tab3, new Tab[] { tab0, tab1, tab2 },
+ fullList, tab3);
+
+ // 15.
+ cancelTabClosureOnUiThread(model, tab2);
+ checkState(model, new Tab[] { tab2, tab3 }, tab2, new Tab[] { tab0, tab1 },
+ fullList, tab2);
+
+ // 16.
+ cancelTabClosureOnUiThread(model, tab1);
+ checkState(model, new Tab[] { tab1, tab2, tab3 }, tab1, new Tab[] { tab0 },
+ fullList, tab1);
+
+ // 17.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab0, EMPTY, fullList, tab0);
+
+ // 18.
+ selectTabOnUiThread(model, tab3);
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 19.
+ closeTabOnUiThread(model, tab2, true);
+ checkState(model, new Tab[] { tab0, tab1, tab3 }, tab3, new Tab[] { tab2 },
+ fullList, tab3);
+
+ // 20.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab1, tab3 }, tab3, new Tab[] { tab0, tab2 },
+ fullList, tab3);
+
+ // 21.
+ closeTabOnUiThread(model, tab3, true);
+ checkState(model, new Tab[] { tab1 }, tab1, new Tab[] { tab3, tab0, tab2 },
+ fullList, tab1);
+
+ // 22.
+ cancelTabClosureOnUiThread(model, tab2);
+ checkState(model, new Tab[] { tab1, tab2 }, tab2, new Tab[] { tab3, tab0 },
+ fullList, tab2);
+
+ // 23.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0, tab1, tab2 }, tab0, new Tab[] { tab3 },
+ fullList, tab0);
+
+ // 24.
+ cancelTabClosureOnUiThread(model, tab3);
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+ }
+
+ /**
+ * Test restoring out of order with the following actions/expected states:
+ * Action Model List Close List Comprehensive List
+ * 1. Initial State [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 2. CloseTab(0, allow undo) [ 1 2 3s ] [ 0 ] [ 0 1 2 3s ]
+ * 3. CloseTab(1, allow undo) [ 2 3s ] [ 1 0 ] [ 0 1 2 3s ]
+ * 4. CloseTab(2, allow undo) [ 3s ] [ 2 1 0 ] [ 0 1 2 3s ]
+ * 5. CloseTab(3, allow undo) - [ 3 2 1 0 ] [ 0s 1 2 3 ]
+ * 6. CancelClose(2) [ 2s ] [ 3 1 0 ] [ 0 1 2s 3 ]
+ * 7. CancelClose(1) [ 1s 2 ] [ 3 0 ] [ 0 1s 2 3 ]
+ * 8. CancelClose(3) [ 1 2 3s ] [ 0 ] [ 0 1 2 3s ]
+ * 9. CancelClose(0) [ 0s 1 2 3 ] - [ 0s 1 2 3 ]
+ * 10. CloseTab(1, allow undo) [ 0s 2 3 ] [ 1 ] [ 0s 1 2 3 ]
+ * 11. CancelClose(1) [ 0 1s 2 3 ] - [ 0 1s 2 3 ]
+ * 12. CloseTab(3, disallow undo) [ 0 1s 2 ] - [ 0 1s 2 ]
+ * 13. CloseTab(1, allow undo) [ 0s 2 ] [ 1 ] [ 0s 1 2 ]
+ * 14. CloseTab(0, allow undo) [ 2s ] [ 0 1 ] [ 0 1 2s ]
+ * 15. CommitClose(0) [ 2s ] [ 1 ] [ 1 2s ]
+ * 16. CancelClose(1) [ 1s 2 ] - [ 1s 2 ]
+ * 17. CloseTab(2, disallow undo) [ 1s ] - [ 1s ]
+ *
+ * @throws InterruptedException
+ */
+ @MediumTest
+ public void testOutOfOrder1() throws InterruptedException {
+ TabModel model = getActivity().getTabModelSelector().getModel(false);
+ ChromeTabCreator tabCreator = getActivity().getTabCreator(false);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+
+ Tab tab0 = model.getTabAt(0);
+ Tab tab1 = model.getTabAt(1);
+ Tab tab2 = model.getTabAt(2);
+ Tab tab3 = model.getTabAt(3);
+
+ Tab[] fullList = new Tab[] { tab0, tab1, tab2, tab3 };
+
+ // 1.
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 2.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab1, tab2, tab3 }, tab3, new Tab[] { tab0 },
+ fullList, tab3);
+
+ // 3.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab2, tab3 }, tab3, new Tab[] { tab1, tab0 },
+ fullList, tab3);
+
+ // 4.
+ closeTabOnUiThread(model, tab2, true);
+ checkState(model, new Tab[] { tab3 }, tab3, new Tab[] { tab2, tab1, tab0 },
+ fullList, tab3);
+
+ // 5.
+ closeTabOnUiThread(model, tab3, true);
+ checkState(model, EMPTY, null, new Tab[] { tab3, tab2, tab1, tab0 }, fullList, tab0);
+
+ // 6.
+ cancelTabClosureOnUiThread(model, tab2);
+ checkState(model, new Tab[] { tab2 }, tab2, new Tab[] { tab3, tab1, tab0 },
+ fullList, tab2);
+
+ // 7.
+ cancelTabClosureOnUiThread(model, tab1);
+ checkState(model, new Tab[] { tab1, tab2 }, tab1, new Tab[] { tab3, tab0 },
+ fullList, tab1);
+
+ // 8.
+ cancelTabClosureOnUiThread(model, tab3);
+ checkState(model, new Tab[] { tab1, tab2, tab3 }, tab3, new Tab[] { tab0 },
+ fullList, tab3);
+
+ // 9.
+ cancelTabClosureOnUiThread(model, tab0);
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab0, EMPTY, fullList, tab0);
+
+ // 10.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab0, tab2, tab3 }, tab0, new Tab[] { tab1 },
+ fullList, tab0);
+
+ // 11.
+ cancelTabClosureOnUiThread(model, tab1);
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab1, EMPTY, fullList, tab1);
+
+ // 12.
+ closeTabOnUiThread(model, tab3, false);
+ fullList = new Tab[] { tab0, tab1, tab2 };
+ checkState(model, new Tab[] { tab0, tab1, tab2 }, tab1, EMPTY, fullList, tab1);
+
+ // 13.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab0, tab2 }, tab0, new Tab[] { tab1 }, fullList,
+ tab0);
+
+ // 14.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab2 }, tab2, new Tab[] { tab0, tab1 }, fullList,
+ tab2);
+
+ // 15.
+ commitTabClosureOnUiThread(model, tab0);
+ fullList = new Tab[] { tab1, tab2 };
+ checkState(model, new Tab[] { tab2 }, tab2, new Tab[] { tab1 }, fullList, tab2);
+
+ // 16.
+ cancelTabClosureOnUiThread(model, tab1);
+ checkState(model, new Tab[] { tab1, tab2 }, tab1, EMPTY, fullList, tab1);
+
+ // 17.
+ closeTabOnUiThread(model, tab2, false);
+ fullList = new Tab[] { tab1 };
+ checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1);
+ }
+
+ /**
+ * Test restoring out of order with the following actions/expected states:
+ * Action Model List Close List Comprehensive List
+ * 1. Initial State [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 2. CloseTab(1, allow undo) [ 0 2 3s ] [ 1 ] [ 0 1 2 3s ]
+ * 3. CloseTab(3, allow undo) [ 0 2s ] [ 3 1 ] [ 0 1 2s 3 ]
+ * 4. CancelClose(1) [ 0 1s 2 ] [ 3 ] [ 0 1s 2 3 ]
+ * 5. CloseTab(2, allow undo) [ 0 1s ] [ 2 3 ] [ 0 1s 2 3 ]
+ * 6. CloseTab(0, allow undo) [ 1s ] [ 0 2 3 ] [ 0 1s 2 3 ]
+ * 7. CommitClose(0) [ 1s ] [ 2 3 ] [ 1s 2 3 ]
+ * 8. CancelClose(3) [ 1 3s ] [ 2 ] [ 1 2 3s ]
+ * 9. CloseTab(1, allow undo) [ 3s ] [ 1 2 ] [ 1 2 3s ]
+ * 10. CommitClose(2) [ 3s ] [ 1 ] [ 1 3s ]
+ * 11. CancelClose(1) [ 1s 3 ] - [ 1s 3 ]
+ * 12. CloseTab(3, allow undo) [ 1s ] [ 3 ] [ 1s 3 ]
+ * 13. CloseTab(1, allow undo) - [ 1 3 ] [ 1s 3 ]
+ * 14. CommitAll - - -
+ *
+ * @throws InterruptedException
+ */
+ @MediumTest
+ public void testOutOfOrder2() throws InterruptedException {
+ TabModel model = getActivity().getTabModelSelector().getModel(false);
+ ChromeTabCreator tabCreator = getActivity().getTabCreator(false);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+
+ Tab tab0 = model.getTabAt(0);
+ Tab tab1 = model.getTabAt(1);
+ Tab tab2 = model.getTabAt(2);
+ Tab tab3 = model.getTabAt(3);
+
+ Tab[] fullList = new Tab[] { tab0, tab1, tab2, tab3 };
+
+ // 1.
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 2.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab0, tab2, tab3 }, tab3, new Tab[] { tab1 },
+ fullList, tab3);
+
+ // 3.
+ closeTabOnUiThread(model, tab3, true);
+ checkState(model, new Tab[] { tab0, tab2 }, tab2, new Tab[] { tab3, tab1 },
+ fullList, tab2);
+
+ // 4.
+ cancelTabClosureOnUiThread(model, tab1);
+ checkState(model, new Tab[] { tab0, tab1, tab2 }, tab1, new Tab[] { tab3 },
+ fullList, tab1);
+
+ // 5.
+ closeTabOnUiThread(model, tab2, true);
+ checkState(model, new Tab[] { tab0, tab1 }, tab1, new Tab[] { tab2, tab3 },
+ fullList, tab1);
+
+ // 6.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab1 }, tab1, new Tab[] { tab0, tab2, tab3 },
+ fullList, tab1);
+
+ // 7.
+ commitTabClosureOnUiThread(model, tab0);
+ fullList = new Tab[] { tab1, tab2, tab3 };
+ checkState(model, new Tab[] { tab1 }, tab1, new Tab[] { tab2, tab3 }, fullList,
+ tab1);
+
+ // 8.
+ cancelTabClosureOnUiThread(model, tab3);
+ checkState(model, new Tab[] { tab1, tab3 }, tab3, new Tab[] { tab2 }, fullList,
+ tab3);
+
+ // 9.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab3 }, tab3, new Tab[] { tab1, tab2 }, fullList,
+ tab3);
+
+ // 10.
+ commitTabClosureOnUiThread(model, tab2);
+ fullList = new Tab[] { tab1, tab3 };
+ checkState(model, new Tab[] { tab3 }, tab3, new Tab[] { tab1 }, fullList, tab3);
+
+ // 11.
+ cancelTabClosureOnUiThread(model, tab1);
+ checkState(model, new Tab[] { tab1, tab3 }, tab1, EMPTY, fullList, tab1);
+
+ // 12.
+ closeTabOnUiThread(model, tab3, true);
+ checkState(model, new Tab[] { tab1 }, tab1, new Tab[] { tab3 }, fullList, tab1);
+
+ // 13.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, EMPTY, null, new Tab[] { tab1, tab3 }, fullList, tab1);
+
+ // 14.
+ commitAllTabClosuresOnUiThread(model, new Tab[] { tab1, tab3 });
+ checkState(model, EMPTY, null, EMPTY, EMPTY, null);
+ }
+
+ /**
+ * Test undo {@link TabModel#closeAllTabs()} with the following actions/expected states:
+ * Action Model List Close List Comprehensive List
+ * 1. Initial State [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 2. CloseTab(1, allow undo) [ 0 2 3s ] [ 1 ] [ 0 1 2 3s ]
+ * 3. CloseTab(2, allow undo) [ 0 3s ] [ 2 1 ] [ 0 1 2 3s ]
+ * 4. CloseAll - [ 0 3 2 1 ] [ 0s 1 2 3 ]
+ * 5. CancelAllClose [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 6. CloseAll - [ 0 1 2 3 ] [ 0s 1 2 3 ]
+ * 7. CommitAllClose - - -
+ * 8. CreateTab(0) [ 0s ] - [ 0s ]
+ * 9. CloseAll - [ 0 ] [ 0s ]
+ *
+ * @throws InterruptedException
+ */
+ @MediumTest
+ public void testCloseAll() throws InterruptedException {
+ TabModel model = getActivity().getTabModelSelector().getModel(false);
+ ChromeTabCreator tabCreator = getActivity().getTabCreator(false);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+
+ Tab tab0 = model.getTabAt(0);
+ Tab tab1 = model.getTabAt(1);
+ Tab tab2 = model.getTabAt(2);
+ Tab tab3 = model.getTabAt(3);
+
+ Tab[] fullList = new Tab[] { tab0, tab1, tab2, tab3 };
+
+ // 1.
+ checkState(model, fullList, tab3, EMPTY, fullList, tab3);
+
+ // 2.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab0, tab2, tab3 }, tab3, new Tab[] { tab1 }, fullList, tab3);
+
+ // 3.
+ closeTabOnUiThread(model, tab2, true);
+ checkState(model, new Tab[] { tab0, tab3 }, tab3, new Tab[] { tab1, tab2 }, fullList, tab3);
+
+ // 4.
+ closeAllTabsOnUiThread(model);
+ checkState(model, EMPTY, null, fullList, fullList, tab0);
+
+ // 5.
+ cancelAllTabClosuresOnUiThread(model, fullList);
+ checkState(model, fullList, tab3, EMPTY, fullList, tab3);
+
+ // 6.
+ closeAllTabsOnUiThread(model);
+ checkState(model, EMPTY, null, fullList, fullList, tab0);
+
+ // 7.
+ commitAllTabClosuresOnUiThread(model, fullList);
+ checkState(model, EMPTY, null, EMPTY, EMPTY, null);
+ assertTrue(tab0.isClosing());
+ assertTrue(tab1.isClosing());
+ assertTrue(tab2.isClosing());
+ assertTrue(tab3.isClosing());
+ assertFalse(tab0.isInitialized());
+ assertFalse(tab1.isInitialized());
+ assertFalse(tab2.isInitialized());
+ assertFalse(tab3.isInitialized());
+
+ // 8.
+ createTabOnUiThread(tabCreator);
+ tab0 = model.getTabAt(0);
+ fullList = new Tab[] { tab0 };
+ checkState(model, new Tab[] { tab0 }, tab0, EMPTY, fullList, tab0);
+
+ // 9.
+ closeAllTabsOnUiThread(model);
+ checkState(model, EMPTY, null, fullList, fullList, tab0);
+ assertTrue(tab0.isClosing());
+ assertTrue(tab0.isInitialized());
+ }
+
+ /**
+ * Test {@link TabModel#closeTab(Tab)} when not allowing a close commits all pending
+ * closes:
+ * Action Model List Close List Comprehensive List
+ * 1. Initial State [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 2. CloseTab(1, allow undo) [ 0 2 3s ] [ 1 ] [ 0 1 2 3s ]
+ * 3. CloseTab(2, allow undo) [ 0 3s ] [ 2 1 ] [ 0 1 2 3s ]
+ * 4. CloseTab(3, disallow undo) [ 0s ] - [ 0s ]
+ *
+ * @throws InterruptedException
+ */
+ @MediumTest
+ public void testCloseTab() throws InterruptedException {
+ TabModel model = getActivity().getTabModelSelector().getModel(false);
+ ChromeTabCreator tabCreator = getActivity().getTabCreator(false);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+
+ Tab tab0 = model.getTabAt(0);
+ Tab tab1 = model.getTabAt(1);
+ Tab tab2 = model.getTabAt(2);
+ Tab tab3 = model.getTabAt(3);
+
+ Tab[] fullList = new Tab[] { tab0, tab1, tab2, tab3 };
+
+ // 1.
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 2.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab0, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 3.
+ closeTabOnUiThread(model, tab2, true);
+ checkState(model, new Tab[] { tab0, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 4.
+ closeTabOnUiThread(model, tab3, false);
+ fullList = new Tab[] { tab0 };
+ checkState(model, new Tab[] { tab0 }, tab0, EMPTY, fullList, tab0);
+ assertTrue(tab1.isClosing());
+ assertTrue(tab2.isClosing());
+ assertFalse(tab1.isInitialized());
+ assertFalse(tab2.isInitialized());
+ }
+
+ /**
+ * Test {@link TabModel#moveTab(int, int)} commits all pending closes:
+ * Action Model List Close List Comprehensive List
+ * 1. Initial State [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 2. CloseTab(1, allow undo) [ 0 2 3s ] [ 1 ] [ 0 1 2 3s ]
+ * 3. CloseTab(2, allow undo) [ 0 3s ] [ 2 1 ] [ 0 1 2 3s ]
+ * 4. MoveTab(0, 2) [ 3s 0 ] - [ 3s 0 ]
+ *
+ * @throws InterruptedException
+ */
+ @MediumTest
+ public void testMoveTab() throws InterruptedException {
+ TabModel model = getActivity().getTabModelSelector().getModel(false);
+ ChromeTabCreator tabCreator = getActivity().getTabCreator(false);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+
+ Tab tab0 = model.getTabAt(0);
+ Tab tab1 = model.getTabAt(1);
+ Tab tab2 = model.getTabAt(2);
+ Tab tab3 = model.getTabAt(3);
+
+ Tab[] fullList = new Tab[] { tab0, tab1, tab2, tab3 };
+
+ // 1.
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 2.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab0, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 3.
+ closeTabOnUiThread(model, tab2, true);
+ checkState(model, new Tab[] { tab0, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 4.
+ moveTabOnUiThread(model, tab0, 2);
+ fullList = new Tab[] { tab3, tab0 };
+ checkState(model, new Tab[] { tab3, tab0 }, tab3, EMPTY, fullList, tab3);
+ assertTrue(tab1.isClosing());
+ assertTrue(tab2.isClosing());
+ assertFalse(tab1.isInitialized());
+ assertFalse(tab1.isInitialized());
+ }
+
+ /**
+ * Test adding a {@link Tab} to a {@link TabModel} commits all pending closes:
+ * Action Model List Close List Comprehensive List
+ * 1. Initial State [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 2. CloseTab(1, allow undo) [ 0 2 3s ] [ 1 ] [ 0 1 2 3s ]
+ * 3. CloseTab(2, allow undo) [ 0 3s ] [ 2 1 ] [ 0 1 2 3s ]
+ * 4. CreateTab(4) [ 0 3 4s ] - [ 0 3 4s ]
+ * 5. CloseTab(0, allow undo) [ 3 4s ] [ 0 ] [ 0 3 4s ]
+ * 6. CloseTab(3, allow undo) [ 4s ] [ 3 0 ] [ 0 3 4s ]
+ * 7. CloseTab(4, allow undo) - [ 4 3 0 ] [ 0s 3 4 ]
+ * 8. CreateTab(5) [ 5s ] - [ 5s ]
+ * @throws InterruptedException
+ */
+ @MediumTest
+ public void testAddTab() throws InterruptedException {
+ TabModel model = getActivity().getTabModelSelector().getModel(false);
+ ChromeTabCreator tabCreator = getActivity().getTabCreator(false);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+
+ Tab tab0 = model.getTabAt(0);
+ Tab tab1 = model.getTabAt(1);
+ Tab tab2 = model.getTabAt(2);
+ Tab tab3 = model.getTabAt(3);
+
+ Tab[] fullList = new Tab[] { tab0, tab1, tab2, tab3 };
+
+ // 1.
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 2.
+ closeTabOnUiThread(model, tab1, true);
+ checkState(model, new Tab[] { tab0, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 3.
+ closeTabOnUiThread(model, tab2, true);
+ checkState(model, new Tab[] { tab0, tab3 }, tab3, EMPTY, fullList, tab3);
+
+ // 4.
+ createTabOnUiThread(tabCreator);
+ Tab tab4 = model.getTabAt(2);
+ fullList = new Tab[] { tab0, tab3, tab4 };
+ checkState(model, new Tab[] { tab0, tab3, tab4 }, tab4, EMPTY, fullList, tab4);
+ assertTrue(tab1.isClosing());
+ assertTrue(tab2.isClosing());
+ assertFalse(tab1.isInitialized());
+ assertFalse(tab2.isInitialized());
+
+ // 5.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab3, tab4 }, tab4, new Tab[] { tab0 }, fullList,
+ tab4);
+
+ // 6.
+ closeTabOnUiThread(model, tab3, true);
+ checkState(model, new Tab[] { tab4 }, tab4, new Tab[] { tab3, tab0 }, fullList,
+ tab4);
+
+ // 7.
+ closeTabOnUiThread(model, tab4, true);
+ checkState(model, EMPTY, null, new Tab[] { tab4, tab3, tab0 }, fullList, tab0);
+
+ // 8.
+ createTabOnUiThread(tabCreator);
+ Tab tab5 = model.getTabAt(0);
+ fullList = new Tab[] { tab5 };
+ checkState(model, new Tab[] { tab5 }, tab5, EMPTY, fullList, tab5);
+ assertTrue(tab0.isClosing());
+ assertTrue(tab3.isClosing());
+ assertTrue(tab4.isClosing());
+ assertFalse(tab0.isInitialized());
+ assertFalse(tab3.isInitialized());
+ assertFalse(tab4.isInitialized());
+ }
+
+ /**
+ * Test a {@link TabModel} where undo is not supported:
+ * Action Model List Close List Comprehensive List
+ * 1. Initial State [ 0 1 2 3s ] - [ 0 1 2 3s ]
+ * 2. CloseTab(1, allow undo) [ 0 2 3s ] - [ 0 2 3s ]
+ * 3. CloseAll - - -
+ *
+ * @throws InterruptedException
+ */
+ @MediumTest
+ public void testUndoNotSupported() throws InterruptedException {
+ TabModel model = getActivity().getTabModelSelector().getModel(true);
+ ChromeTabCreator tabCreator = getActivity().getTabCreator(true);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+ createTabOnUiThread(tabCreator);
+
+ Tab tab0 = model.getTabAt(0);
+ Tab tab1 = model.getTabAt(1);
+ Tab tab2 = model.getTabAt(2);
+ Tab tab3 = model.getTabAt(3);
+
+ Tab[] fullList = new Tab[] { tab0, tab1, tab2, tab3 };
+
+ // 1.
+ checkState(model, new Tab[] { tab0, tab1, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+ assertFalse(model.supportsPendingClosures());
+
+ // 2.
+ closeTabOnUiThread(model, tab1, true);
+ fullList = new Tab[] { tab0, tab2, tab3 };
+ checkState(model, new Tab[] { tab0, tab2, tab3 }, tab3, EMPTY, fullList, tab3);
+ assertTrue(tab1.isClosing());
+ assertFalse(tab1.isInitialized());
+
+ // 3.
+ closeAllTabsOnUiThread(model);
+ checkState(model, EMPTY, null, EMPTY, EMPTY, null);
+ assertTrue(tab0.isClosing());
+ assertTrue(tab2.isClosing());
+ assertTrue(tab3.isClosing());
+ assertFalse(tab0.isInitialized());
+ assertFalse(tab2.isInitialized());
+ assertFalse(tab3.isInitialized());
+ }
+
+ /**
+ * Test calling {@link TabModelSelectorImpl#saveState()} commits all pending closures:
+ * Action Model List Close List Comprehensive List
+ * 1. Initial State [ 0 1s ] - [ 0 1s ]
+ * 2. CloseTab(0, allow undo) [ 1s ] [ 0 ] [ 0 1s ]
+ * 3. SaveState [ 1s ] - [ 1s ]
+ */
+ @MediumTest
+ public void testSaveStateCommitsUndos() throws InterruptedException {
+ TabModelSelector selector = getActivity().getTabModelSelector();
+ TabModel model = selector.getModel(false);
+ ChromeTabCreator tabCreator = getActivity().getTabCreator(false);
+ createTabOnUiThread(tabCreator);
+
+ Tab tab0 = model.getTabAt(0);
+ Tab tab1 = model.getTabAt(1);
+
+ Tab[] fullList = new Tab[] { tab0, tab1 };
+
+ // 1.
+ checkState(model, new Tab[] { tab0, tab1 }, tab1, EMPTY, fullList, tab1);
+
+ // 2.
+ closeTabOnUiThread(model, tab0, true);
+ checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1);
+
+ // 3.
+ saveStateOnUiThread(selector);
+ fullList = new Tab[] { tab1 };
+ checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1);
+ assertTrue(tab0.isClosing());
+ assertFalse(tab0.isInitialized());
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698