1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/test/unit/test_backgroundfilesaver.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,725 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* Any copyright is dedicated to the Public Domain. 1.7 + * http://creativecommons.org/publicdomain/zero/1.0/ */ 1.8 + 1.9 +/** 1.10 + * This file tests components that implement nsIBackgroundFileSaver. 1.11 + */ 1.12 + 1.13 +//////////////////////////////////////////////////////////////////////////////// 1.14 +//// Globals 1.15 + 1.16 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.17 + 1.18 +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", 1.19 + "resource://gre/modules/FileUtils.jsm"); 1.20 +XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", 1.21 + "resource://gre/modules/NetUtil.jsm"); 1.22 +XPCOMUtils.defineLazyModuleGetter(this, "Promise", 1.23 + "resource://gre/modules/Promise.jsm"); 1.24 +XPCOMUtils.defineLazyModuleGetter(this, "Task", 1.25 + "resource://gre/modules/Task.jsm"); 1.26 + 1.27 +const BackgroundFileSaverOutputStream = Components.Constructor( 1.28 + "@mozilla.org/network/background-file-saver;1?mode=outputstream", 1.29 + "nsIBackgroundFileSaver"); 1.30 + 1.31 +const BackgroundFileSaverStreamListener = Components.Constructor( 1.32 + "@mozilla.org/network/background-file-saver;1?mode=streamlistener", 1.33 + "nsIBackgroundFileSaver"); 1.34 + 1.35 +const StringInputStream = Components.Constructor( 1.36 + "@mozilla.org/io/string-input-stream;1", 1.37 + "nsIStringInputStream", 1.38 + "setData"); 1.39 + 1.40 +const REQUEST_SUSPEND_AT = 1024 * 1024 * 4; 1.41 +const TEST_DATA_SHORT = "This test string is written to the file."; 1.42 +const TEST_FILE_NAME_1 = "test-backgroundfilesaver-1.txt"; 1.43 +const TEST_FILE_NAME_2 = "test-backgroundfilesaver-2.txt"; 1.44 +const TEST_FILE_NAME_3 = "test-backgroundfilesaver-3.txt"; 1.45 + 1.46 +// A map of test data length to the expected SHA-256 hashes 1.47 +const EXPECTED_HASHES = { 1.48 + // No data 1.49 + 0 : "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 1.50 + // TEST_DATA_SHORT 1.51 + 40 : "f37176b690e8744ee990a206c086cba54d1502aa2456c3b0c84ef6345d72a192", 1.52 + // TEST_DATA_SHORT + TEST_DATA_SHORT 1.53 + 80 : "780c0e91f50bb7ec922cc11e16859e6d5df283c0d9470f61772e3d79f41eeb58", 1.54 + // TEST_DATA_LONG 1.55 + 8388608 : "e3611a47714c42bdf326acfb2eb6ed9fa4cca65cb7d7be55217770a5bf5e7ff0", 1.56 + // TEST_DATA_LONG + TEST_DATA_LONG 1.57 + 16777216 : "03a0db69a30140f307587ee746a539247c181bafd85b85c8516a3533c7d9ea1d" 1.58 +}; 1.59 + 1.60 +const gTextDecoder = new TextDecoder(); 1.61 + 1.62 +// Generate a long string of data in a moderately fast way. 1.63 +const TEST_256_CHARS = new Array(257).join("-"); 1.64 +const DESIRED_LENGTH = REQUEST_SUSPEND_AT * 2; 1.65 +const TEST_DATA_LONG = new Array(1 + DESIRED_LENGTH / 256).join(TEST_256_CHARS); 1.66 +do_check_eq(TEST_DATA_LONG.length, DESIRED_LENGTH); 1.67 + 1.68 +/** 1.69 + * Returns a reference to a temporary file. If the file is then created, it 1.70 + * will be removed when tests in this file finish. 1.71 + */ 1.72 +function getTempFile(aLeafName) { 1.73 + let file = FileUtils.getFile("TmpD", [aLeafName]); 1.74 + do_register_cleanup(function GTF_cleanup() { 1.75 + if (file.exists()) { 1.76 + file.remove(false); 1.77 + } 1.78 + }); 1.79 + return file; 1.80 +} 1.81 + 1.82 +/** 1.83 + * Helper function for converting a binary blob to its hex equivalent. 1.84 + * 1.85 + * @param str 1.86 + * String possibly containing non-printable chars. 1.87 + * @return A hex-encoded string. 1.88 + */ 1.89 +function toHex(str) { 1.90 + var hex = ''; 1.91 + for (var i = 0; i < str.length; i++) { 1.92 + hex += ('0' + str.charCodeAt(i).toString(16)).slice(-2); 1.93 + } 1.94 + return hex; 1.95 +} 1.96 + 1.97 +/** 1.98 + * Ensures that the given file contents are equal to the given string. 1.99 + * 1.100 + * @param aFile 1.101 + * nsIFile whose contents should be verified. 1.102 + * @param aExpectedContents 1.103 + * String containing the octets that are expected in the file. 1.104 + * 1.105 + * @return {Promise} 1.106 + * @resolves When the operation completes. 1.107 + * @rejects Never. 1.108 + */ 1.109 +function promiseVerifyContents(aFile, aExpectedContents) { 1.110 + let deferred = Promise.defer(); 1.111 + NetUtil.asyncFetch(aFile, function(aInputStream, aStatus) { 1.112 + do_check_true(Components.isSuccessCode(aStatus)); 1.113 + let contents = NetUtil.readInputStreamToString(aInputStream, 1.114 + aInputStream.available()); 1.115 + if (contents.length <= TEST_DATA_SHORT.length * 2) { 1.116 + do_check_eq(contents, aExpectedContents); 1.117 + } else { 1.118 + // Do not print the entire content string to the test log. 1.119 + do_check_eq(contents.length, aExpectedContents.length); 1.120 + do_check_true(contents == aExpectedContents); 1.121 + } 1.122 + deferred.resolve(); 1.123 + }); 1.124 + return deferred.promise; 1.125 +} 1.126 + 1.127 +/** 1.128 + * Waits for the given saver object to complete. 1.129 + * 1.130 + * @param aSaver 1.131 + * The saver, with the output stream or a stream listener implementation. 1.132 + * @param aOnTargetChangeFn 1.133 + * Optional callback invoked with the target file name when it changes. 1.134 + * 1.135 + * @return {Promise} 1.136 + * @resolves When onSaveComplete is called with a success code. 1.137 + * @rejects With an exception, if onSaveComplete is called with a failure code. 1.138 + */ 1.139 +function promiseSaverComplete(aSaver, aOnTargetChangeFn) { 1.140 + let deferred = Promise.defer(); 1.141 + aSaver.observer = { 1.142 + onTargetChange: function BFSO_onSaveComplete(aSaver, aTarget) 1.143 + { 1.144 + if (aOnTargetChangeFn) { 1.145 + aOnTargetChangeFn(aTarget); 1.146 + } 1.147 + }, 1.148 + onSaveComplete: function BFSO_onSaveComplete(aSaver, aStatus) 1.149 + { 1.150 + if (Components.isSuccessCode(aStatus)) { 1.151 + deferred.resolve(); 1.152 + } else { 1.153 + deferred.reject(new Components.Exception("Saver failed.", aStatus)); 1.154 + } 1.155 + }, 1.156 + }; 1.157 + return deferred.promise; 1.158 +} 1.159 + 1.160 +/** 1.161 + * Feeds a string to a BackgroundFileSaverOutputStream. 1.162 + * 1.163 + * @param aSourceString 1.164 + * The source data to copy. 1.165 + * @param aSaverOutputStream 1.166 + * The BackgroundFileSaverOutputStream to feed. 1.167 + * @param aCloseWhenDone 1.168 + * If true, the output stream will be closed when the copy finishes. 1.169 + * 1.170 + * @return {Promise} 1.171 + * @resolves When the copy completes with a success code. 1.172 + * @rejects With an exception, if the copy fails. 1.173 + */ 1.174 +function promiseCopyToSaver(aSourceString, aSaverOutputStream, aCloseWhenDone) { 1.175 + let deferred = Promise.defer(); 1.176 + let inputStream = new StringInputStream(aSourceString, aSourceString.length); 1.177 + let copier = Cc["@mozilla.org/network/async-stream-copier;1"] 1.178 + .createInstance(Ci.nsIAsyncStreamCopier); 1.179 + copier.init(inputStream, aSaverOutputStream, null, false, true, 0x8000, true, 1.180 + aCloseWhenDone); 1.181 + copier.asyncCopy({ 1.182 + onStartRequest: function () { }, 1.183 + onStopRequest: function (aRequest, aContext, aStatusCode) 1.184 + { 1.185 + if (Components.isSuccessCode(aStatusCode)) { 1.186 + deferred.resolve(); 1.187 + } else { 1.188 + deferred.reject(new Components.Exception(aResult)); 1.189 + } 1.190 + }, 1.191 + }, null); 1.192 + return deferred.promise; 1.193 +} 1.194 + 1.195 +/** 1.196 + * Feeds a string to a BackgroundFileSaverStreamListener. 1.197 + * 1.198 + * @param aSourceString 1.199 + * The source data to copy. 1.200 + * @param aSaverStreamListener 1.201 + * The BackgroundFileSaverStreamListener to feed. 1.202 + * @param aCloseWhenDone 1.203 + * If true, the output stream will be closed when the copy finishes. 1.204 + * 1.205 + * @return {Promise} 1.206 + * @resolves When the operation completes with a success code. 1.207 + * @rejects With an exception, if the operation fails. 1.208 + */ 1.209 +function promisePumpToSaver(aSourceString, aSaverStreamListener, 1.210 + aCloseWhenDone) { 1.211 + let deferred = Promise.defer(); 1.212 + aSaverStreamListener.QueryInterface(Ci.nsIStreamListener); 1.213 + let inputStream = new StringInputStream(aSourceString, aSourceString.length); 1.214 + let pump = Cc["@mozilla.org/network/input-stream-pump;1"] 1.215 + .createInstance(Ci.nsIInputStreamPump); 1.216 + pump.init(inputStream, -1, -1, 0, 0, true); 1.217 + pump.asyncRead({ 1.218 + onStartRequest: function PPTS_onStartRequest(aRequest, aContext) 1.219 + { 1.220 + aSaverStreamListener.onStartRequest(aRequest, aContext); 1.221 + }, 1.222 + onStopRequest: function PPTS_onStopRequest(aRequest, aContext, aStatusCode) 1.223 + { 1.224 + aSaverStreamListener.onStopRequest(aRequest, aContext, aStatusCode); 1.225 + if (Components.isSuccessCode(aStatusCode)) { 1.226 + deferred.resolve(); 1.227 + } else { 1.228 + deferred.reject(new Components.Exception(aResult)); 1.229 + } 1.230 + }, 1.231 + onDataAvailable: function PPTS_onDataAvailable(aRequest, aContext, 1.232 + aInputStream, aOffset, 1.233 + aCount) 1.234 + { 1.235 + aSaverStreamListener.onDataAvailable(aRequest, aContext, aInputStream, 1.236 + aOffset, aCount); 1.237 + }, 1.238 + }, null); 1.239 + return deferred.promise; 1.240 +} 1.241 + 1.242 +let gStillRunning = true; 1.243 + 1.244 +//////////////////////////////////////////////////////////////////////////////// 1.245 +//// Tests 1.246 + 1.247 +function run_test() 1.248 +{ 1.249 + run_next_test(); 1.250 +} 1.251 + 1.252 +add_task(function test_setup() 1.253 +{ 1.254 + // Wait 10 minutes, that is half of the external xpcshell timeout. 1.255 + do_timeout(10 * 60 * 1000, function() { 1.256 + if (gStillRunning) { 1.257 + do_throw("Test timed out."); 1.258 + } 1.259 + }) 1.260 +}); 1.261 + 1.262 +add_task(function test_normal() 1.263 +{ 1.264 + // This test demonstrates the most basic use case. 1.265 + let destFile = getTempFile(TEST_FILE_NAME_1); 1.266 + 1.267 + // Create the object implementing the output stream. 1.268 + let saver = new BackgroundFileSaverOutputStream(); 1.269 + 1.270 + // Set up callbacks for completion and target file name change. 1.271 + let receivedOnTargetChange = false; 1.272 + function onTargetChange(aTarget) { 1.273 + do_check_true(destFile.equals(aTarget)); 1.274 + receivedOnTargetChange = true; 1.275 + } 1.276 + let completionPromise = promiseSaverComplete(saver, onTargetChange); 1.277 + 1.278 + // Set the target file. 1.279 + saver.setTarget(destFile, false); 1.280 + 1.281 + // Write some data and close the output stream. 1.282 + yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true); 1.283 + 1.284 + // Indicate that we are ready to finish, and wait for a successful callback. 1.285 + saver.finish(Cr.NS_OK); 1.286 + yield completionPromise; 1.287 + 1.288 + // Only after we receive the completion notification, we can also be sure that 1.289 + // we've received the target file name change notification before it. 1.290 + do_check_true(receivedOnTargetChange); 1.291 + 1.292 + // Clean up. 1.293 + destFile.remove(false); 1.294 +}); 1.295 + 1.296 +add_task(function test_combinations() 1.297 +{ 1.298 + let initialFile = getTempFile(TEST_FILE_NAME_1); 1.299 + let renamedFile = getTempFile(TEST_FILE_NAME_2); 1.300 + 1.301 + // Keep track of the current file. 1.302 + let currentFile = null; 1.303 + function onTargetChange(aTarget) { 1.304 + currentFile = null; 1.305 + do_print("Target file changed to: " + aTarget.leafName); 1.306 + currentFile = aTarget; 1.307 + } 1.308 + 1.309 + // Tests various combinations of events and behaviors for both the stream 1.310 + // listener and the output stream implementations. 1.311 + for (let testFlags = 0; testFlags < 32; testFlags++) { 1.312 + let keepPartialOnFailure = !!(testFlags & 1); 1.313 + let renameAtSomePoint = !!(testFlags & 2); 1.314 + let cancelAtSomePoint = !!(testFlags & 4); 1.315 + let useStreamListener = !!(testFlags & 8); 1.316 + let useLongData = !!(testFlags & 16); 1.317 + 1.318 + let startTime = Date.now(); 1.319 + do_print("Starting keepPartialOnFailure = " + keepPartialOnFailure + 1.320 + ", renameAtSomePoint = " + renameAtSomePoint + 1.321 + ", cancelAtSomePoint = " + cancelAtSomePoint + 1.322 + ", useStreamListener = " + useStreamListener + 1.323 + ", useLongData = " + useLongData); 1.324 + 1.325 + // Create the object and register the observers. 1.326 + currentFile = null; 1.327 + let saver = useStreamListener 1.328 + ? new BackgroundFileSaverStreamListener() 1.329 + : new BackgroundFileSaverOutputStream(); 1.330 + saver.enableSha256(); 1.331 + let completionPromise = promiseSaverComplete(saver, onTargetChange); 1.332 + 1.333 + // Start feeding the first chunk of data to the saver. In case we are using 1.334 + // the stream listener, we only write one chunk. 1.335 + let testData = useLongData ? TEST_DATA_LONG : TEST_DATA_SHORT; 1.336 + let feedPromise = useStreamListener 1.337 + ? promisePumpToSaver(testData + testData, saver) 1.338 + : promiseCopyToSaver(testData, saver, false); 1.339 + 1.340 + // Set a target output file. 1.341 + saver.setTarget(initialFile, keepPartialOnFailure); 1.342 + 1.343 + // Wait for the first chunk of data to be copied. 1.344 + yield feedPromise; 1.345 + 1.346 + if (renameAtSomePoint) { 1.347 + saver.setTarget(renamedFile, keepPartialOnFailure); 1.348 + } 1.349 + 1.350 + if (cancelAtSomePoint) { 1.351 + saver.finish(Cr.NS_ERROR_FAILURE); 1.352 + } 1.353 + 1.354 + // Feed the second chunk of data to the saver. 1.355 + if (!useStreamListener) { 1.356 + yield promiseCopyToSaver(testData, saver, true); 1.357 + } 1.358 + 1.359 + // Wait for completion, and ensure we succeeded or failed as expected. 1.360 + if (!cancelAtSomePoint) { 1.361 + saver.finish(Cr.NS_OK); 1.362 + } 1.363 + try { 1.364 + yield completionPromise; 1.365 + if (cancelAtSomePoint) { 1.366 + do_throw("Failure expected."); 1.367 + } 1.368 + } catch (ex if cancelAtSomePoint && ex.result == Cr.NS_ERROR_FAILURE) { } 1.369 + 1.370 + if (!cancelAtSomePoint) { 1.371 + // In this case, the file must exist. 1.372 + do_check_true(currentFile.exists()); 1.373 + let expectedContents = testData + testData; 1.374 + yield promiseVerifyContents(currentFile, expectedContents); 1.375 + do_check_eq(EXPECTED_HASHES[expectedContents.length], 1.376 + toHex(saver.sha256Hash)); 1.377 + currentFile.remove(false); 1.378 + 1.379 + // If the target was really renamed, the old file should not exist. 1.380 + if (renamedFile.equals(currentFile)) { 1.381 + do_check_false(initialFile.exists()); 1.382 + } 1.383 + } else if (!keepPartialOnFailure) { 1.384 + // In this case, the file must not exist. 1.385 + do_check_false(initialFile.exists()); 1.386 + do_check_false(renamedFile.exists()); 1.387 + } else { 1.388 + // In this case, the file may or may not exist, because canceling can 1.389 + // interrupt the asynchronous operation at any point, even before the file 1.390 + // has been created for the first time. 1.391 + if (initialFile.exists()) { 1.392 + initialFile.remove(false); 1.393 + } 1.394 + if (renamedFile.exists()) { 1.395 + renamedFile.remove(false); 1.396 + } 1.397 + } 1.398 + 1.399 + do_print("Test case completed in " + (Date.now() - startTime) + " ms."); 1.400 + } 1.401 +}); 1.402 + 1.403 +add_task(function test_setTarget_after_close_stream() 1.404 +{ 1.405 + // This test checks the case where we close the output stream before we call 1.406 + // the setTarget method. All the data should be buffered and written anyway. 1.407 + let destFile = getTempFile(TEST_FILE_NAME_1); 1.408 + 1.409 + // Test the case where the file does not already exists first, then the case 1.410 + // where the file already exists. 1.411 + for (let i = 0; i < 2; i++) { 1.412 + let saver = new BackgroundFileSaverOutputStream(); 1.413 + saver.enableSha256(); 1.414 + let completionPromise = promiseSaverComplete(saver); 1.415 + 1.416 + // Copy some data to the output stream of the file saver. This data must 1.417 + // be shorter than the internal component's pipe buffer for the test to 1.418 + // succeed, because otherwise the test would block waiting for the write to 1.419 + // complete. 1.420 + yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true); 1.421 + 1.422 + // Set the target file and wait for the output to finish. 1.423 + saver.setTarget(destFile, false); 1.424 + saver.finish(Cr.NS_OK); 1.425 + yield completionPromise; 1.426 + 1.427 + // Verify results. 1.428 + yield promiseVerifyContents(destFile, TEST_DATA_SHORT); 1.429 + do_check_eq(EXPECTED_HASHES[TEST_DATA_SHORT.length], 1.430 + toHex(saver.sha256Hash)); 1.431 + } 1.432 + 1.433 + // Clean up. 1.434 + destFile.remove(false); 1.435 +}); 1.436 + 1.437 +add_task(function test_setTarget_fast() 1.438 +{ 1.439 + // This test checks a fast rename of the target file. 1.440 + let destFile1 = getTempFile(TEST_FILE_NAME_1); 1.441 + let destFile2 = getTempFile(TEST_FILE_NAME_2); 1.442 + let saver = new BackgroundFileSaverOutputStream(); 1.443 + let completionPromise = promiseSaverComplete(saver); 1.444 + 1.445 + // Set the initial name after the stream is closed, then rename immediately. 1.446 + yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true); 1.447 + saver.setTarget(destFile1, false); 1.448 + saver.setTarget(destFile2, false); 1.449 + 1.450 + // Wait for all the operations to complete. 1.451 + saver.finish(Cr.NS_OK); 1.452 + yield completionPromise; 1.453 + 1.454 + // Verify results and clean up. 1.455 + do_check_false(destFile1.exists()); 1.456 + yield promiseVerifyContents(destFile2, TEST_DATA_SHORT); 1.457 + destFile2.remove(false); 1.458 +}); 1.459 + 1.460 +add_task(function test_setTarget_multiple() 1.461 +{ 1.462 + // This test checks multiple renames of the target file. 1.463 + let destFile = getTempFile(TEST_FILE_NAME_1); 1.464 + let saver = new BackgroundFileSaverOutputStream(); 1.465 + let completionPromise = promiseSaverComplete(saver); 1.466 + 1.467 + // Rename both before and after the stream is closed. 1.468 + saver.setTarget(getTempFile(TEST_FILE_NAME_2), false); 1.469 + saver.setTarget(getTempFile(TEST_FILE_NAME_3), false); 1.470 + yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true); 1.471 + saver.setTarget(getTempFile(TEST_FILE_NAME_2), false); 1.472 + saver.setTarget(destFile, false); 1.473 + 1.474 + // Wait for all the operations to complete. 1.475 + saver.finish(Cr.NS_OK); 1.476 + yield completionPromise; 1.477 + 1.478 + // Verify results and clean up. 1.479 + do_check_false(getTempFile(TEST_FILE_NAME_2).exists()); 1.480 + do_check_false(getTempFile(TEST_FILE_NAME_3).exists()); 1.481 + yield promiseVerifyContents(destFile, TEST_DATA_SHORT); 1.482 + destFile.remove(false); 1.483 +}); 1.484 + 1.485 +add_task(function test_enableAppend() 1.486 +{ 1.487 + // This test checks append mode with hashing disabled. 1.488 + let destFile = getTempFile(TEST_FILE_NAME_1); 1.489 + 1.490 + // Test the case where the file does not already exists first, then the case 1.491 + // where the file already exists. 1.492 + for (let i = 0; i < 2; i++) { 1.493 + let saver = new BackgroundFileSaverOutputStream(); 1.494 + saver.enableAppend(); 1.495 + let completionPromise = promiseSaverComplete(saver); 1.496 + 1.497 + saver.setTarget(destFile, false); 1.498 + yield promiseCopyToSaver(TEST_DATA_LONG, saver, true); 1.499 + 1.500 + saver.finish(Cr.NS_OK); 1.501 + yield completionPromise; 1.502 + 1.503 + // Verify results. 1.504 + let expectedContents = (i == 0 ? TEST_DATA_LONG 1.505 + : TEST_DATA_LONG + TEST_DATA_LONG); 1.506 + yield promiseVerifyContents(destFile, expectedContents); 1.507 + } 1.508 + 1.509 + // Clean up. 1.510 + destFile.remove(false); 1.511 +}); 1.512 + 1.513 +add_task(function test_enableAppend_setTarget_fast() 1.514 +{ 1.515 + // This test checks a fast rename of the target file in append mode. 1.516 + let destFile1 = getTempFile(TEST_FILE_NAME_1); 1.517 + let destFile2 = getTempFile(TEST_FILE_NAME_2); 1.518 + 1.519 + // Test the case where the file does not already exists first, then the case 1.520 + // where the file already exists. 1.521 + for (let i = 0; i < 2; i++) { 1.522 + let saver = new BackgroundFileSaverOutputStream(); 1.523 + saver.enableAppend(); 1.524 + let completionPromise = promiseSaverComplete(saver); 1.525 + 1.526 + yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true); 1.527 + 1.528 + // The first time, we start appending to the first file and rename to the 1.529 + // second file. The second time, we start appending to the second file, 1.530 + // that was created the first time, and rename back to the first file. 1.531 + let firstFile = (i == 0) ? destFile1 : destFile2; 1.532 + let secondFile = (i == 0) ? destFile2 : destFile1; 1.533 + saver.setTarget(firstFile, false); 1.534 + saver.setTarget(secondFile, false); 1.535 + 1.536 + saver.finish(Cr.NS_OK); 1.537 + yield completionPromise; 1.538 + 1.539 + // Verify results. 1.540 + do_check_false(firstFile.exists()); 1.541 + let expectedContents = (i == 0 ? TEST_DATA_SHORT 1.542 + : TEST_DATA_SHORT + TEST_DATA_SHORT); 1.543 + yield promiseVerifyContents(secondFile, expectedContents); 1.544 + } 1.545 + 1.546 + // Clean up. 1.547 + destFile1.remove(false); 1.548 +}); 1.549 + 1.550 +add_task(function test_enableAppend_hash() 1.551 +{ 1.552 + // This test checks append mode, also verifying that the computed hash 1.553 + // includes the contents of the existing data. 1.554 + let destFile = getTempFile(TEST_FILE_NAME_1); 1.555 + 1.556 + // Test the case where the file does not already exists first, then the case 1.557 + // where the file already exists. 1.558 + for (let i = 0; i < 2; i++) { 1.559 + let saver = new BackgroundFileSaverOutputStream(); 1.560 + saver.enableAppend(); 1.561 + saver.enableSha256(); 1.562 + let completionPromise = promiseSaverComplete(saver); 1.563 + 1.564 + saver.setTarget(destFile, false); 1.565 + yield promiseCopyToSaver(TEST_DATA_LONG, saver, true); 1.566 + 1.567 + saver.finish(Cr.NS_OK); 1.568 + yield completionPromise; 1.569 + 1.570 + // Verify results. 1.571 + let expectedContents = (i == 0 ? TEST_DATA_LONG 1.572 + : TEST_DATA_LONG + TEST_DATA_LONG); 1.573 + yield promiseVerifyContents(destFile, expectedContents); 1.574 + do_check_eq(EXPECTED_HASHES[expectedContents.length], 1.575 + toHex(saver.sha256Hash)); 1.576 + } 1.577 + 1.578 + // Clean up. 1.579 + destFile.remove(false); 1.580 +}); 1.581 + 1.582 +add_task(function test_finish_only() 1.583 +{ 1.584 + // This test checks creating the object and doing nothing. 1.585 + let destFile = getTempFile(TEST_FILE_NAME_1); 1.586 + let saver = new BackgroundFileSaverOutputStream(); 1.587 + function onTargetChange(aTarget) { 1.588 + do_throw("Should not receive the onTargetChange notification."); 1.589 + } 1.590 + let completionPromise = promiseSaverComplete(saver, onTargetChange); 1.591 + saver.finish(Cr.NS_OK); 1.592 + yield completionPromise; 1.593 +}); 1.594 + 1.595 +add_task(function test_empty() 1.596 +{ 1.597 + // This test checks we still create an empty file when no data is fed. 1.598 + let destFile = getTempFile(TEST_FILE_NAME_1); 1.599 + 1.600 + let saver = new BackgroundFileSaverOutputStream(); 1.601 + let completionPromise = promiseSaverComplete(saver); 1.602 + 1.603 + saver.setTarget(destFile, false); 1.604 + yield promiseCopyToSaver("", saver, true); 1.605 + 1.606 + saver.finish(Cr.NS_OK); 1.607 + yield completionPromise; 1.608 + 1.609 + // Verify results. 1.610 + do_check_true(destFile.exists()); 1.611 + do_check_eq(destFile.fileSize, 0); 1.612 + 1.613 + // Clean up. 1.614 + destFile.remove(false); 1.615 +}); 1.616 + 1.617 +add_task(function test_empty_hash() 1.618 +{ 1.619 + // This test checks the hash of an empty file, both in normal and append mode. 1.620 + let destFile = getTempFile(TEST_FILE_NAME_1); 1.621 + 1.622 + // Test normal mode first, then append mode. 1.623 + for (let i = 0; i < 2; i++) { 1.624 + let saver = new BackgroundFileSaverOutputStream(); 1.625 + if (i == 1) { 1.626 + saver.enableAppend(); 1.627 + } 1.628 + saver.enableSha256(); 1.629 + let completionPromise = promiseSaverComplete(saver); 1.630 + 1.631 + saver.setTarget(destFile, false); 1.632 + yield promiseCopyToSaver("", saver, true); 1.633 + 1.634 + saver.finish(Cr.NS_OK); 1.635 + yield completionPromise; 1.636 + 1.637 + // Verify results. 1.638 + do_check_eq(destFile.fileSize, 0); 1.639 + do_check_eq(EXPECTED_HASHES[0], toHex(saver.sha256Hash)); 1.640 + } 1.641 + 1.642 + // Clean up. 1.643 + destFile.remove(false); 1.644 +}); 1.645 + 1.646 +add_task(function test_invalid_hash() 1.647 +{ 1.648 + let saver = new BackgroundFileSaverStreamListener(); 1.649 + let completionPromise = promiseSaverComplete(saver); 1.650 + // We shouldn't be able to get the hash if hashing hasn't been enabled 1.651 + try { 1.652 + let hash = saver.sha256Hash; 1.653 + do_throw("Shouldn't be able to get hash if hashing not enabled"); 1.654 + } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { } 1.655 + // Enable hashing, but don't feed any data to saver 1.656 + saver.enableSha256(); 1.657 + let destFile = getTempFile(TEST_FILE_NAME_1); 1.658 + saver.setTarget(destFile, false); 1.659 + // We don't wait on promiseSaverComplete, so the hash getter can run before 1.660 + // or after onSaveComplete is called. However, the expected behavior is the 1.661 + // same in both cases since the hash is only valid when the save completes 1.662 + // successfully. 1.663 + saver.finish(Cr.NS_ERROR_FAILURE); 1.664 + try { 1.665 + let hash = saver.sha256Hash; 1.666 + do_throw("Shouldn't be able to get hash if save did not succeed"); 1.667 + } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { } 1.668 + // Wait for completion so that the worker thread finishes dealing with the 1.669 + // target file. We expect it to fail. 1.670 + try { 1.671 + yield completionPromise; 1.672 + do_throw("completionPromise should throw"); 1.673 + } catch (ex if ex.result == Cr.NS_ERROR_FAILURE) { } 1.674 +}); 1.675 + 1.676 +add_task(function test_signature() 1.677 +{ 1.678 + // Check that we get a signature if the saver is finished. 1.679 + let destFile = getTempFile(TEST_FILE_NAME_1); 1.680 + 1.681 + let saver = new BackgroundFileSaverOutputStream(); 1.682 + let completionPromise = promiseSaverComplete(saver); 1.683 + 1.684 + try { 1.685 + let signatureInfo = saver.signatureInfo; 1.686 + do_throw("Can't get signature if saver is not complete"); 1.687 + } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { } 1.688 + 1.689 + saver.enableSignatureInfo(); 1.690 + saver.setTarget(destFile, false); 1.691 + yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true); 1.692 + 1.693 + saver.finish(Cr.NS_OK); 1.694 + yield completionPromise; 1.695 + yield promiseVerifyContents(destFile, TEST_DATA_SHORT); 1.696 + 1.697 + // signatureInfo is an empty nsIArray 1.698 + do_check_eq(0, saver.signatureInfo.length); 1.699 + 1.700 + // Clean up. 1.701 + destFile.remove(false); 1.702 +}); 1.703 + 1.704 +add_task(function test_signature_not_enabled() 1.705 +{ 1.706 + // Check that we get a signature if the saver is finished on Windows. 1.707 + let destFile = getTempFile(TEST_FILE_NAME_1); 1.708 + 1.709 + let saver = new BackgroundFileSaverOutputStream(); 1.710 + let completionPromise = promiseSaverComplete(saver); 1.711 + saver.setTarget(destFile, false); 1.712 + yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true); 1.713 + 1.714 + saver.finish(Cr.NS_OK); 1.715 + yield completionPromise; 1.716 + try { 1.717 + let signatureInfo = saver.signatureInfo; 1.718 + do_throw("Can't get signature if not enabled"); 1.719 + } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { } 1.720 + 1.721 + // Clean up. 1.722 + destFile.remove(false); 1.723 +}); 1.724 + 1.725 +add_task(function test_teardown() 1.726 +{ 1.727 + gStillRunning = false; 1.728 +});