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

import androidx.annotation.Nullable;

import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;
import org.junit.Assert;

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.CallbackHelper;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
import org.chromium.chrome.browser.offlinepages.OfflinePageBridge.OfflinePageModelObserver;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.components.offline_items_collection.OfflineItem;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/** Test utility functions for OfflinePages. */
@JNINamespace("offline_pages")
public class OfflineTestUtil {
    // Forces request coordinator to process the requests in the queue.
    public static void startRequestCoordinatorProcessing() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> OfflineTestUtilJni.get().startRequestCoordinatorProcessing());
    }

    // Gets all the URLs in the request queue.
    public static SavePageRequest[] getRequestsInQueue() throws TimeoutException {
        final AtomicReference<SavePageRequest[]> result = new AtomicReference<>();
        final CallbackHelper callbackHelper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    OfflineTestUtilJni.get()
                            .getRequestsInQueue(
                                    (SavePageRequest[] requests) -> {
                                        result.set(requests);
                                        callbackHelper.notifyCalled();
                                    });
                });
        callbackHelper.waitForCallback(0);
        return result.get();
    }

    // Gets all available offline pages.
    public static List<OfflinePageItem> getAllPages() throws TimeoutException {
        final AtomicReference<List<OfflinePageItem>> result =
                new AtomicReference<List<OfflinePageItem>>();
        final CallbackHelper callbackHelper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    OfflineTestUtilJni.get()
                            .getAllPages(
                                    new ArrayList<OfflinePageItem>(),
                                    (List<OfflinePageItem> items) -> {
                                        result.set(items);
                                        callbackHelper.notifyCalled();
                                    });
                });
        callbackHelper.waitForCallback(0);
        return result.get();
    }

    // Returns a string representation of the requests contained in the RequestCoordinator.
    // For logging out to debug test failures.
    public static String dumpRequestCoordinatorState() throws TimeoutException {
        final CallbackHelper callbackHelper = new CallbackHelper();
        final AtomicReference<String> result = new AtomicReference<String>();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    OfflineTestUtilJni.get()
                            .dumpRequestCoordinatorState(
                                    (String dump) -> {
                                        result.set(dump);
                                        callbackHelper.notifyCalled();
                                    });
                });
        callbackHelper.waitForCallback(0);
        return result.get();
    }

    // Returns the OfflinePageItem with the given clientId, or null if one doesn't exist.
    public static @Nullable OfflinePageItem getPageByClientId(ClientId clientId)
            throws TimeoutException {
        for (OfflinePageItem item : getAllPages()) {
            if (item.getClientId().equals(clientId)) {
                return item;
            }
        }
        return null;
    }

    // Returns all OfflineItems provided by the OfflineContentProvider.
    public static List<OfflineItem> getOfflineItems() throws TimeoutException {
        CallbackHelper finished = new CallbackHelper();
        final AtomicReference<ArrayList<OfflineItem>> result =
                new AtomicReference<ArrayList<OfflineItem>>();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    OfflineContentAggregatorFactory.get()
                            .getAllItems(
                                    items -> {
                                        result.set(items);
                                        finished.notifyCalled();
                                    });
                });
        finished.waitForCallback(0);
        return result.get();
    }

    public static byte[] getRawThumbnail(long offlineId) throws TimeoutException {
        final AtomicReference<byte[]> result = new AtomicReference<>();
        final CallbackHelper callbackHelper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    OfflineTestUtilJni.get()
                            .getRawThumbnail(
                                    offlineId,
                                    (byte[] rawThumbnail) -> {
                                        result.set(rawThumbnail);
                                        callbackHelper.notifyCalled();
                                    });
                });
        callbackHelper.waitForCallback(0);
        return result.get();
    }

    // Waits for the offline model to initialize and returns an OfflinePageBridge.
    public static OfflinePageBridge getOfflinePageBridge() throws TimeoutException {
        final CallbackHelper ready = new CallbackHelper();
        final AtomicReference<OfflinePageBridge> result = new AtomicReference<OfflinePageBridge>();
        PostTask.runOrPostTask(
                TaskTraits.UI_DEFAULT,
                () -> {
                    OfflinePageBridge bridge =
                            OfflinePageBridge.getForProfile(
                                    ProfileManager.getLastUsedRegularProfile());
                    if (bridge == null || bridge.isOfflinePageModelLoaded()) {
                        result.set(bridge);
                        ready.notifyCalled();
                        return;
                    }
                    bridge.addObserver(
                            new OfflinePageModelObserver() {
                                @Override
                                public void offlinePageModelLoaded() {
                                    result.set(bridge);
                                    ready.notifyCalled();
                                    bridge.removeObserver(this);
                                }
                            });
                });
        ready.waitForCallback(0);
        Assert.assertTrue(result.get() != null);
        return result.get();
    }

    // Intercepts future HTTP requests for |url| with an offline net error.
    public static void interceptWithOfflineError(String url) throws TimeoutException {
        final CallbackHelper callbackHelper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    OfflineTestUtilJni.get()
                            .interceptWithOfflineError(url, () -> callbackHelper.notifyCalled());
                });
        callbackHelper.waitForCallback(0);
    }

    // Clears all previous intercepts installed by interceptWithOfflineError.
    public static void clearIntercepts() {
        ThreadUtils.runOnUiThreadBlocking(() -> OfflineTestUtilJni.get().clearIntercepts());
    }

    // Waits for the connectivity state to change in the native network change notifier.
    public static void waitForConnectivityState(boolean connected) {
        AtomicBoolean done = new AtomicBoolean();
        ThreadUtils.runOnUiThreadBlocking(
                () ->
                        OfflineTestUtilJni.get()
                                .waitForConnectivityState(connected, () -> done.set(true)));
        CriteriaHelper.pollInstrumentationThread(() -> done.get());
    }

    @NativeMethods
    interface Natives {
        void getRequestsInQueue(Callback<SavePageRequest[]> callback);

        void getAllPages(
                List<OfflinePageItem> offlinePages, final Callback<List<OfflinePageItem>> callback);

        void getRawThumbnail(long offlineId, Callback<byte[]> callback);

        void startRequestCoordinatorProcessing();

        void interceptWithOfflineError(String url, Runnable readyRunnable);

        void clearIntercepts();

        void dumpRequestCoordinatorState(Callback<String> callback);

        void waitForConnectivityState(boolean connected, Runnable callback);
    }
}