зеркало из https://github.com/mozilla/gecko-dev.git
767 строки
23 KiB
JavaScript
767 строки
23 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
/**
|
|
* This file tests components that implement nsIBackgroundFileSaver.
|
|
*/
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// Globals
|
|
|
|
"use strict";
|
|
|
|
ChromeUtils.defineModuleGetter(
|
|
this,
|
|
"NetUtil",
|
|
"resource://gre/modules/NetUtil.jsm"
|
|
);
|
|
ChromeUtils.defineESModuleGetters(this, {
|
|
FileTestUtils: "resource://testing-common/FileTestUtils.sys.mjs",
|
|
});
|
|
|
|
const BackgroundFileSaverOutputStream = Components.Constructor(
|
|
"@mozilla.org/network/background-file-saver;1?mode=outputstream",
|
|
"nsIBackgroundFileSaver"
|
|
);
|
|
|
|
const BackgroundFileSaverStreamListener = Components.Constructor(
|
|
"@mozilla.org/network/background-file-saver;1?mode=streamlistener",
|
|
"nsIBackgroundFileSaver"
|
|
);
|
|
|
|
const StringInputStream = Components.Constructor(
|
|
"@mozilla.org/io/string-input-stream;1",
|
|
"nsIStringInputStream",
|
|
"setData"
|
|
);
|
|
|
|
const REQUEST_SUSPEND_AT = 1024 * 1024 * 4;
|
|
const TEST_DATA_SHORT = "This test string is written to the file.";
|
|
const TEST_FILE_NAME_1 = "test-backgroundfilesaver-1.txt";
|
|
const TEST_FILE_NAME_2 = "test-backgroundfilesaver-2.txt";
|
|
const TEST_FILE_NAME_3 = "test-backgroundfilesaver-3.txt";
|
|
|
|
// A map of test data length to the expected SHA-256 hashes
|
|
const EXPECTED_HASHES = {
|
|
// No data
|
|
0: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
// TEST_DATA_SHORT
|
|
40: "f37176b690e8744ee990a206c086cba54d1502aa2456c3b0c84ef6345d72a192",
|
|
// TEST_DATA_SHORT + TEST_DATA_SHORT
|
|
80: "780c0e91f50bb7ec922cc11e16859e6d5df283c0d9470f61772e3d79f41eeb58",
|
|
// TEST_DATA_LONG
|
|
4718592: "372cb9e5ce7b76d3e2a5042e78aa72dcf973e659a262c61b7ff51df74b36767b",
|
|
// TEST_DATA_LONG + TEST_DATA_LONG
|
|
9437184: "693e4f8c6855a6fed4f5f9370d12cc53105672f3ff69783581e7d925984c41d3",
|
|
};
|
|
|
|
// Generate a long string of data in a moderately fast way.
|
|
const TEST_256_CHARS = new Array(257).join("-");
|
|
const DESIRED_LENGTH = REQUEST_SUSPEND_AT * 1.125;
|
|
const TEST_DATA_LONG = new Array(1 + DESIRED_LENGTH / 256).join(TEST_256_CHARS);
|
|
Assert.equal(TEST_DATA_LONG.length, DESIRED_LENGTH);
|
|
|
|
/**
|
|
* Returns a reference to a temporary file that is guaranteed not to exist and
|
|
* is cleaned up later. See FileTestUtils.getTempFile for details.
|
|
*/
|
|
function getTempFile(leafName) {
|
|
return FileTestUtils.getTempFile(leafName);
|
|
}
|
|
|
|
/**
|
|
* Helper function for converting a binary blob to its hex equivalent.
|
|
*
|
|
* @param str
|
|
* String possibly containing non-printable chars.
|
|
* @return A hex-encoded string.
|
|
*/
|
|
function toHex(str) {
|
|
var hex = "";
|
|
for (var i = 0; i < str.length; i++) {
|
|
hex += ("0" + str.charCodeAt(i).toString(16)).slice(-2);
|
|
}
|
|
return hex;
|
|
}
|
|
|
|
/**
|
|
* Ensures that the given file contents are equal to the given string.
|
|
*
|
|
* @param aFile
|
|
* nsIFile whose contents should be verified.
|
|
* @param aExpectedContents
|
|
* String containing the octets that are expected in the file.
|
|
*
|
|
* @return {Promise}
|
|
* @resolves When the operation completes.
|
|
* @rejects Never.
|
|
*/
|
|
function promiseVerifyContents(aFile, aExpectedContents) {
|
|
return new Promise(resolve => {
|
|
NetUtil.asyncFetch(
|
|
{
|
|
uri: NetUtil.newURI(aFile),
|
|
loadUsingSystemPrincipal: true,
|
|
},
|
|
function(aInputStream, aStatus) {
|
|
Assert.ok(Components.isSuccessCode(aStatus));
|
|
let contents = NetUtil.readInputStreamToString(
|
|
aInputStream,
|
|
aInputStream.available()
|
|
);
|
|
if (contents.length <= TEST_DATA_SHORT.length * 2) {
|
|
Assert.equal(contents, aExpectedContents);
|
|
} else {
|
|
// Do not print the entire content string to the test log.
|
|
Assert.equal(contents.length, aExpectedContents.length);
|
|
Assert.ok(contents == aExpectedContents);
|
|
}
|
|
resolve();
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Waits for the given saver object to complete.
|
|
*
|
|
* @param aSaver
|
|
* The saver, with the output stream or a stream listener implementation.
|
|
* @param aOnTargetChangeFn
|
|
* Optional callback invoked with the target file name when it changes.
|
|
*
|
|
* @return {Promise}
|
|
* @resolves When onSaveComplete is called with a success code.
|
|
* @rejects With an exception, if onSaveComplete is called with a failure code.
|
|
*/
|
|
function promiseSaverComplete(aSaver, aOnTargetChangeFn) {
|
|
return new Promise((resolve, reject) => {
|
|
aSaver.observer = {
|
|
onTargetChange: function BFSO_onSaveComplete(aSaver, aTarget) {
|
|
if (aOnTargetChangeFn) {
|
|
aOnTargetChangeFn(aTarget);
|
|
}
|
|
},
|
|
onSaveComplete: function BFSO_onSaveComplete(aSaver, aStatus) {
|
|
if (Components.isSuccessCode(aStatus)) {
|
|
resolve();
|
|
} else {
|
|
reject(new Components.Exception("Saver failed.", aStatus));
|
|
}
|
|
},
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Feeds a string to a BackgroundFileSaverOutputStream.
|
|
*
|
|
* @param aSourceString
|
|
* The source data to copy.
|
|
* @param aSaverOutputStream
|
|
* The BackgroundFileSaverOutputStream to feed.
|
|
* @param aCloseWhenDone
|
|
* If true, the output stream will be closed when the copy finishes.
|
|
*
|
|
* @return {Promise}
|
|
* @resolves When the copy completes with a success code.
|
|
* @rejects With an exception, if the copy fails.
|
|
*/
|
|
function promiseCopyToSaver(aSourceString, aSaverOutputStream, aCloseWhenDone) {
|
|
return new Promise((resolve, reject) => {
|
|
let inputStream = new StringInputStream(
|
|
aSourceString,
|
|
aSourceString.length
|
|
);
|
|
let copier = Cc[
|
|
"@mozilla.org/network/async-stream-copier;1"
|
|
].createInstance(Ci.nsIAsyncStreamCopier);
|
|
copier.init(
|
|
inputStream,
|
|
aSaverOutputStream,
|
|
null,
|
|
false,
|
|
true,
|
|
0x8000,
|
|
true,
|
|
aCloseWhenDone
|
|
);
|
|
copier.asyncCopy(
|
|
{
|
|
onStartRequest() {},
|
|
onStopRequest(aRequest, aStatusCode) {
|
|
if (Components.isSuccessCode(aStatusCode)) {
|
|
resolve();
|
|
} else {
|
|
reject(new Components.Exception(aStatusCode));
|
|
}
|
|
},
|
|
},
|
|
null
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Feeds a string to a BackgroundFileSaverStreamListener.
|
|
*
|
|
* @param aSourceString
|
|
* The source data to copy.
|
|
* @param aSaverStreamListener
|
|
* The BackgroundFileSaverStreamListener to feed.
|
|
* @param aCloseWhenDone
|
|
* If true, the output stream will be closed when the copy finishes.
|
|
*
|
|
* @return {Promise}
|
|
* @resolves When the operation completes with a success code.
|
|
* @rejects With an exception, if the operation fails.
|
|
*/
|
|
function promisePumpToSaver(
|
|
aSourceString,
|
|
aSaverStreamListener,
|
|
aCloseWhenDone
|
|
) {
|
|
return new Promise((resolve, reject) => {
|
|
aSaverStreamListener.QueryInterface(Ci.nsIStreamListener);
|
|
let inputStream = new StringInputStream(
|
|
aSourceString,
|
|
aSourceString.length
|
|
);
|
|
let pump = Cc["@mozilla.org/network/input-stream-pump;1"].createInstance(
|
|
Ci.nsIInputStreamPump
|
|
);
|
|
pump.init(inputStream, 0, 0, true);
|
|
pump.asyncRead({
|
|
onStartRequest: function PPTS_onStartRequest(aRequest) {
|
|
aSaverStreamListener.onStartRequest(aRequest);
|
|
},
|
|
onStopRequest: function PPTS_onStopRequest(aRequest, aStatusCode) {
|
|
aSaverStreamListener.onStopRequest(aRequest, aStatusCode);
|
|
if (Components.isSuccessCode(aStatusCode)) {
|
|
resolve();
|
|
} else {
|
|
reject(new Components.Exception(aStatusCode));
|
|
}
|
|
},
|
|
onDataAvailable: function PPTS_onDataAvailable(
|
|
aRequest,
|
|
aInputStream,
|
|
aOffset,
|
|
aCount
|
|
) {
|
|
aSaverStreamListener.onDataAvailable(
|
|
aRequest,
|
|
aInputStream,
|
|
aOffset,
|
|
aCount
|
|
);
|
|
},
|
|
});
|
|
});
|
|
}
|
|
|
|
var gStillRunning = true;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// Tests
|
|
|
|
add_task(function test_setup() {
|
|
// Wait 10 minutes, that is half of the external xpcshell timeout.
|
|
do_timeout(10 * 60 * 1000, function() {
|
|
if (gStillRunning) {
|
|
do_throw("Test timed out.");
|
|
}
|
|
});
|
|
});
|
|
|
|
add_task(async function test_normal() {
|
|
// This test demonstrates the most basic use case.
|
|
let destFile = getTempFile(TEST_FILE_NAME_1);
|
|
|
|
// Create the object implementing the output stream.
|
|
let saver = new BackgroundFileSaverOutputStream();
|
|
|
|
// Set up callbacks for completion and target file name change.
|
|
let receivedOnTargetChange = false;
|
|
function onTargetChange(aTarget) {
|
|
Assert.ok(destFile.equals(aTarget));
|
|
receivedOnTargetChange = true;
|
|
}
|
|
let completionPromise = promiseSaverComplete(saver, onTargetChange);
|
|
|
|
// Set the target file.
|
|
saver.setTarget(destFile, false);
|
|
|
|
// Write some data and close the output stream.
|
|
await promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
|
|
|
|
// Indicate that we are ready to finish, and wait for a successful callback.
|
|
saver.finish(Cr.NS_OK);
|
|
await completionPromise;
|
|
|
|
// Only after we receive the completion notification, we can also be sure that
|
|
// we've received the target file name change notification before it.
|
|
Assert.ok(receivedOnTargetChange);
|
|
|
|
// Clean up.
|
|
destFile.remove(false);
|
|
});
|
|
|
|
add_task(async function test_combinations() {
|
|
let initialFile = getTempFile(TEST_FILE_NAME_1);
|
|
let renamedFile = getTempFile(TEST_FILE_NAME_2);
|
|
|
|
// Keep track of the current file.
|
|
let currentFile = null;
|
|
function onTargetChange(aTarget) {
|
|
currentFile = null;
|
|
info("Target file changed to: " + aTarget.leafName);
|
|
currentFile = aTarget;
|
|
}
|
|
|
|
// Tests various combinations of events and behaviors for both the stream
|
|
// listener and the output stream implementations.
|
|
for (let testFlags = 0; testFlags < 32; testFlags++) {
|
|
let keepPartialOnFailure = !!(testFlags & 1);
|
|
let renameAtSomePoint = !!(testFlags & 2);
|
|
let cancelAtSomePoint = !!(testFlags & 4);
|
|
let useStreamListener = !!(testFlags & 8);
|
|
let useLongData = !!(testFlags & 16);
|
|
|
|
let startTime = Date.now();
|
|
info(
|
|
"Starting keepPartialOnFailure = " +
|
|
keepPartialOnFailure +
|
|
", renameAtSomePoint = " +
|
|
renameAtSomePoint +
|
|
", cancelAtSomePoint = " +
|
|
cancelAtSomePoint +
|
|
", useStreamListener = " +
|
|
useStreamListener +
|
|
", useLongData = " +
|
|
useLongData
|
|
);
|
|
|
|
// Create the object and register the observers.
|
|
currentFile = null;
|
|
let saver = useStreamListener
|
|
? new BackgroundFileSaverStreamListener()
|
|
: new BackgroundFileSaverOutputStream();
|
|
saver.enableSha256();
|
|
let completionPromise = promiseSaverComplete(saver, onTargetChange);
|
|
|
|
// Start feeding the first chunk of data to the saver. In case we are using
|
|
// the stream listener, we only write one chunk.
|
|
let testData = useLongData ? TEST_DATA_LONG : TEST_DATA_SHORT;
|
|
let feedPromise = useStreamListener
|
|
? promisePumpToSaver(testData + testData, saver)
|
|
: promiseCopyToSaver(testData, saver, false);
|
|
|
|
// Set a target output file.
|
|
saver.setTarget(initialFile, keepPartialOnFailure);
|
|
|
|
// Wait for the first chunk of data to be copied.
|
|
await feedPromise;
|
|
|
|
if (renameAtSomePoint) {
|
|
saver.setTarget(renamedFile, keepPartialOnFailure);
|
|
}
|
|
|
|
if (cancelAtSomePoint) {
|
|
saver.finish(Cr.NS_ERROR_FAILURE);
|
|
}
|
|
|
|
// Feed the second chunk of data to the saver.
|
|
if (!useStreamListener) {
|
|
await promiseCopyToSaver(testData, saver, true);
|
|
}
|
|
|
|
// Wait for completion, and ensure we succeeded or failed as expected.
|
|
if (!cancelAtSomePoint) {
|
|
saver.finish(Cr.NS_OK);
|
|
}
|
|
try {
|
|
await completionPromise;
|
|
if (cancelAtSomePoint) {
|
|
do_throw("Failure expected.");
|
|
}
|
|
} catch (ex) {
|
|
if (!cancelAtSomePoint || ex.result != Cr.NS_ERROR_FAILURE) {
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
if (!cancelAtSomePoint) {
|
|
// In this case, the file must exist.
|
|
Assert.ok(currentFile.exists());
|
|
let expectedContents = testData + testData;
|
|
await promiseVerifyContents(currentFile, expectedContents);
|
|
Assert.equal(
|
|
EXPECTED_HASHES[expectedContents.length],
|
|
toHex(saver.sha256Hash)
|
|
);
|
|
currentFile.remove(false);
|
|
|
|
// If the target was really renamed, the old file should not exist.
|
|
if (renamedFile.equals(currentFile)) {
|
|
Assert.ok(!initialFile.exists());
|
|
}
|
|
} else if (!keepPartialOnFailure) {
|
|
// In this case, the file must not exist.
|
|
Assert.ok(!initialFile.exists());
|
|
Assert.ok(!renamedFile.exists());
|
|
} else {
|
|
// In this case, the file may or may not exist, because canceling can
|
|
// interrupt the asynchronous operation at any point, even before the file
|
|
// has been created for the first time.
|
|
if (initialFile.exists()) {
|
|
initialFile.remove(false);
|
|
}
|
|
if (renamedFile.exists()) {
|
|
renamedFile.remove(false);
|
|
}
|
|
}
|
|
|
|
info("Test case completed in " + (Date.now() - startTime) + " ms.");
|
|
}
|
|
});
|
|
|
|
add_task(async function test_setTarget_after_close_stream() {
|
|
// This test checks the case where we close the output stream before we call
|
|
// the setTarget method. All the data should be buffered and written anyway.
|
|
let destFile = getTempFile(TEST_FILE_NAME_1);
|
|
|
|
// Test the case where the file does not already exists first, then the case
|
|
// where the file already exists.
|
|
for (let i = 0; i < 2; i++) {
|
|
let saver = new BackgroundFileSaverOutputStream();
|
|
saver.enableSha256();
|
|
let completionPromise = promiseSaverComplete(saver);
|
|
|
|
// Copy some data to the output stream of the file saver. This data must
|
|
// be shorter than the internal component's pipe buffer for the test to
|
|
// succeed, because otherwise the test would block waiting for the write to
|
|
// complete.
|
|
await promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
|
|
|
|
// Set the target file and wait for the output to finish.
|
|
saver.setTarget(destFile, false);
|
|
saver.finish(Cr.NS_OK);
|
|
await completionPromise;
|
|
|
|
// Verify results.
|
|
await promiseVerifyContents(destFile, TEST_DATA_SHORT);
|
|
Assert.equal(
|
|
EXPECTED_HASHES[TEST_DATA_SHORT.length],
|
|
toHex(saver.sha256Hash)
|
|
);
|
|
}
|
|
|
|
// Clean up.
|
|
destFile.remove(false);
|
|
});
|
|
|
|
add_task(async function test_setTarget_fast() {
|
|
// This test checks a fast rename of the target file.
|
|
let destFile1 = getTempFile(TEST_FILE_NAME_1);
|
|
let destFile2 = getTempFile(TEST_FILE_NAME_2);
|
|
let saver = new BackgroundFileSaverOutputStream();
|
|
let completionPromise = promiseSaverComplete(saver);
|
|
|
|
// Set the initial name after the stream is closed, then rename immediately.
|
|
await promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
|
|
saver.setTarget(destFile1, false);
|
|
saver.setTarget(destFile2, false);
|
|
|
|
// Wait for all the operations to complete.
|
|
saver.finish(Cr.NS_OK);
|
|
await completionPromise;
|
|
|
|
// Verify results and clean up.
|
|
Assert.ok(!destFile1.exists());
|
|
await promiseVerifyContents(destFile2, TEST_DATA_SHORT);
|
|
destFile2.remove(false);
|
|
});
|
|
|
|
add_task(async function test_setTarget_multiple() {
|
|
// This test checks multiple renames of the target file.
|
|
let destFile = getTempFile(TEST_FILE_NAME_1);
|
|
let saver = new BackgroundFileSaverOutputStream();
|
|
let completionPromise = promiseSaverComplete(saver);
|
|
|
|
// Rename both before and after the stream is closed.
|
|
saver.setTarget(getTempFile(TEST_FILE_NAME_2), false);
|
|
saver.setTarget(getTempFile(TEST_FILE_NAME_3), false);
|
|
await promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
|
|
saver.setTarget(getTempFile(TEST_FILE_NAME_2), false);
|
|
saver.setTarget(destFile, false);
|
|
|
|
// Wait for all the operations to complete.
|
|
saver.finish(Cr.NS_OK);
|
|
await completionPromise;
|
|
|
|
// Verify results and clean up.
|
|
Assert.ok(!getTempFile(TEST_FILE_NAME_2).exists());
|
|
Assert.ok(!getTempFile(TEST_FILE_NAME_3).exists());
|
|
await promiseVerifyContents(destFile, TEST_DATA_SHORT);
|
|
destFile.remove(false);
|
|
});
|
|
|
|
add_task(async function test_enableAppend() {
|
|
// This test checks append mode with hashing disabled.
|
|
let destFile = getTempFile(TEST_FILE_NAME_1);
|
|
|
|
// Test the case where the file does not already exists first, then the case
|
|
// where the file already exists.
|
|
for (let i = 0; i < 2; i++) {
|
|
let saver = new BackgroundFileSaverOutputStream();
|
|
saver.enableAppend();
|
|
let completionPromise = promiseSaverComplete(saver);
|
|
|
|
saver.setTarget(destFile, false);
|
|
await promiseCopyToSaver(TEST_DATA_LONG, saver, true);
|
|
|
|
saver.finish(Cr.NS_OK);
|
|
await completionPromise;
|
|
|
|
// Verify results.
|
|
let expectedContents =
|
|
i == 0 ? TEST_DATA_LONG : TEST_DATA_LONG + TEST_DATA_LONG;
|
|
await promiseVerifyContents(destFile, expectedContents);
|
|
}
|
|
|
|
// Clean up.
|
|
destFile.remove(false);
|
|
});
|
|
|
|
add_task(async function test_enableAppend_setTarget_fast() {
|
|
// This test checks a fast rename of the target file in append mode.
|
|
let destFile1 = getTempFile(TEST_FILE_NAME_1);
|
|
let destFile2 = getTempFile(TEST_FILE_NAME_2);
|
|
|
|
// Test the case where the file does not already exists first, then the case
|
|
// where the file already exists.
|
|
for (let i = 0; i < 2; i++) {
|
|
let saver = new BackgroundFileSaverOutputStream();
|
|
saver.enableAppend();
|
|
let completionPromise = promiseSaverComplete(saver);
|
|
|
|
await promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
|
|
|
|
// The first time, we start appending to the first file and rename to the
|
|
// second file. The second time, we start appending to the second file,
|
|
// that was created the first time, and rename back to the first file.
|
|
let firstFile = i == 0 ? destFile1 : destFile2;
|
|
let secondFile = i == 0 ? destFile2 : destFile1;
|
|
saver.setTarget(firstFile, false);
|
|
saver.setTarget(secondFile, false);
|
|
|
|
saver.finish(Cr.NS_OK);
|
|
await completionPromise;
|
|
|
|
// Verify results.
|
|
Assert.ok(!firstFile.exists());
|
|
let expectedContents =
|
|
i == 0 ? TEST_DATA_SHORT : TEST_DATA_SHORT + TEST_DATA_SHORT;
|
|
await promiseVerifyContents(secondFile, expectedContents);
|
|
}
|
|
|
|
// Clean up.
|
|
destFile1.remove(false);
|
|
});
|
|
|
|
add_task(async function test_enableAppend_hash() {
|
|
// This test checks append mode, also verifying that the computed hash
|
|
// includes the contents of the existing data.
|
|
let destFile = getTempFile(TEST_FILE_NAME_1);
|
|
|
|
// Test the case where the file does not already exists first, then the case
|
|
// where the file already exists.
|
|
for (let i = 0; i < 2; i++) {
|
|
let saver = new BackgroundFileSaverOutputStream();
|
|
saver.enableAppend();
|
|
saver.enableSha256();
|
|
let completionPromise = promiseSaverComplete(saver);
|
|
|
|
saver.setTarget(destFile, false);
|
|
await promiseCopyToSaver(TEST_DATA_LONG, saver, true);
|
|
|
|
saver.finish(Cr.NS_OK);
|
|
await completionPromise;
|
|
|
|
// Verify results.
|
|
let expectedContents =
|
|
i == 0 ? TEST_DATA_LONG : TEST_DATA_LONG + TEST_DATA_LONG;
|
|
await promiseVerifyContents(destFile, expectedContents);
|
|
Assert.equal(
|
|
EXPECTED_HASHES[expectedContents.length],
|
|
toHex(saver.sha256Hash)
|
|
);
|
|
}
|
|
|
|
// Clean up.
|
|
destFile.remove(false);
|
|
});
|
|
|
|
add_task(async function test_finish_only() {
|
|
// This test checks creating the object and doing nothing.
|
|
let saver = new BackgroundFileSaverOutputStream();
|
|
function onTargetChange(aTarget) {
|
|
do_throw("Should not receive the onTargetChange notification.");
|
|
}
|
|
let completionPromise = promiseSaverComplete(saver, onTargetChange);
|
|
saver.finish(Cr.NS_OK);
|
|
await completionPromise;
|
|
});
|
|
|
|
add_task(async function test_empty() {
|
|
// This test checks we still create an empty file when no data is fed.
|
|
let destFile = getTempFile(TEST_FILE_NAME_1);
|
|
|
|
let saver = new BackgroundFileSaverOutputStream();
|
|
let completionPromise = promiseSaverComplete(saver);
|
|
|
|
saver.setTarget(destFile, false);
|
|
await promiseCopyToSaver("", saver, true);
|
|
|
|
saver.finish(Cr.NS_OK);
|
|
await completionPromise;
|
|
|
|
// Verify results.
|
|
Assert.ok(destFile.exists());
|
|
Assert.equal(destFile.fileSize, 0);
|
|
|
|
// Clean up.
|
|
destFile.remove(false);
|
|
});
|
|
|
|
add_task(async function test_empty_hash() {
|
|
// This test checks the hash of an empty file, both in normal and append mode.
|
|
let destFile = getTempFile(TEST_FILE_NAME_1);
|
|
|
|
// Test normal mode first, then append mode.
|
|
for (let i = 0; i < 2; i++) {
|
|
let saver = new BackgroundFileSaverOutputStream();
|
|
if (i == 1) {
|
|
saver.enableAppend();
|
|
}
|
|
saver.enableSha256();
|
|
let completionPromise = promiseSaverComplete(saver);
|
|
|
|
saver.setTarget(destFile, false);
|
|
await promiseCopyToSaver("", saver, true);
|
|
|
|
saver.finish(Cr.NS_OK);
|
|
await completionPromise;
|
|
|
|
// Verify results.
|
|
Assert.equal(destFile.fileSize, 0);
|
|
Assert.equal(EXPECTED_HASHES[0], toHex(saver.sha256Hash));
|
|
}
|
|
|
|
// Clean up.
|
|
destFile.remove(false);
|
|
});
|
|
|
|
add_task(async function test_invalid_hash() {
|
|
let saver = new BackgroundFileSaverStreamListener();
|
|
let completionPromise = promiseSaverComplete(saver);
|
|
// We shouldn't be able to get the hash if hashing hasn't been enabled
|
|
try {
|
|
saver.sha256Hash;
|
|
do_throw("Shouldn't be able to get hash if hashing not enabled");
|
|
} catch (ex) {
|
|
if (ex.result != Cr.NS_ERROR_NOT_AVAILABLE) {
|
|
throw ex;
|
|
}
|
|
}
|
|
// Enable hashing, but don't feed any data to saver
|
|
saver.enableSha256();
|
|
let destFile = getTempFile(TEST_FILE_NAME_1);
|
|
saver.setTarget(destFile, false);
|
|
// We don't wait on promiseSaverComplete, so the hash getter can run before
|
|
// or after onSaveComplete is called. However, the expected behavior is the
|
|
// same in both cases since the hash is only valid when the save completes
|
|
// successfully.
|
|
saver.finish(Cr.NS_ERROR_FAILURE);
|
|
try {
|
|
saver.sha256Hash;
|
|
do_throw("Shouldn't be able to get hash if save did not succeed");
|
|
} catch (ex) {
|
|
if (ex.result != Cr.NS_ERROR_NOT_AVAILABLE) {
|
|
throw ex;
|
|
}
|
|
}
|
|
// Wait for completion so that the worker thread finishes dealing with the
|
|
// target file. We expect it to fail.
|
|
try {
|
|
await completionPromise;
|
|
do_throw("completionPromise should throw");
|
|
} catch (ex) {
|
|
if (ex.result != Cr.NS_ERROR_FAILURE) {
|
|
throw ex;
|
|
}
|
|
}
|
|
});
|
|
|
|
add_task(async function test_signature() {
|
|
// Check that we get a signature if the saver is finished.
|
|
let destFile = getTempFile(TEST_FILE_NAME_1);
|
|
|
|
let saver = new BackgroundFileSaverOutputStream();
|
|
let completionPromise = promiseSaverComplete(saver);
|
|
|
|
try {
|
|
saver.signatureInfo;
|
|
do_throw("Can't get signature if saver is not complete");
|
|
} catch (ex) {
|
|
if (ex.result != Cr.NS_ERROR_NOT_AVAILABLE) {
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
saver.enableSignatureInfo();
|
|
saver.setTarget(destFile, false);
|
|
await promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
|
|
|
|
saver.finish(Cr.NS_OK);
|
|
await completionPromise;
|
|
await promiseVerifyContents(destFile, TEST_DATA_SHORT);
|
|
|
|
// signatureInfo is an empty nsIArray
|
|
Assert.equal(0, saver.signatureInfo.length);
|
|
|
|
// Clean up.
|
|
destFile.remove(false);
|
|
});
|
|
|
|
add_task(async function test_signature_not_enabled() {
|
|
// Check that we get a signature if the saver is finished on Windows.
|
|
let destFile = getTempFile(TEST_FILE_NAME_1);
|
|
|
|
let saver = new BackgroundFileSaverOutputStream();
|
|
let completionPromise = promiseSaverComplete(saver);
|
|
saver.setTarget(destFile, false);
|
|
await promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
|
|
|
|
saver.finish(Cr.NS_OK);
|
|
await completionPromise;
|
|
try {
|
|
saver.signatureInfo;
|
|
do_throw("Can't get signature if not enabled");
|
|
} catch (ex) {
|
|
if (ex.result != Cr.NS_ERROR_NOT_AVAILABLE) {
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
// Clean up.
|
|
destFile.remove(false);
|
|
});
|
|
|
|
add_task(function test_teardown() {
|
|
gStillRunning = false;
|
|
});
|