| // 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.pseudotab; |
| |
| import static org.mockito.ArgumentMatchers.eq; |
| 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.Assert; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.TestRule; |
| import org.junit.runner.RunWith; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Captor; |
| import org.mockito.Mock; |
| import org.mockito.Mockito; |
| import org.mockito.MockitoAnnotations; |
| import org.robolectric.annotation.Config; |
| |
| import org.chromium.base.metrics.RecordHistogram; |
| import org.chromium.base.test.BaseRobolectricTestRunner; |
| import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory; |
| import org.chromium.chrome.browser.tab.TabImpl; |
| import org.chromium.chrome.browser.tab.TabObserver; |
| import org.chromium.chrome.browser.tabmodel.TabModel; |
| import org.chromium.chrome.browser.tabmodel.TabModelFilter; |
| import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider; |
| import org.chromium.chrome.browser.tabmodel.TabModelObserver; |
| import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl; |
| import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver; |
| import org.chromium.chrome.browser.tasks.pseudotab.TabAttributeCache.LastSearchTermProvider; |
| import org.chromium.chrome.test.util.browser.Features; |
| import org.chromium.components.search_engines.TemplateUrlService; |
| import org.chromium.content_public.browser.NavigationController; |
| import org.chromium.content_public.browser.NavigationEntry; |
| import org.chromium.content_public.browser.NavigationHandle; |
| import org.chromium.content_public.browser.NavigationHistory; |
| import org.chromium.content_public.browser.WebContents; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Unit tests for {@link TabAttributeCache}. |
| */ |
| @SuppressWarnings("ResultOfMethodCallIgnored") |
| @RunWith(BaseRobolectricTestRunner.class) |
| @Config(manifest = Config.NONE) |
| public class TabAttributeCacheUnitTest { |
| @Rule |
| public TestRule mProcessor = new Features.JUnitProcessor(); |
| |
| private static final int TAB1_ID = 456; |
| private static final int TAB2_ID = 789; |
| private static final int POSITION1 = 0; |
| private static final int POSITION2 = 1; |
| |
| @Mock |
| TabModelSelectorImpl mTabModelSelector; |
| @Mock |
| TabModelFilterProvider mTabModelFilterProvider; |
| @Mock |
| TabModelFilter mTabModelFilter; |
| @Mock |
| TabModel mTabModel; |
| @Captor |
| ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor; |
| @Captor |
| ArgumentCaptor<TabModelSelectorObserver> mTabModelSelectorObserverCaptor; |
| @Captor |
| ArgumentCaptor<TabObserver> mTabObserverCaptor; |
| |
| private TabImpl mTab1; |
| private TabImpl mTab2; |
| private TabAttributeCache mCache; |
| |
| @Before |
| public void setUp() { |
| RecordHistogram.setDisabledForTests(true); |
| |
| MockitoAnnotations.initMocks(this); |
| |
| mTab1 = prepareTab(TAB1_ID); |
| mTab2 = prepareTab(TAB2_ID); |
| |
| List<TabModel> tabModelList = new ArrayList<>(); |
| tabModelList.add(mTabModel); |
| |
| doReturn(tabModelList).when(mTabModelSelector).getModels(); |
| doReturn(mTabModelFilterProvider).when(mTabModelSelector).getTabModelFilterProvider(); |
| doReturn(mTabModelFilter).when(mTabModelFilterProvider).getTabModelFilter(eq(false)); |
| |
| doNothing().when(mTabModelFilter).addObserver(mTabModelObserverCaptor.capture()); |
| |
| doNothing().when(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture()); |
| |
| doReturn(mTab1).when(mTabModel).getTabAt(POSITION1); |
| doReturn(mTab2).when(mTabModel).getTabAt(POSITION2); |
| doReturn(POSITION1).when(mTabModel).indexOf(mTab1); |
| doReturn(POSITION2).when(mTabModel).indexOf(mTab2); |
| doNothing().when(mTab1).addObserver(mTabObserverCaptor.capture()); |
| doNothing().when(mTab2).addObserver(mTabObserverCaptor.capture()); |
| doReturn(0).when(mTabModel).index(); |
| doReturn(2).when(mTabModel).getCount(); |
| doReturn(mTabModel).when(mTabModel).getComprehensiveModel(); |
| |
| mCache = new TabAttributeCache(mTabModelSelector); |
| } |
| |
| @After |
| public void tearDown() { |
| RecordHistogram.setDisabledForTests(false); |
| mCache.destroy(); |
| TabAttributeCache.setLastSearchTermMockForTesting(null); |
| TabAttributeCache.clearAllForTesting(); |
| } |
| |
| @Test |
| public void updateUrl() { |
| String url = "url 1"; |
| doReturn(url).when(mTab1).getUrlString(); |
| |
| Assert.assertNotEquals(url, TabAttributeCache.getUrl(TAB1_ID)); |
| |
| mTabObserverCaptor.getValue().onUrlUpdated(mTab1); |
| Assert.assertEquals(url, TabAttributeCache.getUrl(TAB1_ID)); |
| |
| mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized(); |
| mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1); |
| Assert.assertNotEquals(url, TabAttributeCache.getUrl(TAB1_ID)); |
| } |
| |
| @Test |
| public void updateUrl_incognito() { |
| String url = "url 1"; |
| doReturn(url).when(mTab1).getUrlString(); |
| doReturn(true).when(mTab1).isIncognito(); |
| |
| mTabObserverCaptor.getValue().onUrlUpdated(mTab1); |
| Assert.assertNotEquals(url, TabAttributeCache.getUrl(TAB1_ID)); |
| } |
| |
| @Test |
| public void updateTitle() { |
| String title = "title 1"; |
| doReturn(title).when(mTab1).getTitle(); |
| |
| Assert.assertNotEquals(title, TabAttributeCache.getTitle(TAB1_ID)); |
| |
| mTabObserverCaptor.getValue().onTitleUpdated(mTab1); |
| Assert.assertEquals(title, TabAttributeCache.getTitle(TAB1_ID)); |
| |
| mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized(); |
| mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1); |
| Assert.assertNotEquals(title, TabAttributeCache.getTitle(TAB1_ID)); |
| } |
| |
| @Test |
| public void updateTitle_incognito() { |
| String title = "title 1"; |
| doReturn(title).when(mTab1).getTitle(); |
| doReturn(true).when(mTab1).isIncognito(); |
| |
| mTabObserverCaptor.getValue().onTitleUpdated(mTab1); |
| Assert.assertNotEquals(title, TabAttributeCache.getTitle(TAB1_ID)); |
| } |
| |
| @Test |
| public void updateRootId() { |
| int rootId = 1337; |
| doReturn(rootId).when(mTab1).getRootId(); |
| |
| Assert.assertNotEquals(rootId, TabAttributeCache.getRootId(TAB1_ID)); |
| |
| mTabObserverCaptor.getValue().onRootIdChanged(mTab1, rootId); |
| Assert.assertEquals(rootId, TabAttributeCache.getRootId(TAB1_ID)); |
| |
| mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized(); |
| mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1); |
| Assert.assertNotEquals(rootId, TabAttributeCache.getRootId(TAB1_ID)); |
| } |
| |
| @Test |
| public void updateRootId_incognito() { |
| int rootId = 1337; |
| doReturn(rootId).when(mTab1).getRootId(); |
| doReturn(true).when(mTab1).isIncognito(); |
| |
| mTabObserverCaptor.getValue().onRootIdChanged(mTab1, rootId); |
| Assert.assertNotEquals(rootId, TabAttributeCache.getRootId(TAB1_ID)); |
| } |
| |
| @Test |
| public void updateLastSearchTerm() { |
| String searchTerm = "chromium"; |
| |
| LastSearchTermProvider lastSearchTermProvider = mock(LastSearchTermProvider.class); |
| TabAttributeCache.setLastSearchTermMockForTesting(lastSearchTermProvider); |
| NavigationHandle navigationHandle = mock(NavigationHandle.class); |
| doReturn(true).when(navigationHandle).isInMainFrame(); |
| |
| Assert.assertNull(TabAttributeCache.getLastSearchTerm(TAB1_ID)); |
| |
| doReturn(searchTerm).when(lastSearchTermProvider).getLastSearchTerm(mTab1); |
| WebContents webContents = mock(WebContents.class); |
| doReturn(webContents).when(mTab1).getWebContents(); |
| |
| mTabObserverCaptor.getValue().onDidFinishNavigation(mTab1, navigationHandle); |
| Assert.assertEquals(searchTerm, TabAttributeCache.getLastSearchTerm(TAB1_ID)); |
| |
| // Non-main frame should not propagate. |
| doReturn("another").when(lastSearchTermProvider).getLastSearchTerm(mTab1); |
| doReturn(false).when(navigationHandle).isInMainFrame(); |
| mTabObserverCaptor.getValue().onDidFinishNavigation(mTab1, navigationHandle); |
| Assert.assertEquals(searchTerm, TabAttributeCache.getLastSearchTerm(TAB1_ID)); |
| doReturn(true).when(navigationHandle).isInMainFrame(); |
| |
| // Empty strings should propagate. |
| doReturn("").when(lastSearchTermProvider).getLastSearchTerm(mTab1); |
| mTabObserverCaptor.getValue().onDidFinishNavigation(mTab1, navigationHandle); |
| Assert.assertEquals("", TabAttributeCache.getLastSearchTerm(TAB1_ID)); |
| |
| // Null should also propagate. |
| doReturn(null).when(lastSearchTermProvider).getLastSearchTerm(mTab1); |
| mTabObserverCaptor.getValue().onDidFinishNavigation(mTab1, navigationHandle); |
| Assert.assertNull(TabAttributeCache.getLastSearchTerm(TAB1_ID)); |
| |
| mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized(); |
| mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1); |
| Assert.assertNull(TabAttributeCache.getLastSearchTerm(TAB1_ID)); |
| } |
| |
| @Test |
| public void updateLastSearchTerm_incognito() { |
| String searchTerm = "chromium"; |
| doReturn(true).when(mTab1).isIncognito(); |
| |
| LastSearchTermProvider lastSearchTermProvider = mock(LastSearchTermProvider.class); |
| TabAttributeCache.setLastSearchTermMockForTesting(lastSearchTermProvider); |
| doReturn(searchTerm).when(lastSearchTermProvider).getLastSearchTerm(mTab1); |
| |
| mTabObserverCaptor.getValue().onDidFinishNavigation(mTab1, null); |
| Assert.assertNull(TabAttributeCache.getLastSearchTerm(TAB1_ID)); |
| } |
| |
| @Test |
| public void findLastSearchTerm() { |
| String otherUrl = "https://example.com"; |
| String searchUrl = "https://www.google.com/search?q=test"; |
| String searchTerm = "test"; |
| String searchUrl2 = "https://www.google.com/search?q=query"; |
| String searchTerm2 = "query"; |
| |
| TemplateUrlService service = Mockito.mock(TemplateUrlService.class); |
| doReturn(null).when(service).getSearchQueryForUrl(otherUrl); |
| doReturn(searchTerm).when(service).getSearchQueryForUrl(searchUrl); |
| doReturn(searchTerm2).when(service).getSearchQueryForUrl(searchUrl2); |
| TemplateUrlServiceFactory.setInstanceForTesting(service); |
| |
| WebContents webContents = mock(WebContents.class); |
| doReturn(webContents).when(mTab1).getWebContents(); |
| NavigationController navigationController = mock(NavigationController.class); |
| doReturn(navigationController).when(webContents).getNavigationController(); |
| NavigationHistory navigationHistory = mock(NavigationHistory.class); |
| doReturn(navigationHistory).when(navigationController).getNavigationHistory(); |
| doReturn(2).when(navigationHistory).getCurrentEntryIndex(); |
| NavigationEntry navigationEntry1 = mock(NavigationEntry.class); |
| NavigationEntry navigationEntry0 = mock(NavigationEntry.class); |
| doReturn(navigationEntry1).when(navigationHistory).getEntryAtIndex(1); |
| doReturn(navigationEntry0).when(navigationHistory).getEntryAtIndex(0); |
| doReturn(otherUrl).when(mTab1).getUrlString(); |
| |
| // No searches. |
| doReturn(otherUrl).when(navigationEntry1).getOriginalUrl(); |
| doReturn(otherUrl).when(navigationEntry0).getOriginalUrl(); |
| Assert.assertNull(TabAttributeCache.findLastSearchTerm(mTab1)); |
| |
| // Has SRP. |
| doReturn(searchUrl).when(navigationEntry1).getOriginalUrl(); |
| doReturn(otherUrl).when(navigationEntry0).getOriginalUrl(); |
| Assert.assertEquals(searchTerm, TabAttributeCache.findLastSearchTerm(mTab1)); |
| |
| // Has earlier SRP. |
| doReturn(otherUrl).when(navigationEntry1).getOriginalUrl(); |
| doReturn(searchUrl2).when(navigationEntry0).getOriginalUrl(); |
| Assert.assertEquals(searchTerm2, TabAttributeCache.findLastSearchTerm(mTab1)); |
| |
| // Latest one wins. |
| doReturn(searchUrl).when(navigationEntry1).getOriginalUrl(); |
| doReturn(searchUrl2).when(navigationEntry0).getOriginalUrl(); |
| Assert.assertEquals(searchTerm, TabAttributeCache.findLastSearchTerm(mTab1)); |
| |
| // Only care about previous ones. |
| doReturn(1).when(navigationHistory).getCurrentEntryIndex(); |
| Assert.assertEquals(searchTerm2, TabAttributeCache.findLastSearchTerm(mTab1)); |
| |
| // Skip if the SRP is showing. |
| doReturn(2).when(navigationHistory).getCurrentEntryIndex(); |
| doReturn(searchUrl).when(mTab1).getUrlString(); |
| Assert.assertNull(TabAttributeCache.findLastSearchTerm(mTab1)); |
| |
| // Reset current SRP. |
| doReturn(otherUrl).when(mTab1).getUrlString(); |
| Assert.assertEquals(searchTerm, TabAttributeCache.findLastSearchTerm(mTab1)); |
| |
| verify(navigationHistory, never()).getEntryAtIndex(eq(2)); |
| } |
| |
| @Test |
| public void removeEscapedCodePoints() { |
| Assert.assertEquals("", TabAttributeCache.removeEscapedCodePoints("")); |
| Assert.assertEquals("", TabAttributeCache.removeEscapedCodePoints("%0a")); |
| Assert.assertEquals("", TabAttributeCache.removeEscapedCodePoints("%0A")); |
| Assert.assertEquals("AB", TabAttributeCache.removeEscapedCodePoints("A%FE%FFB")); |
| Assert.assertEquals("a%0", TabAttributeCache.removeEscapedCodePoints("a%0")); |
| Assert.assertEquals("%", TabAttributeCache.removeEscapedCodePoints("%%00")); |
| Assert.assertEquals("%0G", TabAttributeCache.removeEscapedCodePoints("%0G")); |
| Assert.assertEquals("abcc", TabAttributeCache.removeEscapedCodePoints("abc%abc")); |
| Assert.assertEquals("a%a", TabAttributeCache.removeEscapedCodePoints("a%bc%a%bc")); |
| } |
| |
| @Test |
| public void onTabStateInitialized() { |
| String url1 = "url 1"; |
| doReturn(url1).when(mTab1).getUrlString(); |
| String title1 = "title 1"; |
| doReturn(title1).when(mTab1).getTitle(); |
| int rootId1 = 1337; |
| doReturn(rootId1).when(mTab1).getRootId(); |
| |
| String url2 = "url 2"; |
| doReturn(url2).when(mTab2).getUrlString(); |
| String title2 = "title 2"; |
| doReturn(title2).when(mTab2).getTitle(); |
| int rootId2 = 42; |
| doReturn(rootId2).when(mTab2).getRootId(); |
| |
| String searchTerm = "chromium"; |
| LastSearchTermProvider lastSearchTermProvider = mock(LastSearchTermProvider.class); |
| TabAttributeCache.setLastSearchTermMockForTesting(lastSearchTermProvider); |
| doReturn(searchTerm).when(lastSearchTermProvider).getLastSearchTerm(mTab1); |
| WebContents webContents = mock(WebContents.class); |
| doReturn(webContents).when(mTab1).getWebContents(); |
| |
| doReturn(mTab1).when(mTabModelFilter).getTabAt(0); |
| doReturn(mTab2).when(mTabModelFilter).getTabAt(1); |
| doReturn(2).when(mTabModelFilter).getCount(); |
| doReturn(mTab1).when(mTabModelSelector).getCurrentTab(); |
| |
| Assert.assertNotEquals(url1, TabAttributeCache.getUrl(TAB1_ID)); |
| Assert.assertNotEquals(title1, TabAttributeCache.getTitle(TAB1_ID)); |
| Assert.assertNotEquals(rootId1, TabAttributeCache.getRootId(TAB1_ID)); |
| Assert.assertNotEquals(searchTerm, TabAttributeCache.getLastSearchTerm(TAB1_ID)); |
| |
| Assert.assertNotEquals(url2, TabAttributeCache.getUrl(TAB2_ID)); |
| Assert.assertNotEquals(title2, TabAttributeCache.getTitle(TAB2_ID)); |
| Assert.assertNotEquals(rootId2, TabAttributeCache.getRootId(TAB2_ID)); |
| |
| mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized(); |
| |
| Assert.assertEquals(url1, TabAttributeCache.getUrl(TAB1_ID)); |
| Assert.assertEquals(title1, TabAttributeCache.getTitle(TAB1_ID)); |
| Assert.assertEquals(rootId1, TabAttributeCache.getRootId(TAB1_ID)); |
| Assert.assertEquals(searchTerm, TabAttributeCache.getLastSearchTerm(TAB1_ID)); |
| |
| Assert.assertEquals(url2, TabAttributeCache.getUrl(TAB2_ID)); |
| Assert.assertEquals(title2, TabAttributeCache.getTitle(TAB2_ID)); |
| Assert.assertEquals(rootId2, TabAttributeCache.getRootId(TAB2_ID)); |
| } |
| |
| private TabImpl prepareTab(int id) { |
| TabImpl tab = mock(TabImpl.class); |
| doReturn(id).when(tab).getId(); |
| return tab; |
| } |
| } |