// Copyright 2016 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.incognito;
import static org.junit.Assert.assertTrue;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.service.notification.StatusBarNotification;
import android.util.Pair;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.MediumTest;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Criteria;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.customtabs.CustomTabsIntentTestUtils;
import org.chromium.chrome.browser.customtabs.IncognitoCustomTabActivityTestRule;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.TestTabModelDirectory;
import org.chromium.chrome.browser.tabmodel.TestTabModelDirectory.TabStateInfo;
import org.chromium.chrome.browser.tabpersistence.TabStateDirectory;
import org.chromium.chrome.browser.tabpersistence.TabStateFileManager;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.content_public.browser.LoadUrlParams;
import java.io.File;
import java.util.concurrent.Callable;
/** Tests for the Incognito Notification service. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class IncognitoNotificationServiceTest {
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@Rule
public IncognitoCustomTabActivityTestRule mCustomTabActivityTestRule =
new IncognitoCustomTabActivityTestRule();
private void createTabOnUiThread() {
ThreadUtils.runOnUiThreadBlocking(
(Runnable)
() ->
mActivityTestRule
.getActivity()
.getTabCreator(true)
.createNewTab(
new LoadUrlParams("about:blank"),
TabLaunchType.FROM_CHROME_UI,
null));
}
private void sendClearIncognitoIntent() throws CanceledException {
PendingIntent clearIntent =
IncognitoNotificationServiceImpl.getRemoveAllIncognitoTabsIntent(
ApplicationProvider.getApplicationContext())
.getPendingIntent();
clearIntent.send();
}
private void launchIncognitoTabAndEnsureNotificationDisplayed() {
mActivityTestRule.startMainActivityOnBlankPage();
createTabOnUiThread();
CriteriaHelper.pollUiThread(
() -> {
Criteria.checkThat(
mActivityTestRule
.getActivity()
.getTabModelSelector()
.getModel(true)
.getCount(),
Matchers.greaterThanOrEqualTo(1));
});
Context context = ContextUtils.getApplicationContext();
NotificationManager nm =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
boolean isIncognitoNotificationDisplayed = false;
for (StatusBarNotification statusBarNotification : nm.getActiveNotifications()) {
if (IncognitoNotificationManager.INCOGNITO_TABS_OPEN_TAG.equals(
statusBarNotification.getTag())) {
isIncognitoNotificationDisplayed = true;
}
}
assertTrue(isIncognitoNotificationDisplayed);
}
@Test
@Feature("Incognito")
@MediumTest
public void testSingleRunningChromeTabbedActivity()
throws InterruptedException, CanceledException {
mActivityTestRule.startMainActivityOnBlankPage();
createTabOnUiThread();
createTabOnUiThread();
pollUiThreadForChromeActivityIncognitoTabCount(2);
final Profile incognitoProfile =
ThreadUtils.runOnUiThreadBlocking(
new Callable<Profile>() {
@Override
public Profile call() {
return mActivityTestRule
.getActivity()
.getTabModelSelector()
.getModel(true)
.getProfile();
}
});
ThreadUtils.runOnUiThreadBlocking(
() -> {
assertTrue(incognitoProfile.isOffTheRecord());
assertTrue(incognitoProfile.isNativeInitialized());
});
sendClearIncognitoIntent();
pollUiThreadForChromeActivityIncognitoTabCount(0);
CriteriaHelper.pollUiThread(() -> !incognitoProfile.isNativeInitialized());
}
@Test
@Feature("Incognito")
@MediumTest
@DisabledTest(message = "crbug.com/1033835")
public void testNoAliveProcess() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
final TestTabModelDirectory tabbedModeDirectory =
new TestTabModelDirectory(context, "tabs", String.valueOf(0));
// Add a couple non-incognito tabs (their filenames use a different prefix, so we do not
// need to worry about ID space collisions with the generated incognito tabs).
tabbedModeDirectory.writeTabStateFile(TestTabModelDirectory.V2_DUCK_DUCK_GO);
tabbedModeDirectory.writeTabStateFile(TestTabModelDirectory.V2_BAIDU);
// Generate a few incognito tabs (using arbitrary data from an existing TabState
// definition).
for (int i = 0; i < 3; i++) {
TabStateInfo incognitoInfo =
new TabStateInfo(
true,
TestTabModelDirectory.V2_TEXTAREA.version,
i,
TestTabModelDirectory.V2_TEXTAREA.url,
TestTabModelDirectory.V2_TEXTAREA.title,
TestTabModelDirectory.V2_TEXTAREA.encodedTabState);
tabbedModeDirectory.writeTabStateFile(incognitoInfo);
}
TabStateDirectory.setBaseStateDirectoryForTests(tabbedModeDirectory.getBaseDirectory());
File[] tabbedModeFiles = tabbedModeDirectory.getDataDirectory().listFiles();
Assert.assertNotNull(tabbedModeFiles);
Assert.assertEquals(5, tabbedModeFiles.length);
int incognitoCount = 0;
int normalCount = 0;
for (File tabbedModeFile : tabbedModeFiles) {
Pair<Integer, Boolean> tabFileInfo =
TabStateFileManager.parseInfoFromFilename(tabbedModeFile.getName());
if (tabFileInfo.second) incognitoCount++;
else normalCount++;
}
Assert.assertEquals(2, normalCount);
Assert.assertEquals(3, incognitoCount);
sendClearIncognitoIntent();
CriteriaHelper.pollInstrumentationThread(
() -> {
File[] actualTabbedModeFiles =
tabbedModeDirectory.getDataDirectory().listFiles();
if (actualTabbedModeFiles == null) return;
int actualIncognitoCount = 0;
for (File tabbedModeFile : actualTabbedModeFiles) {
Pair<Integer, Boolean> tabFileInfo =
TabStateFileManager.parseInfoFromFilename(tabbedModeFile.getName());
if (tabFileInfo != null && tabFileInfo.second) actualIncognitoCount++;
}
Criteria.checkThat(actualIncognitoCount, Matchers.is(0));
});
CriteriaHelper.pollInstrumentationThread(
() -> {
File[] actualTabbedModeFiles =
tabbedModeDirectory.getDataDirectory().listFiles();
Criteria.checkThat(actualTabbedModeFiles, Matchers.notNullValue());
int actualNormalCount = 0;
for (File tabbedModeFile : actualTabbedModeFiles) {
Pair<Integer, Boolean> tabFileInfo =
TabStateFileManager.parseInfoFromFilename(tabbedModeFile.getName());
if (tabFileInfo != null && !tabFileInfo.second) actualNormalCount++;
}
Criteria.checkThat(actualNormalCount, Matchers.is(2));
});
ThreadUtils.runOnUiThreadBlocking(
() -> Assert.assertFalse(LibraryLoader.getInstance().isInitialized()));
}
@Test
@MediumTest
@Feature("Incognito")
public void testCloseAllIncognitoNotificationIsDisplayed() {
launchIncognitoTabAndEnsureNotificationDisplayed();
}
@Test
@MediumTest
@Feature("Incognito")
public void testCloseAllIncognitoNotificationForIncognitoCCT_DoesNotCloseCCT()
throws PendingIntent.CanceledException {
launchIncognitoTabAndEnsureNotificationDisplayed();
// Create an Incognito CCT now.
Intent customTabIntent =
CustomTabsIntentTestUtils.createMinimalIncognitoCustomTabIntent(
ApplicationProvider.getApplicationContext(), "about:blank");
mCustomTabActivityTestRule.startCustomTabActivityWithIntent(customTabIntent);
// Click on "Close all Incognito tabs" notification.
PendingIntent clearIntent =
IncognitoNotificationServiceImpl.getRemoveAllIncognitoTabsIntent(
ApplicationProvider.getApplicationContext())
.getPendingIntent();
clearIntent.send();
pollUiThreadForChromeActivityIncognitoTabCount(0);
// Verify the Incognito CCT is not closed.
pollUiThreadForCustomIncognitoTabCount(1);
}
private void pollUiThreadForChromeActivityIncognitoTabCount(int expectedCount) {
CriteriaHelper.pollUiThread(
() -> {
Criteria.checkThat(
mActivityTestRule
.getActivity()
.getTabModelSelector()
.getModel(true)
.getCount(),
Matchers.is(expectedCount));
});
}
private void pollUiThreadForCustomIncognitoTabCount(int expectedCount) {
CriteriaHelper.pollUiThread(
() -> {
Criteria.checkThat(
mCustomTabActivityTestRule
.getActivity()
.getTabModelSelector()
.getModel(true)
.getCount(),
Matchers.is(expectedCount));
});
}
}