// Copyright 2015 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 android.net.Uri;
import android.os.Build.VERSION_CODES;
import android.util.Base64;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.MediumTest;
import org.junit.After;
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.ApiCompatibilityUtils;
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.base.test.util.DisabledTest;
import org.chromium.base.test.util.MaxAndroidSdkLevel;
import org.chromium.base.test.util.MinAndroidSdkLevel;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.offlinepages.OfflinePageBridge.OfflinePageModelObserver;
import org.chromium.chrome.browser.offlinepages.OfflinePageBridge.SavePageCallback;
import org.chromium.chrome.browser.offlinepages.downloads.OfflinePageDownloadBridge;
import org.chromium.chrome.browser.profiles.OTRProfileID;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileKey;
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.DeletePageResult;
import org.chromium.components.offlinepages.SavePageResult;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.net.NetworkChangeNotifier;
import org.chromium.net.test.EmbeddedTestServer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
/** Unit tests for {@link OfflinePageBridge}. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@Batch(Batch.PER_CLASS)
public class OfflinePageBridgeTest {
@ClassRule
public static ChromeTabbedActivityTestRule sActivityTestRule =
new ChromeTabbedActivityTestRule();
@Rule
public final BlankCTATabInitialStateRule mInitialStateRule =
new BlankCTATabInitialStateRule(sActivityTestRule, false);
private static final String TEST_PAGE = "/chrome/test/data/android/about.html";
private static final int TIMEOUT_MS = 5000;
private static final ClientId TEST_CLIENT_ID =
new ClientId(OfflinePageBridge.DOWNLOAD_NAMESPACE, "1234");
private OfflinePageBridge mOfflinePageBridge;
private EmbeddedTestServer mTestServer;
private String mTestPage;
private Profile mProfile;
private void initializeBridgeForProfile() throws InterruptedException {
final Semaphore semaphore = new Semaphore(0);
PostTask.runOrPostTask(
TaskTraits.UI_DEFAULT,
() -> {
// Ensure we start in an offline state.
mOfflinePageBridge = OfflinePageBridge.getForProfile(mProfile);
if (mOfflinePageBridge == null
|| mOfflinePageBridge.isOfflinePageModelLoaded()) {
semaphore.release();
return;
}
mOfflinePageBridge.addObserver(
new OfflinePageModelObserver() {
@Override
public void offlinePageModelLoaded() {
semaphore.release();
mOfflinePageBridge.removeObserver(this);
}
});
});
Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
private OfflinePageBridge getBridgeForProfileKey() throws InterruptedException {
final Semaphore semaphore = new Semaphore(0);
AtomicReference<OfflinePageBridge> offlinePageBridgeRef = new AtomicReference<>();
PostTask.runOrPostTask(
TaskTraits.UI_DEFAULT,
() -> {
ProfileKey profileKey = mProfile.getProfileKey();
// Ensure we start in an offline state.
OfflinePageBridge offlinePageBridge =
OfflinePageBridge.getForProfileKey(profileKey);
offlinePageBridgeRef.set(offlinePageBridge);
if (offlinePageBridge == null || offlinePageBridge.isOfflinePageModelLoaded()) {
semaphore.release();
return;
}
offlinePageBridge.addObserver(
new OfflinePageModelObserver() {
@Override
public void offlinePageModelLoaded() {
semaphore.release();
offlinePageBridge.removeObserver(this);
}
});
});
Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
return offlinePageBridgeRef.get();
}
@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> ids = new ArrayList<>();
for (OfflinePageItem page : OfflineTestUtil.getAllPages()) {
ids.add(page.getOfflineId());
}
deletePages(ids);
mTestServer =
EmbeddedTestServer.createAndStartServer(
ApplicationProvider.getApplicationContext());
mTestPage = mTestServer.getURL(TEST_PAGE);
}
@After
public void tearDown() {
mTestServer.stopAndDestroyServer();
}
@Test
@MediumTest
public void testProfileAndKeyMapToSameOfflinePageBridge() throws Exception {
OfflinePageBridge offlinePageBridgeRetrievedByKey = getBridgeForProfileKey();
Assert.assertSame(mOfflinePageBridge, offlinePageBridgeRetrievedByKey);
}
@Test
@MediumTest
public void testLoadOfflinePagesWhenEmpty() throws Exception {
List<OfflinePageItem> offlinePages = OfflineTestUtil.getAllPages();
Assert.assertEquals("Offline pages count incorrect.", 0, offlinePages.size());
}
@Test
@MediumTest
public void testAddOfflinePageAndLoad() throws Exception {
sActivityTestRule.loadUrl(mTestPage);
savePage(SavePageResult.SUCCESS, mTestPage);
List<OfflinePageItem> allPages = OfflineTestUtil.getAllPages();
OfflinePageItem offlinePage = allPages.get(0);
Assert.assertEquals("Offline pages count incorrect.", 1, allPages.size());
Assert.assertEquals("Offline page item url incorrect.", mTestPage, offlinePage.getUrl());
}
@Test
@MediumTest
public void testGetPageByBookmarkId() throws Exception {
sActivityTestRule.loadUrl(mTestPage);
savePage(SavePageResult.SUCCESS, mTestPage);
OfflinePageItem offlinePage = OfflineTestUtil.getPageByClientId(TEST_CLIENT_ID);
Assert.assertEquals("Offline page item url incorrect.", mTestPage, offlinePage.getUrl());
Assert.assertNull(
"Offline page is not supposed to exist",
OfflineTestUtil.getPageByClientId(
new ClientId(OfflinePageBridge.BOOKMARK_NAMESPACE, "-42")));
}
@Test
@MediumTest
public void testDeleteOfflinePage() throws Exception {
deletePage(TEST_CLIENT_ID, DeletePageResult.SUCCESS);
sActivityTestRule.loadUrl(mTestPage);
savePage(SavePageResult.SUCCESS, mTestPage);
Assert.assertNotNull(
"Offline page should be available, but it is not.",
OfflineTestUtil.getPageByClientId(TEST_CLIENT_ID));
deletePage(TEST_CLIENT_ID, DeletePageResult.SUCCESS);
Assert.assertNull(
"Offline page should be gone, but it is available.",
OfflineTestUtil.getPageByClientId(TEST_CLIENT_ID));
}
@Test
@MediumTest
public void testOfflinePageBridgeDisabled_InIncognitoTabbedActivity() throws Exception {
ThreadUtils.runOnUiThreadBlocking(
() -> {
mProfile =
ProfileManager.getLastUsedRegularProfile()
.getPrimaryOTRProfile(/* createIfNeeded= */ true);
});
initializeBridgeForProfile();
Assert.assertEquals(null, mOfflinePageBridge);
}
@Test
@MediumTest
public void testOfflinePageBridgeForProfileKeyDisabled_InIncognitoTabbedActivity()
throws Exception {
ThreadUtils.runOnUiThreadBlocking(
() -> {
mProfile =
ProfileManager.getLastUsedRegularProfile()
.getPrimaryOTRProfile(/* createIfNeeded= */ true);
});
OfflinePageBridge offlinePageBridgeRetrievedByKey = getBridgeForProfileKey();
Assert.assertNull(offlinePageBridgeRetrievedByKey);
}
@Test
@MediumTest
public void testOfflinePageBridgeDisabled_InIncognitoCCT() throws Exception {
ThreadUtils.runOnUiThreadBlocking(
() -> {
OTRProfileID otrProfileID = OTRProfileID.createUnique("CCT:Incognito");
mProfile =
ProfileManager.getLastUsedRegularProfile()
.getOffTheRecordProfile(
otrProfileID, /* createIfNeeded= */ true);
Assert.assertTrue(mProfile.isOffTheRecord());
Assert.assertFalse(mProfile.isPrimaryOTRProfile());
});
initializeBridgeForProfile();
Assert.assertEquals(null, mOfflinePageBridge);
}
@Test
@MediumTest
public void testOfflinePageBridgeForProfileKeyDisabled_InIncognitoCCT() throws Exception {
ThreadUtils.runOnUiThreadBlocking(
() -> {
OTRProfileID otrProfileID = OTRProfileID.createUnique("CCT:Incognito");
mProfile =
ProfileManager.getLastUsedRegularProfile()
.getOffTheRecordProfile(
otrProfileID, /* createIfNeeded= */ true);
Assert.assertTrue(mProfile.isOffTheRecord());
Assert.assertFalse(mProfile.isPrimaryOTRProfile());
});
OfflinePageBridge offlinePageBridgeRetrievedByKey = getBridgeForProfileKey();
Assert.assertNull(offlinePageBridgeRetrievedByKey);
}
@Test
@MediumTest
public void testDeletePagesByOfflineIds() throws Exception {
// Save 3 pages and record their offline IDs to delete later.
Set<String> pageUrls = new HashSet<>();
pageUrls.add(mTestPage);
pageUrls.add(mTestPage + "?foo=1");
pageUrls.add(mTestPage + "?foo=2");
int pagesToDeleteCount = pageUrls.size();
List<Long> offlineIdsToDelete = new ArrayList<>();
for (String url : pageUrls) {
sActivityTestRule.loadUrl(url);
offlineIdsToDelete.add(savePage(SavePageResult.SUCCESS, url));
}
Assert.assertEquals(
"The pages should exist now that we saved them.",
pagesToDeleteCount,
getUrlsExistOfflineFromSet(pageUrls).size());
// Save one more page but don't save the offline ID, this page should not be deleted.
Set<String> pageUrlsToSave = new HashSet<>();
String pageToSave = mTestPage + "?bar=1";
pageUrlsToSave.add(pageToSave);
int pagesToSaveCount = pageUrlsToSave.size();
for (String url : pageUrlsToSave) {
sActivityTestRule.loadUrl(url);
savePage(SavePageResult.SUCCESS, pageToSave);
}
Assert.assertEquals(
"The pages should exist now that we saved them.",
pagesToSaveCount,
getUrlsExistOfflineFromSet(pageUrlsToSave).size());
// Delete the first 3 pages.
deletePages(offlineIdsToDelete);
Assert.assertEquals(
"The page should cease to exist.", 0, getUrlsExistOfflineFromSet(pageUrls).size());
// We should not have deleted the one we didn't ask to delete.
Assert.assertEquals(
"The page should not be deleted.",
pagesToSaveCount,
getUrlsExistOfflineFromSet(pageUrlsToSave).size());
}
@Test
@MediumTest
public void testGetPagesByNamespace() throws Exception {
// Save 3 pages and record their offline IDs to delete later.
Set<Long> offlineIdsToFetch = new HashSet<>();
for (int i = 0; i < 3; i++) {
String url = mTestPage + "?foo=" + i;
sActivityTestRule.loadUrl(url);
offlineIdsToFetch.add(savePage(SavePageResult.SUCCESS, url));
}
// Save a page in a different namespace.
String urlToIgnore = mTestPage + "?bar=1";
sActivityTestRule.loadUrl(urlToIgnore);
long offlineIdToIgnore =
savePage(
SavePageResult.SUCCESS,
urlToIgnore,
new ClientId(OfflinePageBridge.ASYNC_NAMESPACE, "-42"));
List<OfflinePageItem> pages = getPagesByNamespace(OfflinePageBridge.DOWNLOAD_NAMESPACE);
Assert.assertEquals(
"The number of pages returned does not match the number of pages saved.",
offlineIdsToFetch.size(),
pages.size());
for (OfflinePageItem page : pages) {
offlineIdsToFetch.remove(page.getOfflineId());
}
Assert.assertEquals(
"There were different pages saved than those returned by getPagesByNamespace.",
0,
offlineIdsToFetch.size());
// Check that the page in the other namespace still exists.
List<OfflinePageItem> asyncPages = getPagesByNamespace(OfflinePageBridge.ASYNC_NAMESPACE);
Assert.assertEquals(
"The page saved in an alternate namespace is no longer there.",
1,
asyncPages.size());
Assert.assertEquals(
"The offline ID of the page saved in an alternate namespace does not match.",
offlineIdToIgnore,
asyncPages.get(0).getOfflineId());
}
@Test
@MediumTest
public void testDownloadPage() throws Exception {
final OfflinePageOrigin origin =
new OfflinePageOrigin("abc.xyz", new String[] {"deadbeef"});
sActivityTestRule.loadUrl(mTestPage);
final String originString = origin.encodeAsJsonString();
final Semaphore semaphore = new Semaphore(0);
ThreadUtils.runOnUiThreadBlocking(
() -> {
Assert.assertNotNull(
"Tab is null", sActivityTestRule.getActivity().getActivityTab());
Assert.assertEquals(
"URL does not match requested.",
mTestPage,
sActivityTestRule.getActivity().getActivityTab().getUrl().getSpec());
Assert.assertNotNull("WebContents is null", sActivityTestRule.getWebContents());
mOfflinePageBridge.addObserver(
new OfflinePageModelObserver() {
@Override
public void offlinePageAdded(OfflinePageItem newPage) {
mOfflinePageBridge.removeObserver(this);
semaphore.release();
}
});
OfflinePageDownloadBridge.startDownload(
sActivityTestRule.getActivity().getActivityTab(), origin);
});
Assert.assertTrue(
"Semaphore acquire failed. Timed out.",
semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
List<OfflinePageItem> pages = OfflineTestUtil.getAllPages();
Assert.assertEquals(originString, pages.get(0).getRequestOrigin());
}
@Test
@MediumTest
public void testSavePageWithRequestOrigin() throws Exception {
final OfflinePageOrigin origin =
new OfflinePageOrigin("abc.xyz", new String[] {"deadbeef"});
sActivityTestRule.loadUrl(mTestPage);
final String originString = origin.encodeAsJsonString();
final Semaphore semaphore = new Semaphore(0);
ThreadUtils.runOnUiThreadBlocking(
() -> {
mOfflinePageBridge.addObserver(
new OfflinePageModelObserver() {
@Override
public void offlinePageAdded(OfflinePageItem newPage) {
mOfflinePageBridge.removeObserver(this);
semaphore.release();
}
});
mOfflinePageBridge.savePage(
sActivityTestRule.getWebContents(),
TEST_CLIENT_ID,
origin,
new SavePageCallback() {
@Override
public void onSavePageDone(
int savePageResult, String url, long offlineId) {}
});
});
Assert.assertTrue(
"Semaphore acquire failed. Timed out.",
semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
List<OfflinePageItem> pages = OfflineTestUtil.getAllPages();
Assert.assertEquals(originString, pages.get(0).getRequestOrigin());
}
@Test
@MediumTest
@DisabledTest(message = "crbug.com/842801")
public void testSavePageNoOrigin() throws Exception {
sActivityTestRule.loadUrl(mTestPage);
savePage(SavePageResult.SUCCESS, mTestPage);
List<OfflinePageItem> pages = OfflineTestUtil.getAllPages();
Assert.assertEquals("", pages.get(0).getRequestOrigin());
}
@Test
@MediumTest
@MinAndroidSdkLevel(
value = VERSION_CODES.R,
reason = "OfflinePage File Path is content uri on R+")
// TODO: expand this test to match testGetLoadUrlParamsForOpeningMhtmlFileUrl_File.
public void testGetLoadUrlParamsForOpeningMhtmlFileUrl_ContentUri() throws Exception {
sActivityTestRule.loadUrl(mTestPage);
savePage(SavePageResult.SUCCESS, mTestPage);
List<OfflinePageItem> allPages = OfflineTestUtil.getAllPages();
Assert.assertEquals(1, allPages.size());
OfflinePageItem offlinePage = allPages.get(0);
// The content URL pointing to the archive file should be replaced with http/https URL of
// the offline page.
String contentUrl = offlinePage.getFilePath();
LoadUrlParams loadUrlParams = getLoadUrlParamsForOpeningMhtmlFileOrContent(contentUrl);
Assert.assertEquals(offlinePage.getUrl(), loadUrlParams.getUrl());
String extraHeaders = loadUrlParams.getVerbatimHeaders();
Assert.assertNotNull(extraHeaders);
Assert.assertNotEquals(-1, extraHeaders.indexOf("reason=content_url_intent"));
Assert.assertNotEquals(
"intent_url field not found in header: " + extraHeaders,
-1,
extraHeaders.indexOf(
"intent_url="
+ Base64.encodeToString(
ApiCompatibilityUtils.getBytesUtf8(contentUrl),
Base64.NO_WRAP)));
Assert.assertNotEquals(
-1, extraHeaders.indexOf("id=" + Long.toString(offlinePage.getOfflineId())));
}
@Test
@MediumTest
@MaxAndroidSdkLevel(
value = VERSION_CODES.Q,
reason = "OfflinePage File Path is content uri on R+")
public void testGetLoadUrlParamsForOpeningMhtmlFileUrl_File() throws Exception {
sActivityTestRule.loadUrl(mTestPage);
savePage(SavePageResult.SUCCESS, mTestPage);
List<OfflinePageItem> allPages = OfflineTestUtil.getAllPages();
Assert.assertEquals(1, allPages.size());
OfflinePageItem offlinePage = allPages.get(0);
File archiveFile = new File(offlinePage.getFilePath());
// The file URL pointing to the archive file should be replaced with http/https URL of the
// offline page.
String fileUrl = Uri.fromFile(archiveFile).toString();
LoadUrlParams loadUrlParams = getLoadUrlParamsForOpeningMhtmlFileOrContent(fileUrl);
Assert.assertEquals(offlinePage.getUrl(), loadUrlParams.getUrl());
String extraHeaders = loadUrlParams.getVerbatimHeaders();
Assert.assertNotNull(extraHeaders);
Assert.assertNotEquals(-1, extraHeaders.indexOf("reason=file_url_intent"));
Assert.assertNotEquals(
"intent_url field not found in header: " + extraHeaders,
-1,
extraHeaders.indexOf(
"intent_url="
+ Base64.encodeToString(
ApiCompatibilityUtils.getBytesUtf8(fileUrl),
Base64.NO_WRAP)));
Assert.assertNotEquals(
-1, extraHeaders.indexOf("id=" + Long.toString(offlinePage.getOfflineId())));
// Make a copy of the original archive file.
File tempFile = File.createTempFile("Test", "");
copyFile(archiveFile, tempFile);
// The file URL pointing to file copy should also be replaced with http/https URL of the
// offline page.
String tempFileUrl = Uri.fromFile(tempFile).toString();
loadUrlParams = getLoadUrlParamsForOpeningMhtmlFileOrContent(tempFileUrl);
Assert.assertEquals(offlinePage.getUrl(), loadUrlParams.getUrl());
extraHeaders = loadUrlParams.getVerbatimHeaders();
Assert.assertNotNull(extraHeaders);
Assert.assertNotEquals(
"reason field not found in header: " + extraHeaders,
-1,
extraHeaders.indexOf("reason=file_url_intent"));
Assert.assertNotEquals(
"intent_url field not found in header: " + extraHeaders,
-1,
extraHeaders.indexOf(
"intent_url="
+ Base64.encodeToString(
ApiCompatibilityUtils.getBytesUtf8(tempFileUrl),
Base64.NO_WRAP)));
Assert.assertNotEquals(
"id field not found in header: " + extraHeaders,
-1,
extraHeaders.indexOf("id=" + Long.toString(offlinePage.getOfflineId())));
// Modify the copied file.
FileChannel tempFileChannel = new FileOutputStream(tempFile, true).getChannel();
tempFileChannel.truncate(10);
tempFileChannel.close();
// The file URL pointing to modified file copy should still get the file URL.
loadUrlParams = getLoadUrlParamsForOpeningMhtmlFileOrContent(tempFileUrl);
Assert.assertEquals(tempFileUrl, loadUrlParams.getUrl());
extraHeaders = loadUrlParams.getVerbatimHeaders();
Assert.assertNull(extraHeaders);
// Cleans up.
Assert.assertTrue(tempFile.delete());
}
// Returns offline ID.
private long savePage(final int expectedResult, final String expectedUrl)
throws InterruptedException {
return savePage(expectedResult, expectedUrl, TEST_CLIENT_ID);
}
// Returns offline ID.
private long savePage(
final int expectedResult, final String expectedUrl, final ClientId clientId)
throws InterruptedException {
final Semaphore semaphore = new Semaphore(0);
final AtomicLong result = new AtomicLong(-1);
ThreadUtils.runOnUiThreadBlocking(
() -> {
Assert.assertNotNull(
"Tab is null", sActivityTestRule.getActivity().getActivityTab());
Assert.assertEquals(
"URL does not match requested.",
expectedUrl,
sActivityTestRule.getActivity().getActivityTab().getUrl().getSpec());
Assert.assertNotNull("WebContents is null", sActivityTestRule.getWebContents());
mOfflinePageBridge.savePage(
sActivityTestRule.getWebContents(),
clientId,
new SavePageCallback() {
@Override
public void onSavePageDone(
int savePageResult, String url, long offlineId) {
Assert.assertEquals(
"Requested and returned URLs differ.",
expectedUrl,
url);
Assert.assertEquals(
"Save result incorrect.",
expectedResult,
savePageResult);
result.set(offlineId);
semaphore.release();
}
});
});
Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
return result.get();
}
private void deletePages(final List<Long> offlineIds) throws InterruptedException {
final Semaphore semaphore = new Semaphore(0);
PostTask.runOrPostTask(
TaskTraits.UI_DEFAULT,
() -> {
mOfflinePageBridge.deletePagesByOfflineId(
offlineIds,
new Callback<Integer>() {
@Override
public void onResult(Integer deletePageResult) {
semaphore.release();
}
});
});
Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
private void deletePage(final ClientId bookmarkId, final int expectedResult)
throws InterruptedException {
final Semaphore semaphore = new Semaphore(0);
final AtomicInteger deletePageResultRef = new AtomicInteger();
ThreadUtils.runOnUiThreadBlocking(
() -> {
mOfflinePageBridge.deletePage(
bookmarkId,
new Callback<Integer>() {
@Override
public void onResult(Integer deletePageResult) {
deletePageResultRef.set(deletePageResult.intValue());
semaphore.release();
}
});
});
Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
Assert.assertEquals("Delete result incorrect.", expectedResult, deletePageResultRef.get());
}
private List<OfflinePageItem> getPagesByNamespace(final String namespace)
throws InterruptedException {
final List<OfflinePageItem> result = new ArrayList<OfflinePageItem>();
final Semaphore semaphore = new Semaphore(0);
PostTask.runOrPostTask(
TaskTraits.UI_DEFAULT,
() -> {
mOfflinePageBridge.getPagesByNamespace(
namespace,
new Callback<List<OfflinePageItem>>() {
@Override
public void onResult(List<OfflinePageItem> pages) {
result.addAll(pages);
semaphore.release();
}
});
});
Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
return result;
}
private void forceConnectivityStateOnUiThread(final boolean state) {
ThreadUtils.runOnUiThreadBlocking(
() -> {
NetworkChangeNotifier.forceConnectivityState(state);
});
}
private Set<String> getUrlsExistOfflineFromSet(final Set<String> query)
throws TimeoutException {
final Set<String> result = new HashSet<>();
final List<OfflinePageItem> pages = OfflineTestUtil.getAllPages();
for (String url : query) {
for (OfflinePageItem page : pages) {
if (url.equals(page.getUrl())) {
result.add(page.getUrl());
}
}
}
return result;
}
private LoadUrlParams getLoadUrlParamsForOpeningMhtmlFileOrContent(String url)
throws InterruptedException {
final AtomicReference<LoadUrlParams> ref = new AtomicReference<>();
final Semaphore semaphore = new Semaphore(0);
PostTask.runOrPostTask(
TaskTraits.UI_DEFAULT,
() -> {
mOfflinePageBridge.getLoadUrlParamsForOpeningMhtmlFileOrContent(
url,
(loadUrlParams) -> {
ref.set(loadUrlParams);
semaphore.release();
});
});
Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
return ref.get();
}
private static void copyFile(File source, File dest) throws IOException {
FileChannel inputChannel = null;
FileChannel outputChannel = null;
try {
inputChannel = new FileInputStream(source).getChannel();
outputChannel = new FileOutputStream(dest).getChannel();
outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
} finally {
inputChannel.close();
outputChannel.close();
}
}
}