зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1232984 - Add basic set of tests for downloadable content. r=rnewman
--HG-- extra : commitid : 98BvL0dXkuv extra : rebase_source : 3ddf99f5979e3a05ff5b1049d5be6ae4569c22ae
This commit is contained in:
Родитель
8e726581e9
Коммит
8699452fa5
|
@ -123,6 +123,7 @@ dependencies {
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
testCompile 'org.robolectric:robolectric:3.0'
|
testCompile 'org.robolectric:robolectric:3.0'
|
||||||
testCompile 'org.simpleframework:simple-http:4.1.13'
|
testCompile 'org.simpleframework:simple-http:4.1.13'
|
||||||
|
testCompile 'org.mockito:mockito-core:1.10.19'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
|
|
|
@ -73,6 +73,7 @@ public class DownloadAction extends BaseAction {
|
||||||
File destinationFile = getDestinationFile(context, content);
|
File destinationFile = getDestinationFile(context, content);
|
||||||
if (destinationFile.exists() && verify(destinationFile, content.getChecksum())) {
|
if (destinationFile.exists() && verify(destinationFile, content.getChecksum())) {
|
||||||
Log.d(LOGTAG, "Content already exists and is up-to-date.");
|
Log.d(LOGTAG, "Content already exists and is up-to-date.");
|
||||||
|
catalog.markAsDownloaded(content);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,10 @@ public class DownloadContent {
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getLastModified() {
|
||||||
|
return lastModified;
|
||||||
|
}
|
||||||
|
|
||||||
public String getFilename() {
|
public String getFilename() {
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
|
|
||||||
package org.mozilla.gecko.dlc.catalog;
|
package org.mozilla.gecko.dlc.catalog;
|
||||||
|
|
||||||
import org.mozilla.gecko.dlc.catalog.DownloadContentBootstrap;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.v4.util.AtomicFile;
|
import android.support.v4.util.AtomicFile;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -39,12 +37,17 @@ public class DownloadContentCatalog {
|
||||||
private boolean hasCatalogChanged; // Guarded by 'this'
|
private boolean hasCatalogChanged; // Guarded by 'this'
|
||||||
|
|
||||||
public DownloadContentCatalog(Context context) {
|
public DownloadContentCatalog(Context context) {
|
||||||
content = Collections.emptyList();
|
this(new AtomicFile(new File(context.getApplicationInfo().dataDir, FILE_NAME)));
|
||||||
file = new AtomicFile(new File(context.getApplicationInfo().dataDir, FILE_NAME));
|
|
||||||
|
|
||||||
startLoadFromDisk();
|
startLoadFromDisk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For injecting mocked AtomicFile objects during test
|
||||||
|
protected DownloadContentCatalog(AtomicFile file) {
|
||||||
|
this.content = Collections.emptyList();
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized List<DownloadContent> getContentWithoutState() {
|
public synchronized List<DownloadContent> getContentWithoutState() {
|
||||||
awaitLoadingCatalogLocked();
|
awaitLoadingCatalogLocked();
|
||||||
|
|
||||||
|
@ -145,14 +148,18 @@ public class DownloadContentCatalog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void loadFromDisk() {
|
protected synchronized boolean hasCatalogChanged() {
|
||||||
|
return hasCatalogChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void loadFromDisk() {
|
||||||
Log.d(LOGTAG, "Loading from disk");
|
Log.d(LOGTAG, "Loading from disk");
|
||||||
|
|
||||||
if (hasLoadedCatalog) {
|
if (hasLoadedCatalog) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DownloadContent> content = new ArrayList<DownloadContent>();
|
List<DownloadContent> content = new ArrayList<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JSONArray array;
|
JSONArray array;
|
||||||
|
@ -180,15 +187,19 @@ public class DownloadContentCatalog {
|
||||||
Log.d(LOGTAG, "Can't read catalog due to IOException", e);
|
Log.d(LOGTAG, "Can't read catalog due to IOException", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.content = content;
|
onCatalogLoaded(content);
|
||||||
this.hasLoadedCatalog = true;
|
|
||||||
|
|
||||||
notifyAll();
|
notifyAll();
|
||||||
|
|
||||||
Log.d(LOGTAG, "Loaded " + content.size() + " elements");
|
Log.d(LOGTAG, "Loaded " + content.size() + " elements");
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void writeToDisk() {
|
protected void onCatalogLoaded(List<DownloadContent> content) {
|
||||||
|
this.content = content;
|
||||||
|
this.hasLoadedCatalog = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void writeToDisk() {
|
||||||
if (!hasCatalogChanged) {
|
if (!hasCatalogChanged) {
|
||||||
Log.v(LOGTAG, "Not persisting: Catalog has not changed");
|
Log.v(LOGTAG, "Not persisting: Catalog has not changed");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.dlc;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mozilla.gecko.background.testhelpers.TestRunner;
|
||||||
|
import org.mozilla.gecko.dlc.catalog.DownloadContent;
|
||||||
|
import org.mozilla.gecko.dlc.catalog.DownloadContentCatalog;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||||
|
import ch.boye.httpclientandroidlib.StatusLine;
|
||||||
|
import ch.boye.httpclientandroidlib.client.HttpClient;
|
||||||
|
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
|
||||||
|
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DownloadAction: Download content that has been scheduled during "study" or "verify".
|
||||||
|
*/
|
||||||
|
@RunWith(TestRunner.class)
|
||||||
|
public class TestDownloadAction {
|
||||||
|
private static final String TEST_URL = "http://example.org";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: The current network is metered.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * No download is performed on a metered network
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testNothingIsDoneOnMeteredNetwork() throws Exception {
|
||||||
|
DownloadAction action = spy(new DownloadAction(null));
|
||||||
|
doReturn(true).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
|
||||||
|
|
||||||
|
action.perform(RuntimeEnvironment.application, null);
|
||||||
|
|
||||||
|
verify(action, never()).buildHttpClient();
|
||||||
|
verify(action, never()).download(any(HttpClient.class), anyString(), any(File.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Content is scheduled for download but already exists locally (with correct checksum).
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * No download is performed for existing file
|
||||||
|
* * Content is marked as downloaded in the catalog
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testExistingAndVerifiedFilesAreNotDownloadedAgain() throws Exception {
|
||||||
|
DownloadContent content = new DownloadContent.Builder().build();
|
||||||
|
|
||||||
|
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
|
||||||
|
doReturn(Collections.singletonList(content)).when(catalog).getScheduledDownloads();
|
||||||
|
|
||||||
|
DownloadAction action = spy(new DownloadAction(null));
|
||||||
|
doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
|
||||||
|
|
||||||
|
File file = mock(File.class);
|
||||||
|
doReturn(true).when(file).exists();
|
||||||
|
doReturn(file).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
|
||||||
|
doReturn(file).when(action).getDestinationFile(RuntimeEnvironment.application, content);
|
||||||
|
doReturn(true).when(action).verify(eq(file), anyString());
|
||||||
|
|
||||||
|
action.perform(RuntimeEnvironment.application, catalog);
|
||||||
|
|
||||||
|
verify(action, never()).download(any(HttpClient.class), anyString(), any(File.class));
|
||||||
|
verify(catalog).markAsDownloaded(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Server returns a server error (HTTP 500).
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Situation is treated as recoverable (RecoverableDownloadContentException)
|
||||||
|
*/
|
||||||
|
@Test(expected=BaseAction.RecoverableDownloadContentException.class)
|
||||||
|
public void testServerErrorsAreRecoverable() throws Exception {
|
||||||
|
StatusLine status = mock(StatusLine.class);
|
||||||
|
doReturn(500).when(status).getStatusCode();
|
||||||
|
|
||||||
|
HttpResponse response = mock(HttpResponse.class);
|
||||||
|
doReturn(status).when(response).getStatusLine();
|
||||||
|
|
||||||
|
HttpClient client = mock(HttpClient.class);
|
||||||
|
doReturn(response).when(client).execute(any(HttpUriRequest.class));
|
||||||
|
|
||||||
|
DownloadAction action = spy(new DownloadAction(null));
|
||||||
|
action.download(client, TEST_URL, null);
|
||||||
|
|
||||||
|
verify(client).execute(any(HttpUriRequest.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Server returns a client error (HTTP 404).
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Situation is treated as unrecoverable (UnrecoverableDownloadContentException)
|
||||||
|
*/
|
||||||
|
@Test(expected=BaseAction.UnrecoverableDownloadContentException.class)
|
||||||
|
public void testClientErrorsAreUnrecoverable() throws Exception {
|
||||||
|
StatusLine status = mock(StatusLine.class);
|
||||||
|
doReturn(404).when(status).getStatusCode();
|
||||||
|
|
||||||
|
HttpResponse response = mock(HttpResponse.class);
|
||||||
|
doReturn(status).when(response).getStatusLine();
|
||||||
|
|
||||||
|
HttpClient client = mock(HttpClient.class);
|
||||||
|
doReturn(response).when(client).execute(any(HttpUriRequest.class));
|
||||||
|
|
||||||
|
DownloadAction action = spy(new DownloadAction(null));
|
||||||
|
action.download(client, TEST_URL, null);
|
||||||
|
|
||||||
|
verify(client).execute(any(HttpUriRequest.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: A successful download has been performed.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * The content will be extracted to the destination
|
||||||
|
* * The content is marked as downloaded in the catalog
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSuccessfulDownloadsAreMarkedAsDownloaded() throws Exception {
|
||||||
|
DownloadContent content = new DownloadContent.Builder()
|
||||||
|
.setKind(DownloadContent.KIND_FONT)
|
||||||
|
.setType(DownloadContent.TYPE_ASSET_ARCHIVE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
|
||||||
|
doReturn(Collections.singletonList(content)).when(catalog).getScheduledDownloads();
|
||||||
|
|
||||||
|
DownloadAction action = spy(new DownloadAction(null));
|
||||||
|
doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
|
||||||
|
|
||||||
|
File file = mock(File.class);
|
||||||
|
doReturn(false).when(file).exists();
|
||||||
|
doReturn(file).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
|
||||||
|
doReturn(file).when(action).getDestinationFile(RuntimeEnvironment.application, content);
|
||||||
|
|
||||||
|
doReturn(false).when(action).verify(eq(file), anyString());
|
||||||
|
doNothing().when(action).download(any(HttpClient.class), anyString(), eq(file));
|
||||||
|
doReturn(true).when(action).verify(eq(file), anyString());
|
||||||
|
doNothing().when(action).extract(eq(file), eq(file), anyString());
|
||||||
|
|
||||||
|
action.perform(RuntimeEnvironment.application, catalog);
|
||||||
|
|
||||||
|
verify(action).buildHttpClient();
|
||||||
|
verify(action).download(any(HttpClient.class), anyString(), eq(file));
|
||||||
|
verify(action).extract(eq(file), eq(file), anyString());
|
||||||
|
verify(catalog).markAsDownloaded(content);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.dlc;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mozilla.gecko.background.testhelpers.TestRunner;
|
||||||
|
import org.mozilla.gecko.dlc.catalog.DownloadContent;
|
||||||
|
import org.mozilla.gecko.dlc.catalog.DownloadContentCatalog;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StudyAction: Scan the catalog for "new" content available for download.
|
||||||
|
*/
|
||||||
|
@RunWith(TestRunner.class)
|
||||||
|
public class TestStudyAction {
|
||||||
|
/**
|
||||||
|
* Scenario: Catalog is empty.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * No download is scheduled
|
||||||
|
* * Download action is not started
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testPerformWithEmptyCatalog() {
|
||||||
|
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
|
||||||
|
when(catalog.getContentWithoutState()).thenReturn(new ArrayList<DownloadContent>());
|
||||||
|
|
||||||
|
StudyAction action = spy(new StudyAction());
|
||||||
|
action.perform(RuntimeEnvironment.application, catalog);
|
||||||
|
|
||||||
|
verify(catalog).getContentWithoutState();
|
||||||
|
verify(catalog, never()).markAsDownloaded(any(DownloadContent.class));
|
||||||
|
verify(action, never()).startDownloads(any(Context.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Catalog contains two items that have not been downloaded yet.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Both items are scheduled to be downloaded
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testPerformWithNewContent() {
|
||||||
|
DownloadContent content1 = new DownloadContent.Builder()
|
||||||
|
.setType(DownloadContent.TYPE_ASSET_ARCHIVE)
|
||||||
|
.setKind(DownloadContent.KIND_FONT)
|
||||||
|
.build();
|
||||||
|
DownloadContent content2 = new DownloadContent.Builder()
|
||||||
|
.setType(DownloadContent.TYPE_ASSET_ARCHIVE)
|
||||||
|
.setKind(DownloadContent.KIND_FONT)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
|
||||||
|
when(catalog.getContentWithoutState()).thenReturn(Arrays.asList(content1, content2));
|
||||||
|
|
||||||
|
StudyAction action = spy(new StudyAction());
|
||||||
|
action.perform(RuntimeEnvironment.application, catalog);
|
||||||
|
|
||||||
|
verify(catalog).scheduleDownload(content1);
|
||||||
|
verify(catalog).scheduleDownload(content2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Catalog contains item that are scheduled for download.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Download action is started
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testStartingDownloadsAfterScheduling() {
|
||||||
|
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
|
||||||
|
when(catalog.hasScheduledDownloads()).thenReturn(true);
|
||||||
|
|
||||||
|
StudyAction action = spy(new StudyAction());
|
||||||
|
action.perform(RuntimeEnvironment.application, catalog);
|
||||||
|
|
||||||
|
verify(action).startDownloads(any(Context.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Catalog contains unknown content.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Unknown content is not scheduled for download.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testPerformWithUnknownContent() {
|
||||||
|
DownloadContent content = new DownloadContent.Builder()
|
||||||
|
.setType("Unknown-Type")
|
||||||
|
.setKind("Unknown-Kind")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
|
||||||
|
when(catalog.getContentWithoutState()).thenReturn(Collections.singletonList(content));
|
||||||
|
|
||||||
|
StudyAction action = spy(new StudyAction());
|
||||||
|
action.perform(RuntimeEnvironment.application, catalog);
|
||||||
|
|
||||||
|
verify(catalog, never()).scheduleDownload(content);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.dlc;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mozilla.gecko.background.testhelpers.TestRunner;
|
||||||
|
import org.mozilla.gecko.dlc.catalog.DownloadContent;
|
||||||
|
import org.mozilla.gecko.dlc.catalog.DownloadContentCatalog;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VerifyAction: Validate downloaded content. Does it still exist and does it have the correct checksum?
|
||||||
|
*/
|
||||||
|
@RunWith(TestRunner.class)
|
||||||
|
public class TestVerifyAction {
|
||||||
|
/**
|
||||||
|
* Scenario: Downloaded file does not exist anymore.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Content is re-scheduled for download.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testReschedulingIfFileDoesNotExist() throws Exception {
|
||||||
|
DownloadContent content = new DownloadContent.Builder().build();
|
||||||
|
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
|
||||||
|
when(catalog.getDownloadedContent()).thenReturn(Collections.singletonList(content));
|
||||||
|
|
||||||
|
File file = mock(File.class);
|
||||||
|
when(file.exists()).thenReturn(false);
|
||||||
|
|
||||||
|
VerifyAction action = spy(new VerifyAction());
|
||||||
|
doReturn(file).when(action).getDestinationFile(RuntimeEnvironment.application, content);
|
||||||
|
|
||||||
|
action.perform(RuntimeEnvironment.application, catalog);
|
||||||
|
|
||||||
|
verify(catalog).scheduleDownload(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Content has been scheduled for download.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Download action is started
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testStartingDownloadsAfterScheduling() {
|
||||||
|
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
|
||||||
|
when(catalog.hasScheduledDownloads()).thenReturn(true);
|
||||||
|
|
||||||
|
VerifyAction action = spy(new VerifyAction());
|
||||||
|
action.perform(RuntimeEnvironment.application, catalog);
|
||||||
|
|
||||||
|
verify(action).startDownloads(any(Context.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Checksum of existing file does not match expectation.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Content is re-scheduled for download.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testReschedulingIfVerificationFailed() throws Exception {
|
||||||
|
DownloadContent content = new DownloadContent.Builder().build();
|
||||||
|
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
|
||||||
|
when(catalog.getDownloadedContent()).thenReturn(Collections.singletonList(content));
|
||||||
|
|
||||||
|
File file = mock(File.class);
|
||||||
|
when(file.exists()).thenReturn(true);
|
||||||
|
|
||||||
|
VerifyAction action = spy(new VerifyAction());
|
||||||
|
doReturn(file).when(action).getDestinationFile(RuntimeEnvironment.application, content);
|
||||||
|
doReturn(false).when(action).verify(eq(file), anyString());
|
||||||
|
|
||||||
|
action.perform(RuntimeEnvironment.application, catalog);
|
||||||
|
|
||||||
|
verify(catalog).scheduleDownload(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Downloaded file exists and has the correct checksum.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * No download is scheduled
|
||||||
|
* * Download action is not started
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSuccessfulVerification() throws Exception {
|
||||||
|
DownloadContent content = new DownloadContent.Builder().build();
|
||||||
|
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
|
||||||
|
when(catalog.getDownloadedContent()).thenReturn(Collections.singletonList(content));
|
||||||
|
|
||||||
|
File file = mock(File.class);
|
||||||
|
when(file.exists()).thenReturn(true);
|
||||||
|
|
||||||
|
VerifyAction action = spy(new VerifyAction());
|
||||||
|
doReturn(file).when(action).getDestinationFile(RuntimeEnvironment.application, content);
|
||||||
|
doReturn(true).when(action).verify(eq(file), anyString());
|
||||||
|
|
||||||
|
verify(catalog, never()).scheduleDownload(content);
|
||||||
|
verify(action, never()).startDownloads(RuntimeEnvironment.application);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
package org.mozilla.gecko.dlc.catalog;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mozilla.gecko.background.testhelpers.TestRunner;
|
||||||
|
|
||||||
|
@RunWith(TestRunner.class)
|
||||||
|
public class TestDownloadContent {
|
||||||
|
/**
|
||||||
|
* Verify that the values passed to the builder are all set on the DownloadContent object.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testBuilder() {
|
||||||
|
DownloadContent content = createTestContent();
|
||||||
|
|
||||||
|
Assert.assertEquals("Some-ID", content.getId());
|
||||||
|
Assert.assertEquals("/somewhere/something", content.getLocation());
|
||||||
|
Assert.assertEquals("some.file", content.getFilename());
|
||||||
|
Assert.assertEquals("Some-checksum", content.getChecksum());
|
||||||
|
Assert.assertEquals("Some-download-checksum", content.getDownloadChecksum());
|
||||||
|
Assert.assertEquals(4223, content.getLastModified());
|
||||||
|
Assert.assertEquals("Some-type", content.getType());
|
||||||
|
Assert.assertEquals("Some-kind", content.getKind());
|
||||||
|
Assert.assertEquals(27, content.getSize());
|
||||||
|
Assert.assertEquals(DownloadContent.STATE_SCHEDULED, content.getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that a DownloadContent object exported to JSON and re-imported from JSON does not change.
|
||||||
|
*/
|
||||||
|
public void testJSONSerializationAndDeserialization() throws JSONException {
|
||||||
|
DownloadContent content = DownloadContent.fromJSON(createTestContent().toJSON());
|
||||||
|
|
||||||
|
Assert.assertEquals("Some-ID", content.getId());
|
||||||
|
Assert.assertEquals("/somewhere/something", content.getLocation());
|
||||||
|
Assert.assertEquals("some.file", content.getFilename());
|
||||||
|
Assert.assertEquals("Some-checksum", content.getChecksum());
|
||||||
|
Assert.assertEquals("Some-download-checksum", content.getDownloadChecksum());
|
||||||
|
Assert.assertEquals(4223, content.getLastModified());
|
||||||
|
Assert.assertEquals("Some-type", content.getType());
|
||||||
|
Assert.assertEquals("Some-kind", content.getKind());
|
||||||
|
Assert.assertEquals(27, content.getSize());
|
||||||
|
Assert.assertEquals(DownloadContent.STATE_SCHEDULED, content.getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a DownloadContent object with arbitrary data.
|
||||||
|
*/
|
||||||
|
private DownloadContent createTestContent() {
|
||||||
|
return new DownloadContent.Builder()
|
||||||
|
.setId("Some-ID")
|
||||||
|
.setLocation("/somewhere/something")
|
||||||
|
.setFilename("some.file")
|
||||||
|
.setChecksum("Some-checksum")
|
||||||
|
.setDownloadChecksum("Some-download-checksum")
|
||||||
|
.setLastModified(4223)
|
||||||
|
.setType("Some-type")
|
||||||
|
.setKind("Some-kind")
|
||||||
|
.setSize(27)
|
||||||
|
.setState(DownloadContent.STATE_SCHEDULED)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.dlc.catalog;
|
||||||
|
|
||||||
|
import android.support.v4.util.AtomicFile;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mozilla.gecko.background.testhelpers.TestRunner;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
@RunWith(TestRunner.class)
|
||||||
|
public class TestDownloadContentCatalog {
|
||||||
|
/**
|
||||||
|
* Scenario: Create a new, fresh catalog.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Catalog has not changed
|
||||||
|
* * Unchanged catalog will not be saved to disk
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testUntouchedCatalogHasNotChangedAndWillNotBePersisted() throws Exception {
|
||||||
|
AtomicFile file = mock(AtomicFile.class);
|
||||||
|
doReturn("[]".getBytes("UTF-8")).when(file).readFully();
|
||||||
|
|
||||||
|
DownloadContentCatalog catalog = spy(new DownloadContentCatalog(file));
|
||||||
|
catalog.loadFromDisk();
|
||||||
|
|
||||||
|
Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged());
|
||||||
|
|
||||||
|
catalog.writeToDisk();
|
||||||
|
|
||||||
|
Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged());
|
||||||
|
|
||||||
|
verify(file, never()).startWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Create a new, fresh catalog.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Catalog is bootstrapped with items.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCatalogIsBootstrapedIfFileDoesNotExist() throws Exception {
|
||||||
|
AtomicFile file = mock(AtomicFile.class);
|
||||||
|
doThrow(FileNotFoundException.class).when(file).readFully();
|
||||||
|
|
||||||
|
DownloadContentCatalog catalog = spy(new DownloadContentCatalog(file));
|
||||||
|
catalog.loadFromDisk();
|
||||||
|
|
||||||
|
Assert.assertTrue("Catalog is not empty", catalog.getContentWithoutState().size() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Schedule downloading an item from the catalog.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Catalog has changed
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCatalogHasChangedWhenDownloadIsScheduled() throws Exception {
|
||||||
|
DownloadContentCatalog catalog = spy(new DownloadContentCatalog(mock(AtomicFile.class)));
|
||||||
|
DownloadContent content = new DownloadContent.Builder().build();
|
||||||
|
catalog.onCatalogLoaded(Collections.singletonList(content));
|
||||||
|
|
||||||
|
Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged());
|
||||||
|
|
||||||
|
catalog.scheduleDownload(content);
|
||||||
|
|
||||||
|
Assert.assertTrue("Catalog has changed", catalog.hasCatalogChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Mark an item in the catalog as downloaded.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Catalog has changed
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCatalogHasChangedWhenContentIsDownloaded() throws Exception {
|
||||||
|
DownloadContentCatalog catalog = spy(new DownloadContentCatalog(mock(AtomicFile.class)));
|
||||||
|
DownloadContent content = new DownloadContent.Builder().build();
|
||||||
|
catalog.onCatalogLoaded(Collections.singletonList(content));
|
||||||
|
|
||||||
|
Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged());
|
||||||
|
|
||||||
|
catalog.markAsDownloaded(content);
|
||||||
|
|
||||||
|
Assert.assertTrue("Catalog has changed", catalog.hasCatalogChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Mark an item in the catalog as permanently failed.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Catalog has changed
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCatalogHasChangedIfDownloadHasFailedPermanently() throws Exception {
|
||||||
|
DownloadContentCatalog catalog = spy(new DownloadContentCatalog(mock(AtomicFile.class)));
|
||||||
|
DownloadContent content = new DownloadContent.Builder().build();
|
||||||
|
catalog.onCatalogLoaded(Collections.singletonList(content));
|
||||||
|
|
||||||
|
Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged());
|
||||||
|
|
||||||
|
catalog.markAsPermanentlyFailed(content);
|
||||||
|
|
||||||
|
Assert.assertTrue("Catalog has changed", catalog.hasCatalogChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: Mark an item in the catalog as ignored.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Catalog has changed
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCatalogHasChangedIfContentIsIgnored() throws Exception {
|
||||||
|
DownloadContentCatalog catalog = spy(new DownloadContentCatalog(mock(AtomicFile.class)));
|
||||||
|
DownloadContent content = new DownloadContent.Builder().build();
|
||||||
|
catalog.onCatalogLoaded(Collections.singletonList(content));
|
||||||
|
|
||||||
|
Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged());
|
||||||
|
|
||||||
|
catalog.markAsIgnored(content);
|
||||||
|
|
||||||
|
Assert.assertTrue("Catalog has changed", catalog.hasCatalogChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: A changed catalog is written to disk.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * Before write: Catalog has changed
|
||||||
|
* * After write: Catalog has not changed.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCatalogHasNotChangedAfterWritingToDisk() throws Exception {
|
||||||
|
AtomicFile file = mock(AtomicFile.class);
|
||||||
|
doReturn(mock(FileOutputStream.class)).when(file).startWrite();
|
||||||
|
|
||||||
|
DownloadContentCatalog catalog = spy(new DownloadContentCatalog(file));
|
||||||
|
DownloadContent content = new DownloadContent.Builder().build();
|
||||||
|
catalog.onCatalogLoaded(Collections.singletonList(content));
|
||||||
|
|
||||||
|
catalog.scheduleDownload(content);
|
||||||
|
|
||||||
|
Assert.assertTrue("Catalog has changed", catalog.hasCatalogChanged());
|
||||||
|
|
||||||
|
catalog.writeToDisk();
|
||||||
|
|
||||||
|
Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: A catalog with multiple items in different states.
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * getContentWithoutState(), getDownloadedContent() and getScheduledDownloads() returns
|
||||||
|
* the correct items depenending on their state.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testContentClassification() {
|
||||||
|
DownloadContentCatalog catalog = spy(new DownloadContentCatalog(mock(AtomicFile.class)));
|
||||||
|
|
||||||
|
DownloadContent content1 = new DownloadContent.Builder().setState(DownloadContent.STATE_NONE).build();
|
||||||
|
DownloadContent content2 = new DownloadContent.Builder().setState(DownloadContent.STATE_NONE).build();
|
||||||
|
DownloadContent content3 = new DownloadContent.Builder().setState(DownloadContent.STATE_SCHEDULED).build();
|
||||||
|
DownloadContent content4 = new DownloadContent.Builder().setState(DownloadContent.STATE_SCHEDULED).build();
|
||||||
|
DownloadContent content5 = new DownloadContent.Builder().setState(DownloadContent.STATE_SCHEDULED).build();
|
||||||
|
DownloadContent content6 = new DownloadContent.Builder().setState(DownloadContent.STATE_DOWNLOADED).build();
|
||||||
|
DownloadContent content7 = new DownloadContent.Builder().setState(DownloadContent.STATE_FAILED).build();
|
||||||
|
DownloadContent content8 = new DownloadContent.Builder().setState(DownloadContent.STATE_IGNORED).build();
|
||||||
|
DownloadContent content9 = new DownloadContent.Builder().setState(DownloadContent.STATE_IGNORED).build();
|
||||||
|
|
||||||
|
|
||||||
|
catalog.onCatalogLoaded(Arrays.asList(content1, content2, content3, content4, content5, content6,
|
||||||
|
content7, content8, content9));
|
||||||
|
|
||||||
|
Assert.assertEquals(2, catalog.getContentWithoutState().size());
|
||||||
|
Assert.assertEquals(1, catalog.getDownloadedContent().size());
|
||||||
|
Assert.assertEquals(3, catalog.getScheduledDownloads().size());
|
||||||
|
|
||||||
|
Assert.assertTrue(catalog.getContentWithoutState().contains(content1));
|
||||||
|
Assert.assertTrue(catalog.getContentWithoutState().contains(content2));
|
||||||
|
|
||||||
|
Assert.assertTrue(catalog.getDownloadedContent().contains(content6));
|
||||||
|
|
||||||
|
Assert.assertTrue(catalog.getScheduledDownloads().contains(content3));
|
||||||
|
Assert.assertTrue(catalog.getScheduledDownloads().contains(content4));
|
||||||
|
Assert.assertTrue(catalog.getScheduledDownloads().contains(content5));
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче