Commit 88acaf32 authored by Dan Harrington's avatar Dan Harrington Committed by Commit Bot

Add instrumentation test for prefetch with Feed

Bug: 841516
Change-Id: I53b327330283cc110eb9a23c68753781edfb4218
Reviewed-on: https://chromium-review.googlesource.com/c/1351274
Commit-Queue: Dan H <harringtond@google.com>
Reviewed-by: default avatarCarlos Knippschild <carlosk@chromium.org>
Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Reviewed-by: default avatarSky Malice <skym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#613167}
parent 03439027
...@@ -2059,6 +2059,7 @@ chrome_test_java_sources = [ ...@@ -2059,6 +2059,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskTest.java", "javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskTest.java",
"javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchConfigurationTest.java", "javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchConfigurationTest.java",
"javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFlowTest.java", "javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFlowTest.java",
"javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFeedFlowTest.java",
"javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/TestOfflinePageService.java", "javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/TestOfflinePageService.java",
"javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/TestSuggestionsService.java", "javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/TestSuggestionsService.java",
"javatests/src/org/chromium/chrome/browser/omaha/ExponentialBackoffSchedulerTest.java", "javatests/src/org/chromium/chrome/browser/omaha/ExponentialBackoffSchedulerTest.java",
......
// Copyright 2018 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.offlinepages.prefetch;
import android.graphics.Bitmap;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.support.test.filters.MediumTest;
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;
import org.chromium.base.CommandLine;
import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.RetryOnFailure;
import org.chromium.base.test.util.UrlUtils;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.UrlConstants;
import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
import org.chromium.chrome.browser.feed.FeedProcessScopeFactory;
import org.chromium.chrome.browser.feed.TestNetworkClient;
import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.background_task_scheduler.TaskIds;
import org.chromium.components.background_task_scheduler.TaskParameters;
import org.chromium.components.download.internal.NetworkStatusListenerAndroid;
import org.chromium.components.gcm_driver.instance_id.FakeInstanceIDWithSubtype;
import org.chromium.components.offline_items_collection.ContentId;
import org.chromium.components.offline_items_collection.OfflineContentProvider;
import org.chromium.components.offline_items_collection.OfflineItem;
import org.chromium.components.offline_pages.core.prefetch.proto.StatusOuterClass;
import org.chromium.net.NetworkChangeNotifier;
import org.chromium.net.NetworkChangeNotifierAutoDetect;
import org.chromium.net.test.util.WebServer;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
/**
* Instrumentation tests for Prefetch, using the Feed as the suggestion provider.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@RetryOnFailure
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class PrefetchFeedFlowTest implements WebServer.RequestHandler {
private static final String TAG = "PrefetchFlowTest";
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>());
/* Feed test data
* This file contains test data for Feed so that suggestions can be populated. This test
* attempts to assume as little as possible about this data, so that it can be changed in the
* future. Note: There are many other suggested URLs in this file, and URLs can even be
* repeated.
*/
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/";
private static final String THUMBNAIL_URL1 =
"https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcRydPYA9MW5_G1lKk6fM8OVNf9z7lF5e7ZI2hAAVIAAb-_b3eyQrCQN6j2AcNAaWu-KO11XpQfC-A";
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";
// 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;
// A fake NetworkChangeNotifierAutoDetect which always reports a connection.
private static class FakeAutoDetect extends NetworkChangeNotifierAutoDetect {
public FakeAutoDetect(Observer observer, RegistrationPolicy policy) {
super(observer, policy);
}
@Override
public NetworkState getCurrentNetworkState() {
return new NetworkState(true, ConnectivityManager.TYPE_WIFI, 0, null, false);
}
}
@Rule
public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
new ChromeActivityTestRule<>(ChromeActivity.class);
@Before
public void setUp() throws Exception {
TestNetworkClient client = new TestNetworkClient();
client.setNetworkResponseFile(TEST_FEED);
FeedProcessScopeFactory.setTestNetworkClient(client);
// 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);
// Inject FakeAutoDetect so that the download component will attempt to download a file even
// when there is no connection.
NetworkStatusListenerAndroid.setAutoDetectFactory(
new NetworkStatusListenerAndroid.AutoDetectFactory() {
@Override
public NetworkChangeNotifierAutoDetect create(
NetworkChangeNotifierAutoDetect.Observer observer,
NetworkChangeNotifierAutoDetect.RegistrationPolicy policy) {
return new FakeAutoDetect(observer, policy);
}
});
// Configure flags:
// - Enable OfflinePagesPrefetching and InterestFeedContentSuggestions.
// - Set DownloadService's start_up_delay_ms to 100 ms (5 seconds is default).
// - Set offline_pages_backend.
final String offlinePagesBackend = Uri.encode(mServer.getBaseUrl());
CommandLine.getInstance().appendSwitchWithValue("enable-features",
ChromeFeatureList.OFFLINE_PAGES_PREFETCHING + "<Trial,DownloadService<Trial"
+ "," + ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS + "<Trial");
CommandLine.getInstance().appendSwitchWithValue("force-fieldtrials", "Trial/Group");
CommandLine.getInstance().appendSwitchWithValue("force-fieldtrial-params",
"Trial.Group:start_up_delay_ms/100/offline_pages_backend/" + offlinePagesBackend);
// TestOfflinePageService will send GCM. Enable fake GCM.
FakeInstanceIDWithSubtype.clearDataAndSetEnabled(true);
// Start Chrome.
mActivityTestRule.startMainActivityOnBlankPage();
// Register Offline Page observer and enable limitless prefetching.
ThreadUtils.runOnUiThreadBlocking(() -> {
mProfile = mActivityTestRule.getActivity().getActivityTab().getProfile();
OfflinePageBridge.getForProfile(mProfile).addObserver(
new OfflinePageBridge.OfflinePageModelObserver() {
@Override
public void offlinePageAdded(OfflinePageItem addedPage) {
mAddedPages.add(addedPage);
mPageAddedHelper.notifyCalled();
}
});
PrefetchTestBridge.enableLimitlessPrefetching(true);
PrefetchTestBridge.skipNTPSuggestionsAPIKeyCheck();
});
}
@After
public void tearDown() throws Exception {
FakeInstanceIDWithSubtype.clearDataAndSetEnabled(false);
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());
}
private OfflineItem findItemByUrl(String url) throws InterruptedException, TimeoutException {
CallbackHelper finished = new CallbackHelper();
final AtomicReference<OfflineItem> result = new AtomicReference<OfflineItem>();
ThreadUtils.runOnUiThreadBlocking(() -> {
offlineContentProvider().getAllItems(items -> {
for (OfflineItem item : items) {
if (item.pageUrl.equals(URL1)) {
result.set(item);
}
}
finished.notifyCalled();
});
});
finished.waitForCallback(0);
return result.get();
}
private Bitmap findVisuals(ContentId id) throws InterruptedException, TimeoutException {
final CallbackHelper finished = new CallbackHelper();
final AtomicReference<Bitmap> result = new AtomicReference<Bitmap>();
ThreadUtils.runOnUiThreadBlocking(() -> {
offlineContentProvider().getVisualsForItem(id, (resultId, visuals) -> {
if (visuals != null) {
result.set(visuals.icon);
}
finished.notifyCalled();
});
});
finished.waitForCallback(0);
return result.get();
}
private void runAndWaitForBackgroundTask() throws Throwable {
final CallbackHelper finished = new CallbackHelper();
PrefetchBackgroundTask task = new PrefetchBackgroundTask();
ThreadUtils.runOnUiThreadBlocking(() -> {
TaskParameters.Builder builder =
TaskParameters.create(TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID);
PrefetchBackgroundTask.skipConditionCheckingForTesting();
task.onStartTask(ContextUtils.getApplicationContext(), builder.build(),
(boolean needsReschedule) -> { finished.notifyCalled(); });
});
finished.waitForCallback(0);
}
/**
* Waits for |callbackHelper| to be notified. Runs the background task while waiting.
* @param callbackHelper The callback helper to wait for.
* @param currentCallbacount |callbackHelper|'s current call count.
* @param numberOfCallsToWaitFor number of calls to wait for.
*/
private void runBackgroundTaskUntilCallCountReached(CallbackHelper callbackHelper,
int currentCallCount, int numberOfCallsToWaitFor) throws Throwable {
// It's necessary to run the background task multiple times because we don't always have a
// hook to know when the system is ready for background processing.
long startTime = System.nanoTime();
final long timeoutTime = startTime + TimeUnit.SECONDS.toNanos(5);
while (true) {
runAndWaitForBackgroundTask();
try {
callbackHelper.waitForCallback(
currentCallCount, numberOfCallsToWaitFor, 200, TimeUnit.MILLISECONDS);
return;
} catch (TimeoutException e) {
if (System.nanoTime() > timeoutTime) {
throw e;
}
}
}
}
/** Trigger conditions required to load NTP snippets. */
private void forceLoadSnippets() throws Throwable {
// NTP suggestions require a connection and an accepted EULA.
ThreadUtils.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 {
// TODO(crbug.com/845310): Expand this test. There's some important flows missing and
// systems missing.
ThreadUtils.runOnUiThreadBlocking(() -> {
PrefetchTestBridge.insertIntoCachedImageFetcher(THUMBNAIL_URL1, testImageData());
});
// Set up default (success) OPS behavior for a URL.
TestOfflinePageService.PageBehavior behavior = new TestOfflinePageService.PageBehavior();
mOPS.setPageBehavior(URL1, behavior);
// Set up failure status for all other URLs.
mOPS.setDefaultGenerateStatus(StatusOuterClass.Code.UNKNOWN);
forceLoadSnippets();
// Wait for the page to be added to the offline model.
runBackgroundTaskUntilCallCountReached(mPageAddedHelper, 0, 1);
// Check that the page was downloaded and saved.
Assert.assertEquals(1, mOPS.ReadCalled.getCallCount());
Assert.assertTrue(mAddedPages.size() >= 1);
Assert.assertEquals(URL1, mAddedPages.get(0).getUrl());
Assert.assertEquals(TITLE1, mAddedPages.get(0).getTitle());
Assert.assertEquals(behavior.body.length, mAddedPages.get(0).getFileSize());
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());
}
/** Request two pages. One is ready later, and one fails immediately. */
@Test
@MediumTest
@Feature({"OfflinePrefetchFeed"})
public void testPrefetchPageReadyLater() throws Throwable {
TestOfflinePageService.PageBehavior pageFail = new TestOfflinePageService.PageBehavior();
pageFail.generateStatus = StatusOuterClass.Code.UNKNOWN;
pageFail.getStatus = StatusOuterClass.Code.UNKNOWN;
mOPS.setPageBehavior(URL1, pageFail);
TestOfflinePageService.PageBehavior readyLater = new TestOfflinePageService.PageBehavior();
readyLater.generateStatus = StatusOuterClass.Code.NOT_FOUND;
mOPS.setPageBehavior(URL2, readyLater);
// Set up failure status for all other URLs.
mOPS.setDefaultGenerateStatus(StatusOuterClass.Code.UNKNOWN);
forceLoadSnippets();
runBackgroundTaskUntilCallCountReached(mOPS.GeneratePageBundleCalled, 0, 1);
// At this point, the bundle has been requested. Send the GCM message, and wait for the page
// to be imported. If this assert fails, GeneratePageBundle may not have been called.
Assert.assertEquals("operation-1", mOPS.sendPushMessage());
runAndWaitForBackgroundTask();
mPageAddedHelper.waitForCallback(0, 1);
// Only URL2 is added.
Assert.assertEquals(1, mAddedPages.size());
Assert.assertEquals(URL2, mAddedPages.get(0).getUrl());
Assert.assertEquals(readyLater.body.length, mAddedPages.get(0).getFileSize());
Assert.assertNotEquals("", mAddedPages.get(0).getFilePath());
}
}
...@@ -49,7 +49,9 @@ import java.util.concurrent.TimeUnit; ...@@ -49,7 +49,9 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
/** /**
* Instrumentation tests for Prefetch. * Instrumentation tests for Prefetch, using the Zine as the suggestion provider.
* Note: this test will eventually be removed along with Zine, and is replaced by
* PrefetchFeedFlowTest.
*/ */
@RunWith(ChromeJUnit4ClassRunner.class) @RunWith(ChromeJUnit4ClassRunner.class)
@RetryOnFailure @RetryOnFailure
......
...@@ -18,8 +18,12 @@ public class PrefetchTestBridge { ...@@ -18,8 +18,12 @@ public class PrefetchTestBridge {
public static void skipNTPSuggestionsAPIKeyCheck() { public static void skipNTPSuggestionsAPIKeyCheck() {
nativeSkipNTPSuggestionsAPIKeyCheck(); nativeSkipNTPSuggestionsAPIKeyCheck();
} }
public static void insertIntoCachedImageFetcher(String url, byte[] imageData) {
nativeInsertIntoCachedImageFetcher(url, imageData);
}
static native void nativeEnableLimitlessPrefetching(boolean enabled); static native void nativeEnableLimitlessPrefetching(boolean enabled);
static native boolean nativeIsLimitlessPrefetchingEnabled(); static native boolean nativeIsLimitlessPrefetchingEnabled();
static native void nativeSkipNTPSuggestionsAPIKeyCheck(); static native void nativeSkipNTPSuggestionsAPIKeyCheck();
static native void nativeInsertIntoCachedImageFetcher(String url, byte[] imageData);
} }
...@@ -48,6 +48,7 @@ public class TestOfflinePageService { ...@@ -48,6 +48,7 @@ public class TestOfflinePageService {
private ArrayList<String> mIncompleteOperations = new ArrayList<String>(); private ArrayList<String> mIncompleteOperations = new ArrayList<String>();
// Determines how this fake service responds to requests for pages. // Determines how this fake service responds to requests for pages.
private HashMap<String, PageBehavior> mPageBehaviors = new HashMap<String, PageBehavior>(); private HashMap<String, PageBehavior> mPageBehaviors = new HashMap<String, PageBehavior>();
private StatusOuterClass.Code mDefaultGenerateStatus = StatusOuterClass.Code.OK;
private static int sNextOperationIndex = 1; private static int sNextOperationIndex = 1;
private static String newOperationName() { private static String newOperationName() {
...@@ -74,9 +75,15 @@ public class TestOfflinePageService { ...@@ -74,9 +75,15 @@ public class TestOfflinePageService {
mPageBehaviors.put(url, behavior); mPageBehaviors.put(url, behavior);
} }
public void setDefaultGenerateStatus(StatusOuterClass.Code status) {
mDefaultGenerateStatus = status;
}
public PageBehavior getPageBehavior(String url) { public PageBehavior getPageBehavior(String url) {
if (!mPageBehaviors.containsKey(url)) { if (!mPageBehaviors.containsKey(url)) {
mPageBehaviors.put(url, new PageBehavior()); PageBehavior behavior = new PageBehavior();
behavior.generateStatus = mDefaultGenerateStatus;
mPageBehaviors.put(url, behavior);
} }
return mPageBehaviors.get(url); return mPageBehaviors.get(url);
} }
...@@ -94,9 +101,9 @@ public class TestOfflinePageService { ...@@ -94,9 +101,9 @@ public class TestOfflinePageService {
// Figure out what kind of request this is, and dispatch the appropriate method. // Figure out what kind of request this is, and dispatch the appropriate method.
if (request.getMethod().equals("POST") if (request.getMethod().equals("POST")
&& request.getURI().startsWith("/v1:GeneratePageBundle")) { && request.getURI().startsWith("/v1:GeneratePageBundle")) {
GeneratePageBundleRequest bundleRequest = null;
try { try {
bundleRequest = GeneratePageBundleRequest.parseFrom(request.getBody()); GeneratePageBundleRequest bundleRequest =
GeneratePageBundleRequest.parseFrom(request.getBody());
handleGeneratePageBundle(bundleRequest, stream); handleGeneratePageBundle(bundleRequest, stream);
GeneratePageBundleCalled.notifyCalled(); GeneratePageBundleCalled.notifyCalled();
return true; return true;
......
...@@ -3,8 +3,14 @@ ...@@ -3,8 +3,14 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "base/android/jni_android.h" #include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/jni_utils.h" #include "base/android/jni_utils.h"
#include "chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.h"
#include "chrome/browser/offline_pages/prefetch/prefetch_service_factory.h" #include "chrome/browser/offline_pages/prefetch/prefetch_service_factory.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "components/image_fetcher/core/cache/image_cache.h"
#include "components/image_fetcher/core/cached_image_fetcher_service.h"
#include "components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h" #include "components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h"
#include "components/offline_pages/core/offline_page_feature.h" #include "components/offline_pages/core/offline_page_feature.h"
#include "jni/PrefetchTestBridge_jni.h" #include "jni/PrefetchTestBridge_jni.h"
...@@ -34,5 +40,24 @@ JNI_EXPORT void JNI_PrefetchTestBridge_SkipNTPSuggestionsAPIKeyCheck( ...@@ -34,5 +40,24 @@ JNI_EXPORT void JNI_PrefetchTestBridge_SkipNTPSuggestionsAPIKeyCheck(
set_skip_api_key_check_for_testing(); set_skip_api_key_check_for_testing();
} }
JNI_EXPORT void JNI_PrefetchTestBridge_InsertIntoCachedImageFetcher(
JNIEnv* env,
const JavaParamRef<jstring>& j_url,
const JavaParamRef<jbyteArray>& j_image_data) {
Profile* profile = ProfileManager::GetLastUsedProfile();
DCHECK(profile);
image_fetcher::CachedImageFetcherService* service =
image_fetcher::CachedImageFetcherServiceFactory::GetForBrowserContext(
profile);
DCHECK(service);
scoped_refptr<image_fetcher::ImageCache> cache =
service->ImageCacheForTesting();
std::string url = base::android::ConvertJavaStringToUTF8(env, j_url);
std::string image_data;
base::android::JavaByteArrayToString(env, j_image_data, &image_data);
cache->SaveImage(url, image_data);
}
} // namespace prefetch } // namespace prefetch
} // namespace offline_pages } // namespace offline_pages
...@@ -36,4 +36,9 @@ CachedImageFetcherService::CreateCachedImageFetcher() { ...@@ -36,4 +36,9 @@ CachedImageFetcherService::CreateCachedImageFetcher() {
image_cache_, read_only_); image_cache_, read_only_);
} }
scoped_refptr<ImageCache> CachedImageFetcherService::ImageCacheForTesting()
const {
return image_cache_;
}
} // namespace image_fetcher } // namespace image_fetcher
...@@ -40,6 +40,8 @@ class CachedImageFetcherService : public KeyedService { ...@@ -40,6 +40,8 @@ class CachedImageFetcherService : public KeyedService {
// Create an instance of CachedImageFetcher based on the ImageCache. // Create an instance of CachedImageFetcher based on the ImageCache.
std::unique_ptr<CachedImageFetcher> CreateCachedImageFetcher(); std::unique_ptr<CachedImageFetcher> CreateCachedImageFetcher();
scoped_refptr<ImageCache> ImageCacheForTesting() const;
private: private:
CreateImageDecoderCallback create_image_decoder_callback_; CreateImageDecoderCallback create_image_decoder_callback_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
......
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