chromium/chrome/android/javatests/src/org/chromium/chrome/browser/policy/PolicyAuditorBridgeTest.java

// Copyright 2022 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.policy;

import android.content.Context;

import androidx.test.filters.SmallTest;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.chrome.browser.AppHooks;
import org.chromium.chrome.browser.AppHooksImpl;
import org.chromium.chrome.browser.policy.PolicyAuditor.AuditEvent;
import org.chromium.chrome.browser.policy.PolicyAuditorBridgeTest.FakePolicyAuditor.Entry;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.content_public.browser.GlobalRenderFrameHostId;
import org.chromium.content_public.browser.LifecycleState;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.WebContentsObserver;
import org.chromium.url.GURL;

import java.util.ArrayList;

/** PolicyAuditor integration test. */
@RunWith(ChromeJUnit4ClassRunner.class)
// TODO(crbug.com/344672097): Failing when batched, batch this again.
public class PolicyAuditorBridgeTest {
    static class FakePolicyAuditor extends PolicyAuditor {
        private static FakePolicyAuditor sInstance;

        private FakePolicyAuditor() {
            mEntries = new ArrayList<>();
        }

        static class Entry {
            int mEvent;
            String mUrl;

            public Entry(int event, String url) {
                mEvent = event;
                mUrl = url;
            }
        }

        private ArrayList<Entry> mEntries;

        public static FakePolicyAuditor get() {
            if (sInstance == null) sInstance = new FakePolicyAuditor();
            return sInstance;
        }

        public Entry getEntry(int index) {
            return mEntries.get(index);
        }

        public int getEntriesSize() {
            return mEntries.size();
        }

        public void clearEntries() {
            mEntries.clear();
        }

        @Override
        public void notifyAuditEvent(
                Context context, @AuditEvent int event, String url, String message) {
            mEntries.add(new Entry(event, url));
        }
    }

    @Rule
    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();

    @Rule
    public final BlankCTATabInitialStateRule mInitialStateRule =
            new BlankCTATabInitialStateRule(mActivityTestRule, false);

    @BeforeClass
    public static void beforeClass() {
        AppHooks.setInstanceForTesting(
                new AppHooksImpl() {
                    @Override
                    public PolicyAuditor getPolicyAuditor() {
                        return FakePolicyAuditor.get();
                    }
                });
    }

    @Before
    public void setUp() {
        clearFakePolicyAuditor();
    }

    @After
    public void tearDown() {
        clearFakePolicyAuditor();
    }

    public FakePolicyAuditor getFakePolicyAuditor() {
        PolicyAuditor policyAuditor = AppHooks.get().getPolicyAuditor();
        Assert.assertTrue(policyAuditor instanceof FakePolicyAuditor);
        return (FakePolicyAuditor) policyAuditor;
    }

    public void clearFakePolicyAuditor() {
        getFakePolicyAuditor().clearEntries();
    }

    @Test
    @SmallTest
    public void testSuccessfulNavigation() {
        mActivityTestRule.loadUrl(UrlConstants.VERSION_URL);

        FakePolicyAuditor fakePolicyAuditor = getFakePolicyAuditor();
        Assert.assertEquals(1, fakePolicyAuditor.getEntriesSize());
        Entry entry = fakePolicyAuditor.getEntry(0);
        Assert.assertEquals(AuditEvent.OPEN_URL_SUCCESS, entry.mEvent);
        Assert.assertEquals(UrlConstants.VERSION_URL, entry.mUrl);
    }

    @Test
    @SmallTest
    public void testUnsuccessfulNavigation() throws Exception {
        String invalidUrl = "https://invalid/";

        // Can't use the activity test rule to navigate to invalid urls, the rule has an assert that
        // fails the testcase upon unsuccessful navigations. So, use the tab directly to navigate to
        // the invalid url.
        Tab tab = mActivityTestRule.getActivity().getActivityTab();
        final CallbackHelper loadFinishCallback = new CallbackHelper();
        WebContentsObserver observer =
                new WebContentsObserver() {
                    @Override
                    public void didFinishLoadInPrimaryMainFrame(
                            GlobalRenderFrameHostId rfhId,
                            GURL url,
                            boolean isKnownValid,
                            @LifecycleState int rfhLifecycleState) {
                        loadFinishCallback.notifyCalled();
                    }
                };
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    tab.getWebContents().addObserver(observer);
                    tab.loadUrl(new LoadUrlParams(invalidUrl));
                });

        try {
            loadFinishCallback.waitForCallback(0);
        } finally {
            ThreadUtils.runOnUiThreadBlocking(
                    () -> {
                        tab.getWebContents().removeObserver(observer);
                    });
        }

        FakePolicyAuditor fakePolicyAuditor = (FakePolicyAuditor) AppHooks.get().getPolicyAuditor();

        // After a failed navigation that is not caused by the url being blocked by an
        // administrator, we expect an OPEN_URL_FAILURE entry from didFinishNavigation, followed by
        // an OPEN_URL_SUCCESS entry from didFinishLoad. Android looper keeps executing tasks which
        // keeps calling didFinishNavigation, we only care about the first two after the navigation.
        Assert.assertTrue(fakePolicyAuditor.getEntriesSize() >= 2);

        Entry entry = fakePolicyAuditor.getEntry(0);
        Assert.assertEquals(AuditEvent.OPEN_URL_FAILURE, entry.mEvent);
        Assert.assertEquals(invalidUrl, entry.mUrl);
        entry = fakePolicyAuditor.getEntry(1);
        Assert.assertEquals(AuditEvent.OPEN_URL_SUCCESS, entry.mEvent);
        Assert.assertEquals("chrome-error://chromewebdata/", entry.mUrl);
    }
}