| // Copyright 2019 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.tasks.tabgroup; |
| |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.doNothing; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.verify; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Captor; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| import org.mockito.invocation.InvocationOnMock; |
| import org.mockito.stubbing.Answer; |
| import org.robolectric.annotation.Config; |
| |
| import org.chromium.base.metrics.RecordHistogram; |
| import org.chromium.base.metrics.RecordUserAction; |
| import org.chromium.chrome.browser.tab.Tab; |
| import org.chromium.chrome.browser.tabmodel.TabLaunchType; |
| import org.chromium.chrome.browser.tabmodel.TabModel; |
| import org.chromium.chrome.browser.tabmodel.TabModelObserver; |
| import org.chromium.chrome.browser.tabmodel.TabModelUtils; |
| import org.chromium.testing.local.LocalRobolectricTestRunner; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| /** |
| * Tests for {@link TabGroupModelFilter}. |
| */ |
| @RunWith(LocalRobolectricTestRunner.class) |
| @Config(manifest = Config.NONE) |
| public class TabGroupModelFilterUnitTest { |
| private static final int TAB1_ID = 456; |
| private static final int TAB2_ID = 789; |
| private static final int TAB3_ID = 123; |
| private static final int TAB4_ID = 147; |
| private static final int TAB5_ID = 258; |
| private static final int TAB6_ID = 369; |
| private static final int TAB1_ROOT_ID = TAB1_ID; |
| private static final int TAB2_ROOT_ID = TAB2_ID; |
| private static final int TAB3_ROOT_ID = TAB2_ID; |
| private static final int TAB4_ROOT_ID = TAB4_ID; |
| private static final int TAB5_ROOT_ID = TAB5_ID; |
| private static final int TAB6_ROOT_ID = TAB5_ID; |
| private static final int POSITION1 = 0; |
| private static final int POSITION2 = 1; |
| private static final int POSITION3 = 2; |
| private static final int POSITION4 = 3; |
| private static final int POSITION5 = 4; |
| private static final int POSITION6 = 5; |
| |
| @Mock |
| TabModel mTabModel; |
| |
| @Mock |
| TabGroupModelFilter.Observer mTabGroupModelFilterObserver; |
| |
| @Captor |
| ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor; |
| |
| private Tab mTab1; |
| private Tab mTab2; |
| private Tab mTab3; |
| private Tab mTab4; |
| private Tab mTab5; |
| private Tab mTab6; |
| private List<Tab> mTabs = new ArrayList<>(); |
| |
| private TabGroupModelFilter mTabGroupModelFilter; |
| |
| private Tab prepareTab(int tabId, int rootId) { |
| Tab tab = mock(Tab.class); |
| |
| doAnswer(new Answer() { |
| @Override |
| public Object answer(InvocationOnMock invocation) throws Throwable { |
| int newRootId = invocation.getArgument(0); |
| doReturn(newRootId).when(tab).getRootId(); |
| return null; |
| } |
| }).when(tab).setRootId(anyInt()); |
| |
| doReturn(tabId).when(tab).getId(); |
| tab.setRootId(rootId); |
| |
| return tab; |
| } |
| |
| private void setRootId(Tab tab, int rootId) { |
| doAnswer(new Answer() { |
| @Override |
| public Object answer(InvocationOnMock invocation) throws Throwable { |
| int newRootId = invocation.getArgument(0); |
| doReturn(newRootId).when(tab).getRootId(); |
| return null; |
| } |
| }).when(tab).setRootId(rootId); |
| } |
| |
| private void setUpTab() { |
| mTab1 = prepareTab(TAB1_ID, TAB1_ROOT_ID); |
| mTab2 = prepareTab(TAB2_ID, TAB2_ROOT_ID); |
| mTab3 = prepareTab(TAB3_ID, TAB3_ROOT_ID); |
| mTab4 = prepareTab(TAB4_ID, TAB4_ROOT_ID); |
| mTab5 = prepareTab(TAB5_ID, TAB5_ROOT_ID); |
| mTab6 = prepareTab(TAB6_ID, TAB6_ROOT_ID); |
| } |
| |
| private void setUpTabModel() { |
| doAnswer(new Answer() { |
| @Override |
| public Object answer(InvocationOnMock invocation) throws Throwable { |
| Tab tab = invocation.getArgument(0); |
| mTabs.add(tab); |
| return null; |
| } |
| }).when(mTabModel).addTab(any(Tab.class), anyInt(), anyInt()); |
| |
| doAnswer(new Answer() { |
| @Override |
| public Object answer(InvocationOnMock invocation) throws Throwable { |
| int movedTabId = invocation.getArgument(0); |
| int newIndex = invocation.getArgument(1); |
| |
| int oldIndex = TabModelUtils.getTabIndexById(mTabModel, movedTabId); |
| Tab tab = TabModelUtils.getTabById(mTabModel, movedTabId); |
| |
| mTabs.remove(tab); |
| if (oldIndex < newIndex) --newIndex; |
| mTabs.add(newIndex, tab); |
| mTabModelObserverCaptor.getValue().didMoveTab(tab, newIndex, oldIndex); |
| return null; |
| } |
| }).when(mTabModel).moveTab(anyInt(), anyInt()); |
| |
| doAnswer(new Answer() { |
| @Override |
| public Tab answer(InvocationOnMock invocation) throws Throwable { |
| int index = invocation.getArgument(0); |
| return mTabs.get(index); |
| } |
| }).when(mTabModel).getTabAt(anyInt()); |
| |
| doAnswer(new Answer() { |
| @Override |
| public Integer answer(InvocationOnMock invocation) throws Throwable { |
| Tab tab = invocation.getArgument(0); |
| return mTabs.indexOf(tab); |
| } |
| }).when(mTabModel).indexOf(any(Tab.class)); |
| |
| doAnswer(new Answer() { |
| @Override |
| public Integer answer(InvocationOnMock invocation) throws Throwable { |
| return mTabs.size(); |
| } |
| }).when(mTabModel).getCount(); |
| |
| doReturn(0).when(mTabModel).index(); |
| } |
| |
| @Before |
| public void setUp() { |
| RecordUserAction.setDisabledForTests(true); |
| RecordHistogram.setDisabledForTests(true); |
| |
| MockitoAnnotations.initMocks(this); |
| |
| setUpTab(); |
| setUpTabModel(); |
| |
| doNothing().when(mTabModel).addObserver(mTabModelObserverCaptor.capture()); |
| |
| mTabGroupModelFilter = new TabGroupModelFilter(mTabModel); |
| mTabGroupModelFilter.addTabGroupObserver(mTabGroupModelFilterObserver); |
| |
| mTabModel.addTab(mTab1, -1, TabLaunchType.FROM_CHROME_UI); |
| mTabModelObserverCaptor.getValue().didAddTab(mTab1, TabLaunchType.FROM_CHROME_UI); |
| |
| mTabModel.addTab(mTab2, -1, TabLaunchType.FROM_CHROME_UI); |
| mTabModelObserverCaptor.getValue().didAddTab(mTab2, TabLaunchType.FROM_CHROME_UI); |
| |
| mTabModel.addTab(mTab3, -1, TabLaunchType.FROM_CHROME_UI); |
| mTabModelObserverCaptor.getValue().didAddTab(mTab3, TabLaunchType.FROM_CHROME_UI); |
| |
| mTabModel.addTab(mTab4, -1, TabLaunchType.FROM_CHROME_UI); |
| mTabModelObserverCaptor.getValue().didAddTab(mTab4, TabLaunchType.FROM_CHROME_UI); |
| |
| mTabModel.addTab(mTab5, -1, TabLaunchType.FROM_CHROME_UI); |
| mTabModelObserverCaptor.getValue().didAddTab(mTab5, TabLaunchType.FROM_CHROME_UI); |
| |
| mTabModel.addTab(mTab6, -1, TabLaunchType.FROM_CHROME_UI); |
| mTabModelObserverCaptor.getValue().didAddTab(mTab6, TabLaunchType.FROM_CHROME_UI); |
| } |
| |
| @After |
| public void tearDown() { |
| RecordUserAction.setDisabledForTests(false); |
| RecordHistogram.setDisabledForTests(false); |
| } |
| |
| @Test |
| public void mergeTabToGroup_No_Update_TabModel() { |
| List<Tab> expectedGroup = new ArrayList<>(Arrays.asList(mTab2, mTab3, mTab4)); |
| |
| mTabGroupModelFilter.mergeTabsToGroup(mTab4.getId(), mTab2.getId()); |
| |
| verify(mTabModel, never()).moveTab(anyInt(), anyInt()); |
| assertArrayEquals(mTabGroupModelFilter.getRelatedTabList(mTab4.getId()).toArray(), |
| expectedGroup.toArray()); |
| } |
| |
| @Test |
| public void mergeTabToGroup_Update_TabModel() { |
| mTabGroupModelFilter.mergeTabsToGroup(mTab5.getId(), mTab2.getId()); |
| verify(mTabModel).moveTab(mTab5.getId(), POSITION3 + 1); |
| } |
| |
| @Test |
| public void mergeOneTabToTab_Forward() { |
| List<Tab> expectedGroup = new ArrayList<>(Arrays.asList(mTab1, mTab4)); |
| List<Tab> expectedTabModel = |
| new ArrayList<>(Arrays.asList(mTab1, mTab4, mTab2, mTab3, mTab5, mTab6)); |
| int startIndex = POSITION1; |
| |
| mTabGroupModelFilter.mergeTabsToGroup(mTab4.getId(), mTab1.getId()); |
| |
| verify(mTabModel).moveTab(mTab4.getId(), ++startIndex); |
| verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab4, mTab1.getId()); |
| assertArrayEquals(mTabGroupModelFilter.getRelatedTabList(mTab4.getId()).toArray(), |
| expectedGroup.toArray()); |
| assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); |
| } |
| |
| @Test |
| public void mergeGroupToTab_Forward() { |
| List<Tab> expectedGroup = new ArrayList<>(Arrays.asList(mTab1, mTab5, mTab6)); |
| List<Tab> expectedTabModel = |
| new ArrayList<>(Arrays.asList(mTab1, mTab5, mTab6, mTab2, mTab3, mTab4)); |
| int startIndex = POSITION1; |
| |
| mTabGroupModelFilter.mergeTabsToGroup(mTab5.getId(), mTab1.getId()); |
| |
| verify(mTabModel).moveTab(mTab5.getId(), ++startIndex); |
| verify(mTabModel).moveTab(mTab6.getId(), ++startIndex); |
| verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab6, mTab1.getId()); |
| assertArrayEquals(mTabGroupModelFilter.getRelatedTabList(mTab5.getId()).toArray(), |
| expectedGroup.toArray()); |
| assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); |
| } |
| |
| @Test |
| public void mergeGroupToGroup_Forward() { |
| List<Tab> expectedGroup = new ArrayList<>(Arrays.asList(mTab2, mTab3, mTab5, mTab6)); |
| List<Tab> expectedTabModel = |
| new ArrayList<>(Arrays.asList(mTab1, mTab2, mTab3, mTab5, mTab6, mTab4)); |
| int startIndex = POSITION3; |
| |
| mTabGroupModelFilter.mergeTabsToGroup(mTab5.getId(), mTab2.getId()); |
| |
| verify(mTabModel).moveTab(mTab5.getId(), ++startIndex); |
| verify(mTabModel).moveTab(mTab6.getId(), ++startIndex); |
| verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab6, mTab2.getId()); |
| assertArrayEquals(mTabGroupModelFilter.getRelatedTabList(mTab5.getId()).toArray(), |
| expectedGroup.toArray()); |
| assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); |
| } |
| |
| @Test |
| public void mergeOneTabToTab_Backward() { |
| List<Tab> expectedGroup = new ArrayList<>(Arrays.asList(mTab4, mTab1)); |
| List<Tab> expectedTabModel = |
| new ArrayList<>(Arrays.asList(mTab2, mTab3, mTab4, mTab1, mTab5, mTab6)); |
| int startIndex = POSITION4; |
| |
| mTabGroupModelFilter.mergeTabsToGroup(mTab1.getId(), mTab4.getId()); |
| |
| verify(mTabModel).moveTab(mTab1.getId(), startIndex + 1); |
| verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab1, mTab4.getId()); |
| assertArrayEquals(mTabGroupModelFilter.getRelatedTabList(mTab1.getId()).toArray(), |
| expectedGroup.toArray()); |
| assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); |
| } |
| |
| @Test |
| public void mergeGroupToTab_Backward() { |
| List<Tab> expectedGroup = new ArrayList<>(Arrays.asList(mTab4, mTab2, mTab3)); |
| List<Tab> expectedTabModel = |
| new ArrayList<>(Arrays.asList(mTab1, mTab4, mTab2, mTab3, mTab5, mTab6)); |
| int startIndex = POSITION4; |
| |
| mTabGroupModelFilter.mergeTabsToGroup(mTab2.getId(), mTab4.getId()); |
| |
| verify(mTabModel).moveTab(mTab2.getId(), startIndex + 1); |
| verify(mTabModel).moveTab(mTab3.getId(), startIndex + 1); |
| verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab3, mTab4.getId()); |
| assertArrayEquals(mTabGroupModelFilter.getRelatedTabList(mTab2.getId()).toArray(), |
| expectedGroup.toArray()); |
| assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); |
| } |
| |
| @Test |
| public void moveGroup_Backward() { |
| List<Tab> expectedTabModel = |
| new ArrayList<>(Arrays.asList(mTab1, mTab4, mTab2, mTab3, mTab5, mTab6)); |
| int startIndex = POSITION4; |
| |
| mTabGroupModelFilter.moveRelatedTabs(mTab2.getId(), startIndex + 1); |
| |
| verify(mTabModel).moveTab(mTab2.getId(), startIndex + 1); |
| verify(mTabModel).moveTab(mTab3.getId(), startIndex + 1); |
| verify(mTabGroupModelFilterObserver).didMoveTabGroup(mTab3, POSITION3 - 1, startIndex); |
| assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); |
| } |
| |
| @Test |
| public void moveGroup_Forward() { |
| List<Tab> expectedTabModel = |
| new ArrayList<>(Arrays.asList(mTab1, mTab2, mTab3, mTab5, mTab6, mTab4)); |
| int startIndex = POSITION3; |
| |
| mTabGroupModelFilter.moveRelatedTabs(mTab5.getId(), startIndex + 1); |
| |
| verify(mTabModel).moveTab(mTab5.getId(), startIndex + 1); |
| verify(mTabModel).moveTab(mTab6.getId(), startIndex + 2); |
| verify(mTabGroupModelFilterObserver).didMoveTabGroup(mTab6, POSITION6, startIndex + 2); |
| assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); |
| } |
| } |