chromium/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridgeTest.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.offlinepages;

import androidx.test.filters.MediumTest;

import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.Callback;
import org.chromium.base.ThreadUtils;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskTraits;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.profiles.OTRProfileID;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.components.offlinepages.background.UpdateRequestResult;
import org.chromium.net.NetworkChangeNotifier;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/** Unit tests for {@link RequestCoordinatorBridge}. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@Batch(Batch.PER_CLASS)
public class RequestCoordinatorBridgeTest {
    @ClassRule
    public static ChromeTabbedActivityTestRule sActivityTestRule =
            new ChromeTabbedActivityTestRule();

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

    private static final int TIMEOUT_MS = 5000;

    private RequestCoordinatorBridge mRequestCoordinatorBridge;
    private Profile mProfile;

    private void initializeBridgeForProfile() throws InterruptedException {
        final Semaphore semaphore = new Semaphore(0);
        PostTask.runOrPostTask(
                TaskTraits.UI_DEFAULT,
                () -> {
                    mRequestCoordinatorBridge = RequestCoordinatorBridge.getForProfile(mProfile);
                    semaphore.release();
                });
        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
    }

    @Before
    public void setUp() throws Exception {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    // Ensure we start in an offline state.
                    NetworkChangeNotifier.forceConnectivityState(false);
                    if (!NetworkChangeNotifier.isInitialized()) {
                        NetworkChangeNotifier.init();
                    }
                });

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mProfile = ProfileManager.getLastUsedRegularProfile();
                });

        initializeBridgeForProfile();

        List<Long> requestsToRemove = new ArrayList<>();
        for (SavePageRequest savePageRequest : OfflineTestUtil.getRequestsInQueue()) {
            requestsToRemove.add(Long.valueOf(savePageRequest.getRequestId()));
        }
        removeRequestsFromQueue(requestsToRemove);
    }

    @Test
    @MediumTest
    public void testGetRequestsInQueue() throws Exception {
        String url = "https://www.google.com/";
        String namespace = "custom_tabs";
        savePageLater(url, namespace);
        SavePageRequest[] requests = OfflineTestUtil.getRequestsInQueue();
        Assert.assertEquals(1, requests.length);
        Assert.assertEquals(namespace, requests[0].getClientId().getNamespace());
        Assert.assertEquals(url, requests[0].getUrl());

        String url2 = "https://mail.google.com/";
        String namespace2 = "last_n";
        savePageLater(url2, namespace2);
        requests = OfflineTestUtil.getRequestsInQueue();
        Assert.assertEquals(2, requests.length);

        HashSet<String> expectedUrls = new HashSet<>();
        expectedUrls.add(url);
        expectedUrls.add(url2);

        HashSet<String> expectedNamespaces = new HashSet<>();
        expectedNamespaces.add(namespace);
        expectedNamespaces.add(namespace2);

        for (SavePageRequest request : requests) {
            Assert.assertTrue(expectedNamespaces.contains(request.getClientId().getNamespace()));
            expectedNamespaces.remove(request.getClientId().getNamespace());
            Assert.assertTrue(expectedUrls.contains(request.getUrl()));
            expectedUrls.remove(request.getUrl());
        }
    }

    @Test
    @MediumTest
    public void testRequestCoordinatorBridgeDisabledInIncognitoTabbedActivity() throws Exception {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mProfile =
                            ProfileManager.getLastUsedRegularProfile()
                                    .getPrimaryOTRProfile(/* createIfNeeded= */ true);
                });
        initializeBridgeForProfile();
        Assert.assertEquals(null, mRequestCoordinatorBridge);
    }

    @Test
    @MediumTest
    public void testRequestCoordinatorBridgeDisabledInIncognitoCCT() throws Exception {
        OTRProfileID otrProfileID = OTRProfileID.createUnique("CCT:Incognito");
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mProfile =
                            ProfileManager.getLastUsedRegularProfile()
                                    .getOffTheRecordProfile(
                                            otrProfileID, /* createIfNeeded= */ true);
                });
        initializeBridgeForProfile();
        Assert.assertEquals(null, mRequestCoordinatorBridge);
    }

    @Test
    @MediumTest
    public void testRemoveRequestsFromQueue() throws Exception {
        String url = "https://www.google.com/";
        String namespace = "custom_tabs";
        savePageLater(url, namespace);

        String url2 = "https://mail.google.com/";
        String namespace2 = "last_n";
        savePageLater(url2, namespace2);

        SavePageRequest[] requests = OfflineTestUtil.getRequestsInQueue();
        Assert.assertEquals(2, requests.length);

        List<Long> requestsToRemove = new ArrayList<>();
        requestsToRemove.add(Long.valueOf(requests[1].getRequestId()));

        List<RequestCoordinatorBridge.RequestRemovedResult> removed =
                removeRequestsFromQueue(requestsToRemove);
        Assert.assertEquals(requests[1].getRequestId(), removed.get(0).getRequestId());
        Assert.assertEquals(UpdateRequestResult.SUCCESS, removed.get(0).getUpdateRequestResult());
        SavePageRequest[] remaining = OfflineTestUtil.getRequestsInQueue();
        Assert.assertEquals(1, remaining.length);

        Assert.assertEquals(requests[0].getRequestId(), remaining[0].getRequestId());
        Assert.assertEquals(requests[0].getUrl(), remaining[0].getUrl());
    }

    private void savePageLater(final String url, final String namespace)
            throws InterruptedException {
        final Semaphore semaphore = new Semaphore(0);
        PostTask.runOrPostTask(
                TaskTraits.UI_DEFAULT,
                () -> {
                    mRequestCoordinatorBridge.savePageLater(
                            url,
                            namespace,
                            /* userRequested= */ true,
                            new OfflinePageOrigin(),
                            new Callback<Integer>() {
                                @Override
                                public void onResult(Integer i) {
                                    Assert.assertEquals(
                                            "SavePageLater did not succeed",
                                            Integer.valueOf(0),
                                            i); // 0 is SUCCESS
                                    semaphore.release();
                                }
                            });
                });
        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
    }

    private List<RequestCoordinatorBridge.RequestRemovedResult> removeRequestsFromQueue(
            final List<Long> requestsToRemove) throws InterruptedException {
        final AtomicReference<List<RequestCoordinatorBridge.RequestRemovedResult>> ref =
                new AtomicReference<>();
        final Semaphore semaphore = new Semaphore(0);
        PostTask.runOrPostTask(
                TaskTraits.UI_DEFAULT,
                () -> {
                    mRequestCoordinatorBridge.removeRequestsFromQueue(
                            requestsToRemove,
                            new Callback<List<RequestCoordinatorBridge.RequestRemovedResult>>() {
                                @Override
                                public void onResult(
                                        List<RequestCoordinatorBridge.RequestRemovedResult>
                                                removedRequests) {
                                    ref.set(removedRequests);
                                    semaphore.release();
                                }
                            });
                });
        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
        return ref.get();
    }
}