chromium/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/HostBrowserUtilsTest.java

// Copyright 2018 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.webapk.shell_apk;

import android.content.Context;
import android.os.Bundle;

import androidx.annotation.IntDef;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.ParameterizedRobolectricTestRunner;
import org.robolectric.ParameterizedRobolectricTestRunner.Parameters;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

import org.chromium.components.webapk.lib.common.WebApkMetaDataKeys;
import org.chromium.webapk.test.WebApkTestHelper;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/** Tests for HostBrowserUtils. */
@RunWith(ParameterizedRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class HostBrowserUtilsTest {
    @IntDef({DefaultBrowserWebApkSupport.YES, DefaultBrowserWebApkSupport.NO})
    @Retention(RetentionPolicy.SOURCE)
    public @interface DefaultBrowserWebApkSupport {
        int YES = 0;
        int NO = 1;
    }

    private static final String DEFAULT_BROWSER_SUPPORTING_WEBAPKS =
            "com.google.android.apps.chrome";
    private static final String DEFAULT_BROWSER_NOT_SUPPORTING_WEBAPKS =
            "com.browser.not.supporting.webapks";
    private static final String[] BROWSERS_SUPPORTING_WEBAPKS =
            new String[] {"com.chrome.canary", "com.chrome.dev", "com.chrome.beta"};
    private static final String[] BROWSERS_NOT_SUPPORTING_WEBAPKS =
            new String[] {"com.random.browser1", "com.random.browser2"};
    private static final String[] ALL_BROWSERS =
            mergeStringArrays(
                    new String[] {
                        DEFAULT_BROWSER_SUPPORTING_WEBAPKS, DEFAULT_BROWSER_NOT_SUPPORTING_WEBAPKS
                    },
                    BROWSERS_SUPPORTING_WEBAPKS,
                    BROWSERS_NOT_SUPPORTING_WEBAPKS);

    private Context mContext;
    private TestBrowserInstaller mBrowserInstaller = new TestBrowserInstaller();

    // Whether we are testing with bound WebAPKs.
    private boolean mIsBoundWebApk;

    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {{true}, {false}});
    }

    public HostBrowserUtilsTest(boolean isBoundWebApk) {
        mIsBoundWebApk = isBoundWebApk;
    }

    @Before
    public void setUp() {
        mContext = RuntimeEnvironment.application;
    }

    /**
     * Tests that {@link #computeHostBrowserPackageName()} returns null if there isn't any browser
     * installed on the device.
     */
    @Test
    public void testReturnsNullWhenNoBrowserInstalled() {
        if (mIsBoundWebApk) {
            // Bound browser in AndroidManifest.xml is no longer installed.
            setHostBrowserInMetadata(BROWSERS_SUPPORTING_WEBAPKS[0]);
        }
        Assert.assertNull(HostBrowserUtils.computeHostBrowserPackageName(mContext));
    }

    // Tests the order of precedence for bound WebAPKs for
    // {@link #computeHostBrowserPackageName()}. The expected order of precedence
    // is:
    // 1) Bound browser specified in AndroidManifest.xml
    // 2) The default browser.
    @Test
    public void testComputeHostBrowserBoundWebApkPrecedence() {
        if (!mIsBoundWebApk) return;

        final String boundBrowserSupportingWebApks = BROWSERS_SUPPORTING_WEBAPKS[0];
        setHostBrowserInMetadata(boundBrowserSupportingWebApks);

        // Bound browser in AndroidManifest.xml: Still installed
        setInstalledBrowsers(DefaultBrowserWebApkSupport.YES, ALL_BROWSERS);
        Assert.assertEquals(
                boundBrowserSupportingWebApks,
                HostBrowserUtils.computeHostBrowserPackageName(mContext));

        // Bound browser in AndroidManifest.xml: No longer installed
        // Should use default browser.
        setInstalledBrowsers(DefaultBrowserWebApkSupport.YES, null);
        Assert.assertEquals(
                DEFAULT_BROWSER_SUPPORTING_WEBAPKS,
                HostBrowserUtils.computeHostBrowserPackageName(mContext));
    }

    // Tests the order of precedence for unbound WebAPKs for
    // {@link #computeHostBrowserPackageName()}. This is expected to always open in the default
    // browser.
    @Test
    public void testComputeHostBrowserUnboundWebApkPrecedence() {
        if (mIsBoundWebApk) return;

        // Default browser: Supports WebAPKs
        setInstalledBrowsers(DefaultBrowserWebApkSupport.YES, ALL_BROWSERS);
        Assert.assertEquals(
                DEFAULT_BROWSER_SUPPORTING_WEBAPKS,
                HostBrowserUtils.computeHostBrowserPackageName(mContext));

        // Default browser: Does not support WebAPKs
        // > 1 installed browsers supporting WebAPKs - just use the default browser even though it
        // doesn't support WebAPKs.
        setInstalledBrowsers(DefaultBrowserWebApkSupport.NO, ALL_BROWSERS);
        Assert.assertEquals(
                DEFAULT_BROWSER_NOT_SUPPORTING_WEBAPKS,
                HostBrowserUtils.computeHostBrowserPackageName(mContext));

        // Default browser: Does not support WebAPKS
        // 1 installed browser supporting WebAPKS - still use the default browser even though it
        // doesn't support WebAPKs.
        setInstalledBrowsers(
                DefaultBrowserWebApkSupport.NO,
                new String[] {
                    BROWSERS_SUPPORTING_WEBAPKS[0],
                    BROWSERS_NOT_SUPPORTING_WEBAPKS[0],
                    BROWSERS_NOT_SUPPORTING_WEBAPKS[1]
                });
        Assert.assertEquals(
                DEFAULT_BROWSER_NOT_SUPPORTING_WEBAPKS,
                HostBrowserUtils.computeHostBrowserPackageName(mContext));

        // Default browser: Does not support WebAPKs
        // > 1 installed browsers
        setInstalledBrowsers(
                DefaultBrowserWebApkSupport.NO,
                new String[] {
                    BROWSERS_NOT_SUPPORTING_WEBAPKS[0], BROWSERS_NOT_SUPPORTING_WEBAPKS[1]
                });
        Assert.assertEquals(
                DEFAULT_BROWSER_NOT_SUPPORTING_WEBAPKS,
                HostBrowserUtils.computeHostBrowserPackageName(mContext));
    }

    /**
     * Tests that {@link #computeHostBrowserPackageName()} does not return a browser that has been
     * uninstalled.
     */
    @Test
    public void testDoesNotReturnTheCurrentHostBrowserAfterUninstall() {
        String browserToUse = DEFAULT_BROWSER_NOT_SUPPORTING_WEBAPKS;
        if (mIsBoundWebApk) {
            browserToUse = BROWSERS_SUPPORTING_WEBAPKS[0];
            setHostBrowserInMetadata(browserToUse);
        }

        setInstalledBrowsers(DefaultBrowserWebApkSupport.NO, ALL_BROWSERS);
        Assert.assertEquals(browserToUse, HostBrowserUtils.computeHostBrowserPackageName(mContext));

        mBrowserInstaller.uninstallBrowser(browserToUse);
        Assert.assertNotEquals(
                browserToUse, HostBrowserUtils.computeHostBrowserPackageName(mContext));
    }

    @SafeVarargs
    private static String[] mergeStringArrays(String[]... toMerge) {
        List<String> out = new ArrayList<String>();
        for (String[] toMergeArray : toMerge) {
            out.addAll(Arrays.asList(toMergeArray));
        }
        return out.toArray(new String[0]);
    }

    /** Changes the installed browsers to the passed-in list. */
    private void setInstalledBrowsers(
            @DefaultBrowserWebApkSupport int defaultBrowser, String[] newPackages) {
        String defaultPackage =
                (defaultBrowser == DefaultBrowserWebApkSupport.YES)
                        ? DEFAULT_BROWSER_SUPPORTING_WEBAPKS
                        : DEFAULT_BROWSER_NOT_SUPPORTING_WEBAPKS;
        mBrowserInstaller.setInstalledModernBrowsers(defaultPackage, newPackages);
    }

    private void setHostBrowserInMetadata(String hostBrowserPackage) {
        Bundle bundle = new Bundle();
        bundle.putString(WebApkMetaDataKeys.RUNTIME_HOST, hostBrowserPackage);
        WebApkTestHelper.registerWebApkWithMetaData(
                mContext.getPackageName(), bundle, /* shareTargetMetaData= */ null);
    }
}