Commit 9cc448d4 authored by Henrique Nakashima's avatar Henrique Nakashima Committed by Commit Bot

Extend PrefetchFeedFlowTest to test running in Reduced Mode.

Tests are largely the same as PrefetchFeedFlowTest with a couple of
changes in external code and in the test itself so it works in Reduced
Mode.

Bug: 959957
Change-Id: I75bdf3e149460e8734572f0b7fe6aaae61b796b3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1669656
Commit-Queue: Henrique Nakashima <hnakashima@chromium.org>
Reviewed-by: default avatarDan H <harringtond@chromium.org>
Cr-Commit-Position: refs/heads/master@{#677031}
parent eb26f201
......@@ -111,6 +111,20 @@ public class OfflineTestUtil {
return result.get();
}
public static byte[] getRawThumbnail(long offlineId)
throws TimeoutException, InterruptedException {
final AtomicReference<byte[]> result = new AtomicReference<>();
final CallbackHelper callbackHelper = new CallbackHelper();
TestThreadUtils.runOnUiThreadBlocking(() -> {
nativeGetRawThumbnail(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, InterruptedException {
......@@ -175,6 +189,7 @@ public class OfflineTestUtil {
private static native void nativeGetRequestsInQueue(Callback<SavePageRequest[]> callback);
private static native void nativeGetAllPages(
List<OfflinePageItem> offlinePages, final Callback<List<OfflinePageItem>> callback);
private static native void nativeGetRawThumbnail(long offlineId, Callback<byte[]> callback);
private static native void nativeStartRequestCoordinatorProcessing();
private static native void nativeInterceptWithOfflineError(String url, Runnable readyRunnable);
private static native void nativeClearIntercepts();
......
......@@ -12,7 +12,6 @@ import android.util.Base64;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -37,9 +36,11 @@ import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
import org.chromium.chrome.browser.offlinepages.OfflineTestUtil;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileKey;
import org.chromium.chrome.browser.util.UrlConstants;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ReducedModeNativeTestRule;
import org.chromium.chrome.test.util.ChromeRestriction;
import org.chromium.components.background_task_scheduler.TaskIds;
import org.chromium.components.background_task_scheduler.TaskParameters;
......@@ -67,20 +68,19 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* Instrumentation tests for Prefetch, using the Feed as the suggestion provider.
* Instrumentation tests for Prefetch, using the Feed as the suggestion provider. Most test cases
* are run both in full browser mode and in reduced mode.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@Restriction({ChromeRestriction.RESTRICTION_TYPE_REQUIRES_TOUCH})
@RetryOnFailure
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
private static final String TAG = "PrefetchFlowTest";
public class PrefetchFeedFlowTest {
private TestOfflinePageService mOPS = new TestOfflinePageService();
private WebServer mServer;
private Profile mProfile;
private CallbackHelper mPageAddedHelper = new CallbackHelper();
private List<OfflinePageItem> mAddedPages =
Collections.synchronizedList(new ArrayList<OfflinePageItem>());
private List<OfflinePageItem> mAddedPages = Collections.synchronizedList(new ArrayList<>());
private boolean mUseReducedMode;
/* Feed test data
* This file contains test data for Feed so that suggestions can be populated. This test
......@@ -90,30 +90,86 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
*/
private static final String TEST_FEED =
UrlUtils.getIsolatedTestFilePath("/chrome/test/data/android/feed/feed_large.gcl.bin");
// The first suggestion URL, thumbnail URL, and title.
private static final String URL1 =
"http://profootballtalk.nbcsports.com/2017/11/10/jerry-jones-owners-should-approve-of-roger-goodells-decisions/";
"http://profootballtalk.nbcsports.com/2017/11/10/jerry-jones-owners-should-approve-of-"
+ "roger-goodells-decisions/";
private static final String THUMBNAIL_URL1 =
"https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcRydPYA9MW5_G1lKk6fM8OVNf9z7lF5e7ZI2hAAVIAAb-_b3eyQrCQN6j2AcNAaWu-KO11XpQfC-A";
"https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcRydPYA9MW5_G1lKk6fM8OVNf9z7lF5e7"
+ "ZI2hAAVIAAb-_b3eyQrCQN6j2AcNAaWu-KO11XpQfC-A";
private static final String FAVICON_URL1 =
"https://www.google.com/s2/favicons?domain=www.profootballtalk.com&sz=48";
private static final String TITLE1 =
"Jerry Jones: Owners should approve of Roger Goodell's decisions (2097)";
// The second suggestion URL and thumbnail URL.
private static final String URL2 =
"https://www.nytimes.com/2017/11/10/world/asia/trump-apec-asia-trade.html";
private static final String THUMBNAIL_URL2 =
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRh1tEaJT-br6mBxM89U3vgjDldwb9L_baZszhstAGMQh3_fuG13ax3C9ewR2tq45tbZj74CHl3KNU";
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRh1tEaJT-br6mBxM89U3vgjDldwb9L_b"
+ "aZszhstAGMQh3_fuG13ax3C9ewR2tq45tbZj74CHl3KNU";
private static final String FAVICON_URL2 =
"https://www.google.com/s2/favicons?domain=www.nytimes.com&sz=48";
private static final String TITLE2 =
"Trump Pitches 'American First' Trade Policy at Asia-Pacific Gathering";
// Returns a small PNG image data.
private static byte[] testImageData() {
final String imageBase64 =
"iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAABHNCSVQICAgIfAhkiAAAADRJREFU"
+ "CJlNwTERgDAABLA8h7AaqCwUdcYCmOFYn5UkbSsBWvsU7/GAM7H5u4a07RTrHuADaewQm6Wdp7oA"
+ "AAAASUVORK5CYII=";
return Base64.decode(imageBase64, Base64.DEFAULT);
}
private static final int THUMBNAIL_WIDTH = 4;
private static final int THUMBNAIL_HEIGHT = 4;
@Rule
public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
new ChromeActivityTestRule<>(ChromeActivity.class);
@Rule
public ReducedModeNativeTestRule mReducedModeNativeTestRule =
new ReducedModeNativeTestRule(/*autoLoadNative=*/false);
private WebServer.RequestHandler mRequestHandler = new WebServer.RequestHandler() {
@Override
public void handleRequest(WebServer.HTTPRequest request, OutputStream stream) {
try {
if (mOPS.handleRequest(request, stream)) {
// Handled.
} else {
Assert.fail("Unhandled request: " + request.toString());
}
} catch (IOException e) {
Assert.fail(e.getMessage() + " \n while handling request: " + request.toString());
}
}
};
private void loadNative() throws InterruptedException {
if (mUseReducedMode) {
mReducedModeNativeTestRule.loadNative();
} else {
mActivityTestRule.startMainActivityOnBlankPage();
}
}
private void forceLoadSnippets() throws Throwable {
if (mUseReducedMode) {
// NTP suggestions require a connection.
TestThreadUtils.runOnUiThreadBlocking(() -> {
NetworkChangeNotifier.forceConnectivityState(true);
PrefetchTestBridge.addCandidatePrefetchURL(
URL1, TITLE1, THUMBNAIL_URL1, FAVICON_URL1, "", "");
PrefetchTestBridge.addCandidatePrefetchURL(
URL2, TITLE2, THUMBNAIL_URL2, FAVICON_URL2, "", "");
});
} else {
// NTP suggestions require a connection and an accepted EULA.
TestThreadUtils.runOnUiThreadBlocking(() -> {
NetworkChangeNotifier.forceConnectivityState(true);
PrefServiceBridge.getInstance().setEulaAccepted();
});
// Loading the NTP triggers loading suggestions.
mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
}
}
// A fake NetworkChangeNotifierAutoDetect which always reports a connection.
private static class FakeAutoDetect extends NetworkChangeNotifierAutoDetect {
public FakeAutoDetect(Observer observer, RegistrationPolicy policy) {
......@@ -125,6 +181,15 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
}
}
// Returns a small PNG image data.
private static byte[] testImageData() {
final String imageBase64 =
"iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAABHNCSVQICAgIfAhkiAAAADRJREFU"
+ "CJlNwTERgDAABLA8h7AaqCwUdcYCmOFYn5UkbSsBWvsU7/GAM7H5u4a07RTrHuADaewQm6Wdp7oA"
+ "AAAASUVORK5CYII=";
return Base64.decode(imageBase64, Base64.DEFAULT);
}
// Helper for checking isPrefetchingEnabledByServer().
private boolean isEnabledByServer() {
final AtomicBoolean isEnabled = new AtomicBoolean();
......@@ -139,12 +204,12 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
}, "never got wanted value", 5000, 200);
}
@Rule
public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
new ChromeActivityTestRule<>(ChromeActivity.class);
private void doSetUp(boolean isReducedMode) throws Exception {
mUseReducedMode = isReducedMode;
if (mUseReducedMode) {
PrefetchBackgroundTask.alwaysSupportServiceManagerOnlyForTesting();
}
@Before
public void setUp() throws Exception {
TestNetworkClient client = new TestNetworkClient();
client.setNetworkResponseFile(TEST_FEED);
FeedProcessScopeFactory.setTestNetworkClient(client);
......@@ -152,7 +217,7 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
// Start the server before Chrome starts, offline_pages_backend is overridden later with
// this server's address.
mServer = new WebServer(0, false);
mServer.setRequestHandler(this);
mServer.setRequestHandler(mRequestHandler);
// Inject FakeAutoDetect so that the download component will attempt to download a file even
// when there is no connection.
NetworkStatusListenerAndroid.setAutoDetectFactory(
......@@ -181,13 +246,12 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
FakeInstanceIDWithSubtype.clearDataAndSetEnabled(true);
// Start Chrome.
mActivityTestRule.startMainActivityOnBlankPage();
loadNative();
// Register Offline Page observer and enable limitless prefetching.
TestThreadUtils.runOnUiThreadBlocking(() -> {
mProfile = mActivityTestRule.getActivity().getActivityTab().getProfile();
OfflinePageBridge.getForProfile(mProfile).addObserver(
new OfflinePageBridge.OfflinePageModelObserver() {
OfflinePageBridge.getForProfileKey(ProfileKey.getLastUsedProfileKey())
.addObserver(new OfflinePageBridge.OfflinePageModelObserver() {
@Override
public void offlinePageAdded(OfflinePageItem addedPage) {
mAddedPages.add(addedPage);
......@@ -208,19 +272,6 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
mServer.shutdown();
}
@Override
public void handleRequest(WebServer.HTTPRequest request, OutputStream stream) {
try {
if (mOPS.handleRequest(request, stream)) {
// Handled.
} else {
Assert.fail("Unhandled request: " + request.toString());
}
} catch (IOException e) {
Assert.fail(e.getMessage() + " \n while handling request: " + request.toString());
}
}
private static OfflineContentProvider offlineContentProvider() {
return OfflineContentAggregatorFactory.forProfile(
Profile.getLastUsedProfile().getOriginalProfile());
......@@ -235,9 +286,19 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
return null;
}
private OfflinePageItem findPageByUrl(String url)
throws InterruptedException, TimeoutException {
for (OfflinePageItem page : OfflineTestUtil.getAllPages()) {
if (page.getUrl().equals(url)) {
return page;
}
}
return null;
}
private Bitmap findVisuals(ContentId id) throws InterruptedException, TimeoutException {
final CallbackHelper finished = new CallbackHelper();
final AtomicReference<Bitmap> result = new AtomicReference<Bitmap>();
final AtomicReference<Bitmap> result = new AtomicReference<>();
TestThreadUtils.runOnUiThreadBlocking(() -> {
offlineContentProvider().getVisualsForItem(id, (resultId, visuals) -> {
if (visuals != null) {
......@@ -289,26 +350,11 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
}
}
/** Trigger conditions required to load NTP snippets. */
private void forceLoadSnippets() throws Throwable {
// NTP suggestions require a connection and an accepted EULA.
TestThreadUtils.runOnUiThreadBlocking(() -> {
NetworkChangeNotifier.forceConnectivityState(true);
PrefServiceBridge.getInstance().setEulaAccepted();
});
// Loading the NTP triggers loading suggestions.
mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
}
/**
* Serve a single suggestion to NTP snippets. That suggestion is successfully handled by
* offline prefetch.
*/
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
public void testPrefetchSinglePageSuccess() throws Throwable {
public void doTestPrefetchSinglePageSuccess() throws Throwable {
// TODO(crbug.com/845310): Expand this test. There's some important flows missing and
// systems missing.
TestThreadUtils.runOnUiThreadBlocking(() -> {
......@@ -335,12 +381,10 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
Assert.assertNotEquals("", mAddedPages.get(0).getFilePath());
// Check that the thumbnail was fetched.
OfflineItem item1 = findItemByUrl(URL1);
Assert.assertTrue(item1 != null);
Bitmap visuals = findVisuals(item1.id);
Assert.assertTrue(visuals != null);
Assert.assertEquals(THUMBNAIL_WIDTH, visuals.getWidth());
Assert.assertEquals(THUMBNAIL_HEIGHT, visuals.getHeight());
OfflinePageItem page1 = findPageByUrl(URL1);
Assert.assertNotNull(page1);
byte[] rawThumbnail = OfflineTestUtil.getRawThumbnail(page1.getOfflineId());
Assert.assertArrayEquals(testImageData(), rawThumbnail);
}
/**
......@@ -348,10 +392,7 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
*
* WARNING: this test might be flakey, sometimes waiting for the callback times out regardless.
*/
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
public void testPrefetchPageReadyLater() throws Throwable {
public void doTestPrefetchPageReadyLater() throws Throwable {
TestOfflinePageService.PageBehavior pageFail = new TestOfflinePageService.PageBehavior();
pageFail.generateStatus = StatusOuterClass.Code.UNKNOWN;
pageFail.getStatus = StatusOuterClass.Code.UNKNOWN;
......@@ -381,11 +422,7 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
}
/** Request a page and get a Forbidden response. The enabled-by-server state should change. */
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
@DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/950749
public void testPrefetchForbiddenByServer() throws Throwable {
public void doTestPrefetchForbiddenByServer() throws Throwable {
mOPS.setForbidGeneratePageBundle(true);
Assert.assertTrue(isEnabledByServer());
......@@ -400,11 +437,7 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
/**
* Check that a server-enabled check can enable prefetching.
*/
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
@DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/950749
public void testPrefetchBecomesEnabledByServer() throws Throwable {
public void doTestPrefetchBecomesEnabledByServer() throws Throwable {
OfflineTestUtil.setPrefetchingEnabledByServer(false);
Assert.assertFalse(isEnabledByServer());
......@@ -419,10 +452,7 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
* Check that prefetching remains disabled by the server after receiving a forbidden
* response.
*/
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
public void testPrefetchRemainsDisabledByServer() throws Throwable {
public void doTestPrefetchRemainsDisabledByServer() throws Throwable {
OfflineTestUtil.setPrefetchingEnabledByServer(false);
mOPS.setForbidGeneratePageBundle(true);
......@@ -433,4 +463,87 @@ public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
Assert.assertFalse(isEnabledByServer());
}
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
public void testPrefetchSinglePageSuccess_FullBrowser() throws Throwable {
doSetUp(/*isReducedMode=*/false);
doTestPrefetchSinglePageSuccess();
OfflineItem item1 = findItemByUrl(URL1);
Assert.assertNotNull(item1);
Bitmap visuals = findVisuals(item1.id);
Assert.assertNotNull(visuals);
Assert.assertEquals(THUMBNAIL_WIDTH, visuals.getWidth());
Assert.assertEquals(THUMBNAIL_HEIGHT, visuals.getHeight());
}
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
public void testPrefetchPageReadyLater_FullBrowser() throws Throwable {
doSetUp(/*isReducedMode=*/false);
doTestPrefetchPageReadyLater();
}
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
@DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/950749
public void testPrefetchForbiddenByServer_FullBrowser() throws Throwable {
doSetUp(/*isReducedMode=*/false);
doTestPrefetchForbiddenByServer();
}
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
@DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/950749
public void testPrefetchBecomesEnabledByServer_FullBrowser() throws Throwable {
doSetUp(/*isReducedMode=*/false);
doTestPrefetchBecomesEnabledByServer();
}
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
public void testPrefetchRemainsDisabledByServer_FullBrowser() throws Throwable {
doSetUp(/*isReducedMode=*/false);
doTestPrefetchRemainsDisabledByServer();
}
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
public void testPrefetchSinglePageSuccess_ReducedMode() throws Throwable {
doSetUp(/*isReducedMode=*/true);
doTestPrefetchSinglePageSuccess();
}
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
@DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/950749
public void testPrefetchForbiddenByServer_ReducedMode() throws Throwable {
doSetUp(/*isReducedMode=*/true);
doTestPrefetchForbiddenByServer();
}
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
@DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/950749
public void testPrefetchBecomesEnabledByServer_ReducedMode() throws Throwable {
doSetUp(/*isReducedMode=*/true);
doTestPrefetchBecomesEnabledByServer();
}
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
public void testPrefetchRemainsDisabledByServer_ReducedMode() throws Throwable {
doSetUp(/*isReducedMode=*/true);
doTestPrefetchRemainsDisabledByServer();
}
}
......@@ -46,7 +46,7 @@ RequestCoordinator* GetRequestCoordinator() {
return RequestCoordinatorFactory::GetForBrowserContext(GetProfile());
}
OfflinePageModel* GetOfflinePageModel() {
return OfflinePageModelFactory::GetForBrowserContext(GetProfile());
return OfflinePageModelFactory::GetForKey(::android::GetLastUsedProfileKey());
}
void OnGetAllRequestsDone(
......@@ -67,6 +67,15 @@ void OnGetAllPagesDone(
base::android::RunObjectCallbackAndroid(j_callback_obj, j_result_obj);
}
void OnGetVisualsDoneExtractThumbnail(
const ScopedJavaGlobalRef<jobject>& j_callback_obj,
std::unique_ptr<OfflinePageVisuals> visuals) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> j_bytes =
base::android::ToJavaByteArray(env, visuals->thumbnail);
base::android::RunObjectCallbackAndroid(j_callback_obj, j_bytes);
}
void OnDeletePageDone(const ScopedJavaGlobalRef<jobject>& j_callback_obj,
OfflinePageModel::DeletePageResult result) {
base::android::RunIntCallbackAndroid(j_callback_obj,
......@@ -191,6 +200,18 @@ void JNI_OfflineTestUtil_GetAllPages(
&OnGetAllPagesDone, std::move(j_result_ref), std::move(j_callback_ref)));
}
void JNI_OfflineTestUtil_GetRawThumbnail(
JNIEnv* env,
jlong j_offline_id,
const JavaParamRef<jobject>& j_callback_obj) {
DCHECK(j_offline_id);
GetOfflinePageModel()->GetVisualsByOfflineId(
j_offline_id,
base::BindOnce(&OnGetVisualsDoneExtractThumbnail,
ScopedJavaGlobalRef<jobject>(j_callback_obj)));
}
void JNI_OfflineTestUtil_DeletePagesByOfflineId(
JNIEnv* env,
const JavaParamRef<jlongArray>& j_offline_ids_array,
......
......@@ -71,10 +71,13 @@ void PrefetchServiceImpl::ForceRefreshSuggestions() {
if (suggestions_provider_) {
// Feed only.
NewSuggestionsAvailable();
} else {
} else if (suggested_articles_observer_) {
// Zine only.
DCHECK(suggested_articles_observer_);
suggested_articles_observer_->ConsumeSuggestions();
} else {
// Neither |suggestions_provider_| nor |suggested_articles_observer_| are
// set in reduced mode, which is only supported with Feed.
// ForceRefreshSuggestions() is only called in reduced mode in tests.
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment