chromium/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncherTest.java

// Copyright 2019 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 android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;

import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsSession;
import androidx.core.app.BundleCompat;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DisableIf;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.chrome.browser.AppHooksModule;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
import org.chromium.chrome.browser.dependency_injection.ModuleOverridesRule;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.components.externalauth.ExternalAuthUtils;

import java.util.concurrent.TimeoutException;

/** Tests for {@link IncognitoTabLauncher}. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@EnableFeatures({ChromeFeatureList.ALLOW_NEW_INCOGNITO_TAB_INTENTS})
@DisableIf.Build(
        sdk_is_greater_than = Build.VERSION_CODES.O,
        message = "Flaky, see crbug.com/1246132")
public class IncognitoTabLauncherTest {
    private boolean mIsCurrentTestFirstParty;

    private final TestRule mModuleOverridesRule =
            new ModuleOverridesRule()
                    .setOverride(AppHooksModule.Factory.class, AppHooksModuleForTest::new);

    private final ChromeTabbedActivityTestRule mActivityRule = new ChromeTabbedActivityTestRule();

    @Rule
    public TestRule mOverrideModulesThenLaunchRule =
            RuleChain.outerRule(mModuleOverridesRule).around(mActivityRule);

    /**
     * To load a fake module in tests we need to bypass a check if package name of module is
     * Google-signed. This class overrides this check for testing.
     */
    /* package */ class AppHooksModuleForTest extends AppHooksModule {
        @Override
        public ExternalAuthUtils provideExternalAuthUtils() {
            return new ExternalAuthUtils() {
                @Override
                public boolean isGoogleSigned(String packageName) {
                    return mIsCurrentTestFirstParty;
                }
            };
        }
    }

    @Test
    @Feature("Incognito")
    @SmallTest
    public void testEnableComponent() throws TimeoutException {
        Context context = ApplicationProvider.getApplicationContext();
        IncognitoTabLauncher.setComponentEnabled(true);
        Assert.assertNotNull(
                context.getPackageManager().resolveActivity(createLaunchIntent(context), 0));
    }

    @Test
    @Feature("Incognito")
    @SmallTest
    public void testDisableComponent() throws TimeoutException {
        Context context = ApplicationProvider.getApplicationContext();
        IncognitoTabLauncher.setComponentEnabled(false);
        Assert.assertNull(
                context.getPackageManager().resolveActivity(createLaunchIntent(context), 0));
    }

    @Test
    @Feature("Incognito")
    @MediumTest
    @DisabledTest(message = "crbug.com/1237504")
    public void testLaunchIncognitoNewTab() throws TimeoutException {
        ChromeTabbedActivity activity = launchIncognitoTab(false);
        assertIncognitoTabLaunched(activity, false);
    }

    @Test
    @Feature("Incognito")
    @MediumTest
    @DisabledTest(message = "crbug.com/1237504")
    @DisableFeatures({ChromeFeatureList.FOCUS_OMNIBOX_IN_INCOGNITO_TAB_INTENTS})
    public void testLaunchIncognitoNewTab_omniboxFocused_disabled_firstParty()
            throws TimeoutException {
        ChromeTabbedActivity activity = launchIncognitoTab(true);
        assertIncognitoTabLaunched(activity, false);
    }

    @Test
    @Feature("Incognito")
    @MediumTest
    @DisabledTest(message = "crbug.com/1237504")
    @DisableFeatures({ChromeFeatureList.FOCUS_OMNIBOX_IN_INCOGNITO_TAB_INTENTS})
    public void testLaunchIncognitoNewTab_omniboxFocused_disabled_thirdParty()
            throws TimeoutException {
        ChromeTabbedActivity activity = launchIncognitoTab(false);
        assertIncognitoTabLaunched(activity, false);
    }

    @Test
    @Feature("Incognito")
    @MediumTest
    @DisabledTest(message = "crbug.com/1237504")
    @EnableFeatures({ChromeFeatureList.FOCUS_OMNIBOX_IN_INCOGNITO_TAB_INTENTS})
    public void testLaunchIncognitoNewTab_omniboxFocused_enabled_thirdParty()
            throws TimeoutException {
        ChromeTabbedActivity activity = launchIncognitoTab(false);
        assertIncognitoTabLaunched(activity, false);
    }

    @Test
    @Feature("Incognito")
    @MediumTest
    @DisabledTest(message = "crbug.com/1237504")
    @EnableFeatures({ChromeFeatureList.FOCUS_OMNIBOX_IN_INCOGNITO_TAB_INTENTS})
    public void testLaunchIncognitoNewTab_omniboxFocused_enabled_firstParty()
            throws TimeoutException {
        ChromeTabbedActivity activity = launchIncognitoTab(true);
        assertIncognitoTabLaunched(activity, true);
    }

    private ChromeTabbedActivity launchIncognitoTab(boolean asFirstParty) throws TimeoutException {
        mIsCurrentTestFirstParty = asFirstParty;

        Context context = ApplicationProvider.getApplicationContext();
        IncognitoTabLauncher.setComponentEnabled(true);
        Intent intent = createLaunchIntent(context);

        // We need FLAG_ACTIVITY_NEW_TASK because we're calling from the application context (not an
        // Activity context). This is fine though because ChromeActivityTestRule.waitFor uses
        // ApplicationStatus internally, which ignores Tasks and tracks all Chrome Activities.
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        ThreadUtils.runOnUiThreadBlocking(() -> context.startActivity(intent));
        return ChromeTabbedActivityTestRule.waitFor(ChromeTabbedActivity.class);
    }

    private Intent createLaunchIntent(Context context) throws TimeoutException {
        // To emulate first party we create a CustomTabIntent with an associated
        // session token. Then, we create a normal intent and copy the session token
        // from CustomTabIntent to the normal intent.
        CustomTabsConnection connection = CustomTabsTestUtils.setUpConnection();
        CustomTabsSession session = CustomTabsTestUtils.bindWithCallback(null).session;

        CustomTabsIntent custom_tab_intent = new CustomTabsIntent.Builder(session).build();

        // Restrict ourselves to Chrome's package, on the off chance the testing device has
        // another application that answers to the ACTION_LAUNCH_NEW_INCOGNITO_TAB action.
        Intent intent = new Intent(IncognitoTabLauncher.ACTION_LAUNCH_NEW_INCOGNITO_TAB);
        intent.setPackage(context.getPackageName());

        Bundle extras = new Bundle();
        BundleCompat.putBinder(
                extras,
                CustomTabsIntent.EXTRA_SESSION,
                custom_tab_intent.intent.getExtras().getBinder(CustomTabsIntent.EXTRA_SESSION));

        intent.putExtras(extras);
        return intent;
    }

    private void assertNonIncognitoTabLaunched(ChromeTabbedActivity activity) {
        Assert.assertFalse(activity.getTabModelSelector().isIncognitoSelected());
    }

    private void assertIncognitoTabLaunched(
            ChromeTabbedActivity activity, boolean isUrlBarFocused) {
        Assert.assertTrue(activity.getTabModelSelector().isIncognitoSelected());
        Assert.assertTrue(IncognitoTabLauncher.didCreateIntent(activity.getIntent()));
        Assert.assertEquals(isUrlBarFocused, activity.getToolbarManager().isUrlBarFocused());
    }
}