// Copyright 2020 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.ui.default_browser_promo;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
import android.os.Build;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowPackageManager;

import org.chromium.base.ContextUtils;
import org.chromium.base.PackageManagerUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoUtils.DefaultBrowserState;

import java.util.ArrayList;
import java.util.List;

/**
 * Unit test for {@link DefaultBrowserPromoUtils}.
 */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class DefaultBrowserPromoUtilsTest {
    @Mock
    DefaultBrowserPromoDeps mDeps;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testGetCurrentDefaultStateForNoDefault() {
        Assert.assertEquals("Should be no default when resolve info matches no browser.",
                DefaultBrowserState.NO_DEFAULT,
                DefaultBrowserPromoDeps.getInstance().getCurrentDefaultBrowserState(
                        createResolveInfo("android", 0)));
    }

    @Test
    public void testGetCurrentDefaultStateForOtherDefault() {
        Assert.assertEquals("Should be other default when resolve info matches another browser.",
                DefaultBrowserPromoUtils.DefaultBrowserState.OTHER_DEFAULT,
                DefaultBrowserPromoDeps.getInstance().getCurrentDefaultBrowserState(
                        createResolveInfo("android", 1)));
    }

    @Test
    public void testGetCurrentDefaultStateForChromeDefault() {
        Assert.assertEquals(
                "Should be chrome default when resolve info matches current package name.",
                DefaultBrowserPromoUtils.DefaultBrowserState.CHROME_DEFAULT,
                DefaultBrowserPromoDeps.getInstance().getCurrentDefaultBrowserState(
                        createResolveInfo(
                                ContextUtils.getApplicationContext().getPackageName(), 1)));
    }

    @Test
    public void testIsChromePreStableInstalled() {
        List<ResolveInfo> infoList = new ArrayList<>();
        ShadowPackageManager packageManager =
                Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager());
        // Setting android_manifest in the junit_binary build rule causes the current package to
        // appear in the PackageManager.
        packageManager.deletePackage(RuntimeEnvironment.application.getPackageName());

        DefaultBrowserPromoDeps deps = DefaultBrowserPromoDeps.getInstance();
        infoList.add(createResolveInfo(DefaultBrowserPromoDeps.CHROME_STABLE_PACKAGE_NAME, 1));
        packageManager.addResolveInfoForIntent(PackageManagerUtils.BROWSER_INTENT, infoList);
        Assert.assertFalse("Chrome stable should not be counted as a pre-stable channel",
                deps.isChromePreStableInstalled());

        infoList.add(createResolveInfo("com.android.chrome.123", 1));
        packageManager.addResolveInfoForIntent(PackageManagerUtils.BROWSER_INTENT, infoList);
        Assert.assertFalse("A random package should not be counted as a pre-stable channel",
                deps.isChromePreStableInstalled());

        for (String name : DefaultBrowserPromoDeps.CHROME_PACKAGE_NAMES) {
            if (name.equals(DefaultBrowserPromoDeps.CHROME_STABLE_PACKAGE_NAME)) continue;
            List<ResolveInfo> list = new ArrayList<>(infoList);
            list.add(createResolveInfo(name, 1));
            packageManager.addResolveInfoForIntent(PackageManagerUtils.BROWSER_INTENT, list);
            Assert.assertTrue(name + " should be considered as a pre-stable channel",
                    deps.isChromePreStableInstalled());
        }
    }

    @Test
    public void testIsCurrentDefaultBrowserChrome() {
        DefaultBrowserPromoDeps deps = DefaultBrowserPromoDeps.getInstance();
        for (String name : DefaultBrowserPromoDeps.CHROME_PACKAGE_NAMES) {
            Assert.assertTrue(name + " should be considered as a chrome channel",
                    deps.isCurrentDefaultBrowserChrome(createResolveInfo(name, 1)));
        }

        Assert.assertFalse("A random string should not be considered as a chrome channel",
                deps.isCurrentDefaultBrowserChrome(
                        createResolveInfo("com.android.chrome.random.string", 1)));
    }

    @Test
    public void testBasicPromo() {
        setDepsMockWithDefaultValues();
        Assert.assertTrue("Should promo disambiguation sheet on Q.",
                DefaultBrowserPromoUtils.shouldShowPromo(mDeps, null));
    }

    // --- Q above ---
    @Test
    public void testPromo_Q_No_Default() {
        setDepsMockWithDefaultValues();
        when(mDeps.isRoleAvailable(any())).thenReturn(true);
        Assert.assertTrue("Should promo role manager when there is no default browser on Q+.",
                DefaultBrowserPromoUtils.shouldShowPromo(mDeps, null));
    }

    @Test
    public void testPromo_Q_Other_Default() {
        setDepsMockWithDefaultValues();
        when(mDeps.isRoleAvailable(any())).thenReturn(true);
        when(mDeps.getDefaultWebBrowserActivityResolveInfo())
                .thenReturn(createResolveInfo("android", 1));
        Assert.assertTrue("Should promo role manager when there is another default browser on Q+.",
                DefaultBrowserPromoUtils.shouldShowPromo(mDeps, null));
    }

    // --- P below ---
    @Test
    public void testNoPromo_P() {
        setDepsMockWithDefaultValues();
        when(mDeps.getSDKInt()).thenReturn(Build.VERSION_CODES.P);
        when(mDeps.isRoleAvailable(any())).thenCallRealMethod();
        Assert.assertFalse(
                "Should not promo on P-.", DefaultBrowserPromoUtils.shouldShowPromo(mDeps, null));
    }

    // --- prerequisites ---
    @Test
    public void testPromo_increasedPromoCount() {
        setDepsMockWithDefaultValues();
        when(mDeps.getMaxPromoCount()).thenReturn(100);
        when(mDeps.getPromoCount()).thenReturn(99);
        Assert.assertTrue("Should promo when promo count does not reach the upper limit.",
                DefaultBrowserPromoUtils.shouldShowPromo(mDeps, null));
    }

    @Test
    public void testNoPromo_greaterThanMaxPromoCount() {
        setDepsMockWithDefaultValues();
        when(mDeps.getPromoCount()).thenReturn(1);
        when(mDeps.getMaxPromoCount()).thenReturn(1);
        Assert.assertFalse("Should not promo when promo count reaches the upper limit.",
                DefaultBrowserPromoUtils.shouldShowPromo(mDeps, null));
    }

    @Test
    public void testNoPromo_featureDisabled() {
        setDepsMockWithDefaultValues();
        when(mDeps.isFeatureEnabled()).thenReturn(false);
        Assert.assertFalse("Should not promo when the feature is disabled.",
                DefaultBrowserPromoUtils.shouldShowPromo(mDeps, null));
    }

    @Test
    public void testNoPromo_lessThanMinSessionCount() {
        setDepsMockWithDefaultValues();
        when(mDeps.getSessionCount()).thenReturn(1);
        when(mDeps.getMinSessionCount()).thenReturn(3);
        Assert.assertFalse(
                "Should not promo when session count has not reached the required amount.",
                DefaultBrowserPromoUtils.shouldShowPromo(mDeps, null));
    }

    @Test
    public void testNoPromo_isOtherChromeDefault() {
        setDepsMockWithDefaultValues();
        when(mDeps.getDefaultWebBrowserActivityResolveInfo())
                .thenReturn(
                        createResolveInfo(DefaultBrowserPromoDeps.CHROME_STABLE_PACKAGE_NAME, 1));
        when(mDeps.isCurrentDefaultBrowserChrome(any())).thenCallRealMethod();
        Assert.assertFalse("Should not promo when another chrome channel browser has been default.",
                DefaultBrowserPromoUtils.shouldShowPromo(mDeps, null));
    }

    @Test
    public void testNoPromo_isCurrentChromeDefault() {
        setDepsMockWithDefaultValues();
        when(mDeps.getDefaultWebBrowserActivityResolveInfo())
                .thenReturn(createResolveInfo(
                        ContextUtils.getApplicationContext().getPackageName(), 1));
        Assert.assertFalse("Should not promo when chrome has been default.",
                DefaultBrowserPromoUtils.shouldShowPromo(mDeps, null));
    }

    @Test
    public void testNoPromo_webBrowserActivityNotExist() {
        setDepsMockWithDefaultValues();
        when(mDeps.getDefaultWebBrowserActivityResolveInfo()).thenReturn(null);
        Assert.assertFalse("Should not promo when web browser activity does not exist.",
                DefaultBrowserPromoUtils.shouldShowPromo(mDeps, null));
    }

    private void setDepsMockWithDefaultValues() {
        when(mDeps.isFeatureEnabled()).thenReturn(true);
        when(mDeps.getMinSessionCount()).thenReturn(3);
        when(mDeps.getSessionCount()).thenReturn(10);
        when(mDeps.doesManageDefaultAppsSettingsActivityExist()).thenReturn(true);
        when(mDeps.getSDKInt()).thenReturn(Build.VERSION_CODES.Q);
        when(mDeps.isChromeStable()).thenReturn(false);
        when(mDeps.getPromoCount()).thenReturn(0);
        when(mDeps.getMaxPromoCount()).thenReturn(1);
        when(mDeps.getLastPromoInterval()).thenReturn(1000);
        when(mDeps.getMinPromoInterval()).thenReturn(10);
        when(mDeps.isChromePreStableInstalled()).thenReturn(false);
        when(mDeps.isCurrentDefaultBrowserChrome(any())).thenReturn(false);
        when(mDeps.isRoleAvailable(any())).thenReturn(true);
        // No Default
        when(mDeps.getDefaultWebBrowserActivityResolveInfo())
                .thenReturn(createResolveInfo("android", 0));
        when(mDeps.getCurrentDefaultBrowserState(any())).thenCallRealMethod();
    }

    private ResolveInfo createResolveInfo(String packageName, int match) {
        ResolveInfo resolveInfo = new ResolveInfo();
        ActivityInfo activityInfo = new ActivityInfo();
        activityInfo.packageName = packageName;
        resolveInfo.activityInfo = activityInfo;
        resolveInfo.match = match;
        return resolveInfo;
    }
}
