1 // Copyright 2020 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.tasks;
6 
7 import static org.junit.Assert.assertEquals;
8 import static org.junit.Assert.assertFalse;
9 import static org.junit.Assert.assertNotNull;
10 import static org.junit.Assert.assertTrue;
11 import static org.mockito.ArgumentMatchers.anyLong;
12 import static org.mockito.ArgumentMatchers.eq;
13 import static org.mockito.Mockito.doReturn;
14 import static org.mockito.Mockito.times;
15 import static org.mockito.Mockito.verify;
16 
17 import static org.chromium.chrome.browser.tasks.SingleTabViewProperties.CLICK_LISTENER;
18 import static org.chromium.chrome.browser.tasks.SingleTabViewProperties.FAVICON;
19 import static org.chromium.chrome.browser.tasks.SingleTabViewProperties.TITLE;
20 
21 import android.graphics.drawable.BitmapDrawable;
22 import android.graphics.drawable.Drawable;
23 
24 import org.junit.After;
25 import org.junit.Before;
26 import org.junit.Test;
27 import org.junit.runner.RunWith;
28 import org.mockito.ArgumentCaptor;
29 import org.mockito.Captor;
30 import org.mockito.Mock;
31 import org.mockito.MockitoAnnotations;
32 import org.robolectric.annotation.Config;
33 
34 import org.chromium.base.Callback;
35 import org.chromium.base.test.BaseRobolectricTestRunner;
36 import org.chromium.chrome.browser.tab.Tab;
37 import org.chromium.chrome.browser.tab.TabSelectionType;
38 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
39 import org.chromium.chrome.browser.tabmodel.TabList;
40 import org.chromium.chrome.browser.tabmodel.TabModel;
41 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
42 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
43 import org.chromium.chrome.browser.tasks.tab_management.TabListFaviconProvider;
44 import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher;
45 import org.chromium.ui.modelutil.PropertyModel;
46 
47 /** Tests for {@link SingleTabSwitcherMediator}. */
48 @RunWith(BaseRobolectricTestRunner.class)
49 @Config(manifest = Config.NONE)
50 public class SingleTabSwitcherMediatorUnitTest {
51     private final int mTabId = 1;
52     private final String mTitle = "test";
53     private final String mUrlString = "chrome://test.com";
54     private final int mTabId2 = 2;
55     private final String mTitle2 = "test2";
56     private final String mUrlString2 = "chrome://test2.com";
57     private SingleTabSwitcherMediator mMediator;
58     private PropertyModel mPropertyModel;
59 
60     @Mock
61     private TabModelSelector mTabModelSelector;
62     @Mock
63     private TabModel mNormalTabModel;
64     @Mock
65     private TabModel mIncognitoTabModel;
66     @Mock
67     private Tab mTab;
68     @Mock
69     private Tab mTab2;
70     @Mock
71     private TabListFaviconProvider mTabListFaviconProvider;
72     @Mock
73     private TabSwitcher.OnTabSelectingListener mOnTabSelectingListener;
74     @Mock
75     private TabSwitcher.OverviewModeObserver mOverviewModeObserver;
76     @Captor
77     private ArgumentCaptor<EmptyTabModelSelectorObserver> mTabModelSelectorObserverCaptor;
78     @Captor
79     private ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor;
80     @Captor
81     private ArgumentCaptor<Callback<Drawable>> mFaviconCallbackCaptor;
82 
83     @Before
setUp()84     public void setUp() {
85         MockitoAnnotations.initMocks(this);
86 
87         doReturn(new BitmapDrawable())
88                 .when(mTabListFaviconProvider)
89                 .getDefaultFaviconDrawable(false);
90         doReturn(mNormalTabModel).when(mTabModelSelector).getModel(false);
91         doReturn(mTabId).when(mTabModelSelector).getCurrentTabId();
92         doReturn(false).when(mTabModelSelector).isIncognitoSelected();
93         doReturn(mTab).when(mNormalTabModel).getTabAt(0);
94         doReturn(0).when(mNormalTabModel).index();
95         doReturn(1).when(mNormalTabModel).getCount();
96         doReturn(false).when(mNormalTabModel).isIncognito();
97         doReturn(mUrlString).when(mTab).getUrlString();
98         doReturn(mTabId).when(mTab).getId();
99         doReturn(mTitle).when(mTab).getTitle();
100         doReturn(mUrlString2).when(mTab2).getUrlString();
101         doReturn(mTabId2).when(mTab2).getId();
102         doReturn(mTitle2).when(mTab2).getTitle();
103         doReturn(true).when(mIncognitoTabModel).isIncognito();
104 
105         mPropertyModel = new PropertyModel(SingleTabViewProperties.ALL_KEYS);
106         mMediator = new SingleTabSwitcherMediator(
107                 mPropertyModel, mTabModelSelector, mTabListFaviconProvider);
108     }
109 
110     @After
tearDown()111     public void tearDown() {
112         mMediator = null;
113     }
114 
115     @Test
showAndHide()116     public void showAndHide() {
117         assertNotNull(mPropertyModel.get(FAVICON));
118         assertNotNull(mPropertyModel.get(CLICK_LISTENER));
119         assertFalse(mMediator.overviewVisible());
120         mMediator.setOnTabSelectingListener(mOnTabSelectingListener);
121         mMediator.addOverviewModeObserver(mOverviewModeObserver);
122 
123         mMediator.showOverview(true);
124         verify(mNormalTabModel).addObserver(mTabModelObserverCaptor.capture());
125         verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
126         verify(mTabListFaviconProvider)
127                 .getFaviconForUrlAsync(eq(mUrlString), eq(false), mFaviconCallbackCaptor.capture());
128         assertTrue(mMediator.overviewVisible());
129         verify(mOverviewModeObserver).startedShowing();
130         verify(mOverviewModeObserver).finishedShowing();
131         assertEquals(mPropertyModel.get(TITLE), mTitle);
132 
133         mPropertyModel.get(CLICK_LISTENER).onClick(null);
134         verify(mOnTabSelectingListener).onTabSelecting(anyLong(), eq(mTabId));
135 
136         mMediator.hideOverview(true);
137         assertFalse(mMediator.overviewVisible());
138         assertEquals(mPropertyModel.get(TITLE), "");
139         verify(mOverviewModeObserver).startedHiding();
140         verify(mOverviewModeObserver).finishedHiding();
141 
142         mMediator.removeOverviewModeObserver(mOverviewModeObserver);
143         mMediator.setOnTabSelectingListener(null);
144     }
145 
146     @Test
selectTabAfterSwitchingTabModel()147     public void selectTabAfterSwitchingTabModel() {
148         assertFalse(mMediator.overviewVisible());
149         mMediator.setOnTabSelectingListener(mOnTabSelectingListener);
150         mMediator.addOverviewModeObserver(mOverviewModeObserver);
151 
152         mMediator.showOverview(true);
153         verify(mNormalTabModel).addObserver(mTabModelObserverCaptor.capture());
154         verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
155         verify(mTabListFaviconProvider)
156                 .getFaviconForUrlAsync(eq(mUrlString), eq(false), mFaviconCallbackCaptor.capture());
157         assertTrue(mMediator.overviewVisible());
158         verify(mOverviewModeObserver).startedShowing();
159         verify(mOverviewModeObserver).finishedShowing();
160         assertEquals(mPropertyModel.get(TITLE), mTitle);
161 
162         mTabModelObserverCaptor.getValue().didSelectTab(mTab, TabSelectionType.FROM_USER, -1);
163         verify(mOnTabSelectingListener).onTabSelecting(anyLong(), eq(mTabId));
164 
165         mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(
166                 mIncognitoTabModel, mNormalTabModel);
167         mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(
168                 mNormalTabModel, mIncognitoTabModel);
169 
170         // The next tab selecting event should be ignored.
171         mTabModelObserverCaptor.getValue().didSelectTab(mTab, TabSelectionType.FROM_USER, mTabId);
172         verify(mOnTabSelectingListener, times(1)).onTabSelecting(anyLong(), eq(mTabId));
173 
174         mTabModelObserverCaptor.getValue().didSelectTab(mTab, TabSelectionType.FROM_USER, mTabId);
175         verify(mOnTabSelectingListener, times(2)).onTabSelecting(anyLong(), eq(mTabId));
176 
177         mMediator.hideOverview(true);
178         assertFalse(mMediator.overviewVisible());
179         assertEquals(mPropertyModel.get(TITLE), "");
180         verify(mOverviewModeObserver).startedHiding();
181         verify(mOverviewModeObserver).finishedHiding();
182     }
183 
184     @Test
selectNextTabAfterClosingTheSelectedTab()185     public void selectNextTabAfterClosingTheSelectedTab() {
186         assertFalse(mMediator.overviewVisible());
187         mMediator.setOnTabSelectingListener(mOnTabSelectingListener);
188         mMediator.addOverviewModeObserver(mOverviewModeObserver);
189 
190         mMediator.showOverview(true);
191         verify(mNormalTabModel).addObserver(mTabModelObserverCaptor.capture());
192         verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
193         verify(mTabListFaviconProvider)
194                 .getFaviconForUrlAsync(eq(mUrlString), eq(false), mFaviconCallbackCaptor.capture());
195         assertTrue(mMediator.overviewVisible());
196         verify(mOverviewModeObserver).startedShowing();
197         verify(mOverviewModeObserver).finishedShowing();
198         assertEquals(mPropertyModel.get(TITLE), mTitle);
199 
200         mTabModelObserverCaptor.getValue().didSelectTab(
201                 mTab2, TabSelectionType.FROM_CLOSE, mTabId2);
202         verify(mOnTabSelectingListener, times(0)).onTabSelecting(anyLong(), eq(mTabId2));
203         assertEquals(mPropertyModel.get(TITLE), mTitle2);
204 
205         mMediator.hideOverview(true);
206         assertFalse(mMediator.overviewVisible());
207         assertEquals(mPropertyModel.get(TITLE), "");
208         verify(mOverviewModeObserver).startedHiding();
209         verify(mOverviewModeObserver).finishedHiding();
210     }
211 
212     @Test
selectTabAfterSwitchingTabModelAndReshown()213     public void selectTabAfterSwitchingTabModelAndReshown() {
214         assertFalse(mMediator.overviewVisible());
215         mMediator.setOnTabSelectingListener(mOnTabSelectingListener);
216         mMediator.addOverviewModeObserver(mOverviewModeObserver);
217 
218         mMediator.showOverview(true);
219         verify(mNormalTabModel).addObserver(mTabModelObserverCaptor.capture());
220         verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
221         verify(mTabListFaviconProvider)
222                 .getFaviconForUrlAsync(eq(mUrlString), eq(false), mFaviconCallbackCaptor.capture());
223         assertEquals(mPropertyModel.get(TITLE), mTitle);
224 
225         mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(
226                 mIncognitoTabModel, mNormalTabModel);
227         mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(
228                 mNormalTabModel, mIncognitoTabModel);
229         mMediator.hideOverview(true);
230 
231         // The next tab selecting event should not be ignored after hiding and reshowing.
232         mMediator.showOverview(true);
233         mTabModelObserverCaptor.getValue().didSelectTab(mTab, TabSelectionType.FROM_USER, -1);
234         verify(mOnTabSelectingListener).onTabSelecting(anyLong(), eq(mTabId));
235 
236         mMediator.hideOverview(true);
237     }
238 
239     @Test
onBackPressed()240     public void onBackPressed() {
241         assertFalse(mMediator.overviewVisible());
242         mMediator.setOnTabSelectingListener(mOnTabSelectingListener);
243         mMediator.addOverviewModeObserver(mOverviewModeObserver);
244 
245         assertFalse(mMediator.onBackPressed());
246 
247         mMediator.showOverview(true);
248         assertTrue(mMediator.onBackPressed());
249         verify(mOnTabSelectingListener).onTabSelecting(anyLong(), eq(mTabId));
250 
251         doReturn(TabList.INVALID_TAB_INDEX).when(mTabModelSelector).getCurrentTabId();
252         assertFalse(mMediator.onBackPressed());
253 
254         doReturn(mTabId).when(mTabModelSelector).getCurrentTabId();
255         doReturn(true).when(mTabModelSelector).isIncognitoSelected();
256         assertFalse(mMediator.onBackPressed());
257     }
258 }
259