// Copyright 2015 The Chromium Authors
// 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.toolbar;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.params.ParameterAnnotations;
import org.chromium.base.test.params.ParameterProvider;
import org.chromium.base.test.params.ParameterSet;
import org.chromium.base.test.params.ParameterizedRunner;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.UrlUtils;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.dom_distiller.DomDistillerTabUtils;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.omnibox.LocationBarDataProvider;
import org.chromium.chrome.browser.omnibox.NewTabPageDelegate;
import org.chromium.chrome.browser.omnibox.UrlBarData;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.tab.MockTab;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.chrome.browser.toolbar.top.ToolbarLayout;
import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.R;
import org.chromium.chrome.test.util.ChromeTabUtils;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.url.GURL;
import org.chromium.url.JUnitTestGURLs;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** Tests for LocationBarModel. */
@RunWith(ParameterizedRunner.class)
@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class LocationBarModelTest {
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@Before
public void setUp() throws InterruptedException {
mActivityTestRule.startMainActivityOnBlankPage();
}
/**
* After closing all {@link Tab}s, the {@link LocationBarModel} should know that it is not
* showing any {@link Tab}.
*/
@Test
@Feature({"Android-Toolbar"})
@MediumTest
public void testClosingLastTabReflectedInModel() {
Assert.assertNotSame(
"No current tab",
Tab.INVALID_TAB_ID,
getCurrentTabId(mActivityTestRule.getActivity()));
ChromeTabUtils.closeCurrentTab(
InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity());
assertEquals(
"Didn't close all tabs.",
0,
ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity()));
assertEquals(
"LocationBarModel is still trying to show a tab.",
Tab.INVALID_TAB_ID,
getCurrentTabId(mActivityTestRule.getActivity()));
}
@Test
@SmallTest
public void testDisplayAndEditText() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
TestLocationBarModel model =
new TestLocationBarModel(mActivityTestRule.getActivity());
model.setVisibleGurl(UrlConstants.ntpGurl());
assertDisplayAndEditText(model, "", null);
model.setVisibleGurl(JUnitTestGURLs.CHROME_ABOUT);
model.setDisplayUrl("chrome://about");
model.setFullUrl("chrome://about");
assertDisplayAndEditText(model, "chrome://about", "chrome://about");
model.setVisibleGurl(JUnitTestGURLs.URL_1);
model.setDisplayUrl("https://one.com");
model.setFullUrl("https://one.com");
assertDisplayAndEditText(model, "https://one.com", "https://one.com");
model.setDisplayUrl("one.com");
assertDisplayAndEditText(model, "one.com", "https://one.com");
// https://crbug.com/1214481
model.setVisibleGurl(GURL.emptyGURL());
model.setDisplayUrl("about:blank");
model.setFullUrl("about:blank");
assertDisplayAndEditText(model, "about:blank", "about:blank");
model.destroy();
});
}
/** Provides parameters for different types of transitions between tabs. */
public static class IncognitoTransitionParamProvider implements ParameterProvider {
@Override
public Iterable<ParameterSet> getParameters() {
List<ParameterSet> result = new ArrayList<>(8);
for (boolean fromIncognito : Arrays.asList(true, false)) {
for (boolean toIncognito : Arrays.asList(true, false)) {
result.add(
new ParameterSet()
.value(fromIncognito, toIncognito)
.name(
String.format(
"from_%b_to_%b", fromIncognito, toIncognito)));
}
}
return result;
}
}
@Test
@MediumTest
@ParameterAnnotations.UseMethodParameter(IncognitoTransitionParamProvider.class)
public void testOnIncognitoStateChange_switchTab(boolean fromIncognito, boolean toIncognito) {
// Add a regular tab next to the one created in setup.
mActivityTestRule.loadUrlInNewTab("about:blank", /* incognito= */ false);
// Add two incognito tabs.
mActivityTestRule.loadUrlInNewTab("about:blank", /* incognito= */ true);
mActivityTestRule.loadUrlInNewTab("about:blank", /* incognito= */ true);
ChromeTabbedActivity activity = mActivityTestRule.getActivity();
LocationBarModel locationBarModel =
activity.getToolbarManager().getLocationBarModelForTesting();
LocationBarDataProvider.Observer observer = mock(LocationBarDataProvider.Observer.class);
doAnswer(
(invocation) -> {
assertEquals(toIncognito, locationBarModel.isIncognito());
return null;
})
.when(observer)
.onIncognitoStateChanged();
ThreadUtils.runOnUiThreadBlocking(
() -> {
mActivityTestRule
.getActivity()
.getTabModelSelector()
.selectModel(fromIncognito);
locationBarModel.addObserver(observer);
// Switch to an existing tab.
mActivityTestRule
.getActivity()
.getTabModelSelector()
.selectModel(/* incognito= */ toIncognito);
mActivityTestRule
.getActivity()
.getTabModelSelector()
.getCurrentModel()
.setIndex(0, TabSelectionType.FROM_USER);
});
assertEquals(toIncognito, locationBarModel.isIncognito());
if (fromIncognito != toIncognito) {
verify(observer).onIncognitoStateChanged();
} else {
verify(observer, times(0)).onIncognitoStateChanged();
}
}
@Test
@MediumTest
@ParameterAnnotations.UseMethodParameter(IncognitoTransitionParamProvider.class)
public void testOnIncognitoStateChange_newTab(boolean fromIncognito, boolean toIncognito) {
// Add a regular tab next to the one created in setup.
mActivityTestRule.loadUrlInNewTab("about:blank", /* incognito= */ false);
// Add two incognito tabs.
mActivityTestRule.loadUrlInNewTab("about:blank", /* incognito= */ true);
mActivityTestRule.loadUrlInNewTab("about:blank", /* incognito= */ true);
ChromeTabbedActivity activity = mActivityTestRule.getActivity();
LocationBarModel locationBarModel =
activity.getToolbarManager().getLocationBarModelForTesting();
LocationBarDataProvider.Observer observer = mock(LocationBarDataProvider.Observer.class);
doAnswer(
(invocation) -> {
assertEquals(toIncognito, locationBarModel.isIncognito());
return null;
})
.when(observer)
.onIncognitoStateChanged();
ThreadUtils.runOnUiThreadBlocking(
() -> {
mActivityTestRule
.getActivity()
.getTabModelSelector()
.selectModel(fromIncognito);
locationBarModel.addObserver(observer);
});
// Switch to a new tab.
mActivityTestRule.loadUrlInNewTab("about:blank", toIncognito);
assertEquals(toIncognito, locationBarModel.isIncognito());
if (fromIncognito != toIncognito) {
verify(observer).onIncognitoStateChanged();
} else {
verify(observer, times(0)).onIncognitoStateChanged();
}
}
@Test
@MediumTest
public void testOnSecurityStateChanged() {
LocationBarModel locationBarModel =
mActivityTestRule.getActivity().getToolbarManager().getLocationBarModelForTesting();
LocationBarDataProvider.Observer observer = mock(LocationBarDataProvider.Observer.class);
ThreadUtils.runOnUiThreadBlocking(
() -> {
locationBarModel.addObserver(observer);
});
mActivityTestRule.loadUrl(UrlUtils.encodeHtmlDataUri("test content"));
verify(observer, atLeast(1)).onSecurityStateChanged();
}
private void assertDisplayAndEditText(
ToolbarDataProvider dataProvider, String displayText, String editText) {
ThreadUtils.runOnUiThreadBlocking(
() -> {
UrlBarData urlBarData = dataProvider.getUrlBarData();
assertEquals(
"Display text did not match",
displayText,
urlBarData.displayText.toString());
assertEquals("Editing text did not match", editText, urlBarData.editingText);
});
}
/**
* @param activity A reference to {@link ChromeTabbedActivity} to pull {@link android.view.View}
* data from.
* @return The id of the current {@link Tab} as far as the {@link LocationBarModel} sees it.
*/
public static int getCurrentTabId(final ChromeTabbedActivity activity) {
ToolbarLayout toolbar = activity.findViewById(R.id.toolbar);
Assert.assertNotNull("Toolbar is null", toolbar);
ToolbarDataProvider dataProvider = toolbar.getToolbarDataProvider();
Tab tab = dataProvider.getTab();
return tab != null ? tab.getId() : Tab.INVALID_TAB_ID;
}
private class TestLocationBarModel extends LocationBarModel {
public TestLocationBarModel(Context context) {
super(
context,
NewTabPageDelegate.EMPTY,
DomDistillerTabUtils::getFormattedUrlFromOriginalDistillerUrl,
new LocationBarModel.OfflineStatus() {});
initializeWithNative();
Tab tab =
new MockTab(0, ProfileManager.getLastUsedRegularProfile()) {
@Override
public boolean isInitialized() {
return true;
}
@Override
public boolean isFrozen() {
return false;
}
};
setTab(tab, tab.getProfile());
}
private void setVisibleGurl(GURL gurl) {
mVisibleGurl = gurl;
}
private void setFullUrl(String fullUrl) {
mFormattedFullUrl = fullUrl;
}
private void setDisplayUrl(String displayUrl) {
mUrlForDisplay = displayUrl;
}
@Override
public GURL getCurrentGurl() {
return mVisibleGurl == null ? super.getCurrentGurl() : mVisibleGurl;
}
@Override
public String calculateFormattedFullUrl() {
return mFormattedFullUrl == null
? super.calculateFormattedFullUrl()
: mFormattedFullUrl;
}
@Override
public String calculateUrlForDisplay() {
return mUrlForDisplay == null ? super.calculateUrlForDisplay() : mUrlForDisplay;
}
}
}