Bug 1146304 - Touch slider bar or tap forward button, the video got stuck 1s then...
[gecko.git] / netwerk / test / unit / test_backgroundfilesaver.js
blob9499f228e80f7d0dd6997669d871df5233fa25cf
1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* Any copyright is dedicated to the Public Domain.
4  * http://creativecommons.org/publicdomain/zero/1.0/ */
6 /**
7  * This file tests components that implement nsIBackgroundFileSaver.
8  */
10 ////////////////////////////////////////////////////////////////////////////////
11 //// Globals
13 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
15 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
16                                   "resource://gre/modules/FileUtils.jsm");
17 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
18                                   "resource://gre/modules/NetUtil.jsm");
19 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
20                                   "resource://gre/modules/Promise.jsm");
21 XPCOMUtils.defineLazyModuleGetter(this, "Task",
22                                   "resource://gre/modules/Task.jsm");
24 const BackgroundFileSaverOutputStream = Components.Constructor(
25       "@mozilla.org/network/background-file-saver;1?mode=outputstream",
26       "nsIBackgroundFileSaver");
28 const BackgroundFileSaverStreamListener = Components.Constructor(
29       "@mozilla.org/network/background-file-saver;1?mode=streamlistener",
30       "nsIBackgroundFileSaver");
32 const StringInputStream = Components.Constructor(
33       "@mozilla.org/io/string-input-stream;1",
34       "nsIStringInputStream",
35       "setData");
37 const REQUEST_SUSPEND_AT = 1024 * 1024 * 4;
38 const TEST_DATA_SHORT = "This test string is written to the file.";
39 const TEST_FILE_NAME_1 = "test-backgroundfilesaver-1.txt";
40 const TEST_FILE_NAME_2 = "test-backgroundfilesaver-2.txt";
41 const TEST_FILE_NAME_3 = "test-backgroundfilesaver-3.txt";
43 // A map of test data length to the expected SHA-256 hashes
44 const EXPECTED_HASHES = {
45   // No data
46   0 : "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
47   // TEST_DATA_SHORT
48   40 : "f37176b690e8744ee990a206c086cba54d1502aa2456c3b0c84ef6345d72a192",
49   // TEST_DATA_SHORT + TEST_DATA_SHORT
50   80 : "780c0e91f50bb7ec922cc11e16859e6d5df283c0d9470f61772e3d79f41eeb58",
51   // TEST_DATA_LONG
52   8388608 : "e3611a47714c42bdf326acfb2eb6ed9fa4cca65cb7d7be55217770a5bf5e7ff0",
53   // TEST_DATA_LONG + TEST_DATA_LONG
54   16777216 : "03a0db69a30140f307587ee746a539247c181bafd85b85c8516a3533c7d9ea1d"
57 const gTextDecoder = new TextDecoder();
59 // Generate a long string of data in a moderately fast way.
60 const TEST_256_CHARS = new Array(257).join("-");
61 const DESIRED_LENGTH = REQUEST_SUSPEND_AT * 2;
62 const TEST_DATA_LONG = new Array(1 + DESIRED_LENGTH / 256).join(TEST_256_CHARS);
63 do_check_eq(TEST_DATA_LONG.length, DESIRED_LENGTH);
65 /**
66  * Returns a reference to a temporary file.  If the file is then created, it
67  * will be removed when tests in this file finish.
68  */
69 function getTempFile(aLeafName) {
70   let file = FileUtils.getFile("TmpD", [aLeafName]);
71   do_register_cleanup(function GTF_cleanup() {
72     if (file.exists()) {
73       file.remove(false);
74     }
75   });
76   return file;
79 /**
80  * Helper function for converting a binary blob to its hex equivalent.
81  *
82  * @param str
83  *        String possibly containing non-printable chars.
84  * @return A hex-encoded string.
85  */
86 function toHex(str) {
87   var hex = '';
88   for (var i = 0; i < str.length; i++) {
89     hex += ('0' + str.charCodeAt(i).toString(16)).slice(-2);
90   }
91   return hex;
94 /**
95  * Ensures that the given file contents are equal to the given string.
96  *
97  * @param aFile
98  *        nsIFile whose contents should be verified.
99  * @param aExpectedContents
100  *        String containing the octets that are expected in the file.
102  * @return {Promise}
103  * @resolves When the operation completes.
104  * @rejects Never.
105  */
106 function promiseVerifyContents(aFile, aExpectedContents) {
107   let deferred = Promise.defer();
108   NetUtil.asyncFetch(aFile, function(aInputStream, aStatus) {
109     do_check_true(Components.isSuccessCode(aStatus));
110     let contents = NetUtil.readInputStreamToString(aInputStream,
111                                                    aInputStream.available());
112     if (contents.length <= TEST_DATA_SHORT.length * 2) {
113       do_check_eq(contents, aExpectedContents);
114     } else {
115       // Do not print the entire content string to the test log.
116       do_check_eq(contents.length, aExpectedContents.length);
117       do_check_true(contents == aExpectedContents);
118     }
119     deferred.resolve();
120   });
121   return deferred.promise;
125  * Waits for the given saver object to complete.
127  * @param aSaver
128  *        The saver, with the output stream or a stream listener implementation.
129  * @param aOnTargetChangeFn
130  *        Optional callback invoked with the target file name when it changes.
132  * @return {Promise}
133  * @resolves When onSaveComplete is called with a success code.
134  * @rejects With an exception, if onSaveComplete is called with a failure code.
135  */
136 function promiseSaverComplete(aSaver, aOnTargetChangeFn) {
137   let deferred = Promise.defer();
138   aSaver.observer = {
139     onTargetChange: function BFSO_onSaveComplete(aSaver, aTarget)
140     {
141       if (aOnTargetChangeFn) {
142         aOnTargetChangeFn(aTarget);
143       }
144     },
145     onSaveComplete: function BFSO_onSaveComplete(aSaver, aStatus)
146     {
147       if (Components.isSuccessCode(aStatus)) {
148         deferred.resolve();
149       } else {
150         deferred.reject(new Components.Exception("Saver failed.", aStatus));
151       }
152     },
153   };
154   return deferred.promise;
158  * Feeds a string to a BackgroundFileSaverOutputStream.
160  * @param aSourceString
161  *        The source data to copy.
162  * @param aSaverOutputStream
163  *        The BackgroundFileSaverOutputStream to feed.
164  * @param aCloseWhenDone
165  *        If true, the output stream will be closed when the copy finishes.
167  * @return {Promise}
168  * @resolves When the copy completes with a success code.
169  * @rejects With an exception, if the copy fails.
170  */
171 function promiseCopyToSaver(aSourceString, aSaverOutputStream, aCloseWhenDone) {
172   let deferred = Promise.defer();
173   let inputStream = new StringInputStream(aSourceString, aSourceString.length);
174   let copier = Cc["@mozilla.org/network/async-stream-copier;1"]
175                .createInstance(Ci.nsIAsyncStreamCopier);
176   copier.init(inputStream, aSaverOutputStream, null, false, true, 0x8000, true,
177               aCloseWhenDone);
178   copier.asyncCopy({
179     onStartRequest: function () { },
180     onStopRequest: function (aRequest, aContext, aStatusCode)
181     {
182       if (Components.isSuccessCode(aStatusCode)) {
183         deferred.resolve();
184       } else {
185         deferred.reject(new Components.Exception(aResult));
186       }
187     },
188   }, null);
189   return deferred.promise;
193  * Feeds a string to a BackgroundFileSaverStreamListener.
195  * @param aSourceString
196  *        The source data to copy.
197  * @param aSaverStreamListener
198  *        The BackgroundFileSaverStreamListener to feed.
199  * @param aCloseWhenDone
200  *        If true, the output stream will be closed when the copy finishes.
202  * @return {Promise}
203  * @resolves When the operation completes with a success code.
204  * @rejects With an exception, if the operation fails.
205  */
206 function promisePumpToSaver(aSourceString, aSaverStreamListener,
207                             aCloseWhenDone) {
208   let deferred = Promise.defer();
209   aSaverStreamListener.QueryInterface(Ci.nsIStreamListener);
210   let inputStream = new StringInputStream(aSourceString, aSourceString.length);
211   let pump = Cc["@mozilla.org/network/input-stream-pump;1"]
212              .createInstance(Ci.nsIInputStreamPump);
213   pump.init(inputStream, -1, -1, 0, 0, true);
214   pump.asyncRead({
215     onStartRequest: function PPTS_onStartRequest(aRequest, aContext)
216     {
217       aSaverStreamListener.onStartRequest(aRequest, aContext);
218     },
219     onStopRequest: function PPTS_onStopRequest(aRequest, aContext, aStatusCode)
220     {
221       aSaverStreamListener.onStopRequest(aRequest, aContext, aStatusCode);
222       if (Components.isSuccessCode(aStatusCode)) {
223         deferred.resolve();
224       } else {
225         deferred.reject(new Components.Exception(aResult));
226       }
227     },
228     onDataAvailable: function PPTS_onDataAvailable(aRequest, aContext,
229                                                    aInputStream, aOffset,
230                                                    aCount)
231     {
232       aSaverStreamListener.onDataAvailable(aRequest, aContext, aInputStream,
233                                            aOffset, aCount);
234     },
235   }, null);
236   return deferred.promise;
239 let gStillRunning = true;
241 ////////////////////////////////////////////////////////////////////////////////
242 //// Tests
244 function run_test()
246   run_next_test();
249 add_task(function test_setup()
251   // Wait 10 minutes, that is half of the external xpcshell timeout.
252   do_timeout(10 * 60 * 1000, function() {
253     if (gStillRunning) {
254       do_throw("Test timed out.");
255     }
256   })
259 add_task(function test_normal()
261   // This test demonstrates the most basic use case.
262   let destFile = getTempFile(TEST_FILE_NAME_1);
264   // Create the object implementing the output stream.
265   let saver = new BackgroundFileSaverOutputStream();
267   // Set up callbacks for completion and target file name change.
268   let receivedOnTargetChange = false;
269   function onTargetChange(aTarget) {
270     do_check_true(destFile.equals(aTarget));
271     receivedOnTargetChange = true;
272   }
273   let completionPromise = promiseSaverComplete(saver, onTargetChange);
275   // Set the target file.
276   saver.setTarget(destFile, false);
278   // Write some data and close the output stream.
279   yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
281   // Indicate that we are ready to finish, and wait for a successful callback.
282   saver.finish(Cr.NS_OK);
283   yield completionPromise;
285   // Only after we receive the completion notification, we can also be sure that
286   // we've received the target file name change notification before it.
287   do_check_true(receivedOnTargetChange);
289   // Clean up.
290   destFile.remove(false);
293 add_task(function test_combinations()
295   let initialFile = getTempFile(TEST_FILE_NAME_1);
296   let renamedFile = getTempFile(TEST_FILE_NAME_2);
298   // Keep track of the current file.
299   let currentFile = null;
300   function onTargetChange(aTarget) {
301     currentFile = null;
302     do_print("Target file changed to: " + aTarget.leafName);
303     currentFile = aTarget;
304   }
306   // Tests various combinations of events and behaviors for both the stream
307   // listener and the output stream implementations.
308   for (let testFlags = 0; testFlags < 32; testFlags++) {
309     let keepPartialOnFailure = !!(testFlags & 1);
310     let renameAtSomePoint = !!(testFlags & 2);
311     let cancelAtSomePoint = !!(testFlags & 4);
312     let useStreamListener = !!(testFlags & 8);
313     let useLongData = !!(testFlags & 16);
315     let startTime = Date.now();
316     do_print("Starting keepPartialOnFailure = " + keepPartialOnFailure +
317                     ", renameAtSomePoint = " + renameAtSomePoint +
318                     ", cancelAtSomePoint = " + cancelAtSomePoint +
319                     ", useStreamListener = " + useStreamListener +
320                     ", useLongData = " + useLongData);
322     // Create the object and register the observers.
323     currentFile = null;
324     let saver = useStreamListener
325                 ? new BackgroundFileSaverStreamListener()
326                 : new BackgroundFileSaverOutputStream();
327     saver.enableSha256();
328     let completionPromise = promiseSaverComplete(saver, onTargetChange);
330     // Start feeding the first chunk of data to the saver.  In case we are using
331     // the stream listener, we only write one chunk.
332     let testData = useLongData ? TEST_DATA_LONG : TEST_DATA_SHORT;
333     let feedPromise = useStreamListener
334                       ? promisePumpToSaver(testData + testData, saver)
335                       : promiseCopyToSaver(testData, saver, false);
337     // Set a target output file.
338     saver.setTarget(initialFile, keepPartialOnFailure);
340     // Wait for the first chunk of data to be copied.
341     yield feedPromise;
343     if (renameAtSomePoint) {
344       saver.setTarget(renamedFile, keepPartialOnFailure);
345     }
347     if (cancelAtSomePoint) {
348       saver.finish(Cr.NS_ERROR_FAILURE);
349     }
351     // Feed the second chunk of data to the saver.
352     if (!useStreamListener) {
353       yield promiseCopyToSaver(testData, saver, true);
354     }
356     // Wait for completion, and ensure we succeeded or failed as expected.
357     if (!cancelAtSomePoint) {
358       saver.finish(Cr.NS_OK);
359     }
360     try {
361       yield completionPromise;
362       if (cancelAtSomePoint) {
363         do_throw("Failure expected.");
364       }
365     } catch (ex if cancelAtSomePoint && ex.result == Cr.NS_ERROR_FAILURE) { }
367     if (!cancelAtSomePoint) {
368       // In this case, the file must exist.
369       do_check_true(currentFile.exists());
370       let expectedContents = testData + testData;
371       yield promiseVerifyContents(currentFile, expectedContents);
372       do_check_eq(EXPECTED_HASHES[expectedContents.length],
373                   toHex(saver.sha256Hash));
374       currentFile.remove(false);
376       // If the target was really renamed, the old file should not exist.
377       if (renamedFile.equals(currentFile)) {
378         do_check_false(initialFile.exists());
379       }
380     } else if (!keepPartialOnFailure) {
381       // In this case, the file must not exist.
382       do_check_false(initialFile.exists());
383       do_check_false(renamedFile.exists());
384     } else {
385       // In this case, the file may or may not exist, because canceling can
386       // interrupt the asynchronous operation at any point, even before the file
387       // has been created for the first time.
388       if (initialFile.exists()) {
389         initialFile.remove(false);
390       }
391       if (renamedFile.exists()) {
392         renamedFile.remove(false);
393       }
394     }
396     do_print("Test case completed in " + (Date.now() - startTime) + " ms.");
397   }
400 add_task(function test_setTarget_after_close_stream()
402   // This test checks the case where we close the output stream before we call
403   // the setTarget method.  All the data should be buffered and written anyway.
404   let destFile = getTempFile(TEST_FILE_NAME_1);
406   // Test the case where the file does not already exists first, then the case
407   // where the file already exists.
408   for (let i = 0; i < 2; i++) {
409     let saver = new BackgroundFileSaverOutputStream();
410     saver.enableSha256();
411     let completionPromise = promiseSaverComplete(saver);
413     // Copy some data to the output stream of the file saver.  This data must
414     // be shorter than the internal component's pipe buffer for the test to
415     // succeed, because otherwise the test would block waiting for the write to
416     // complete.
417     yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
419     // Set the target file and wait for the output to finish.
420     saver.setTarget(destFile, false);
421     saver.finish(Cr.NS_OK);
422     yield completionPromise;
424     // Verify results.
425     yield promiseVerifyContents(destFile, TEST_DATA_SHORT);
426     do_check_eq(EXPECTED_HASHES[TEST_DATA_SHORT.length],
427                 toHex(saver.sha256Hash));
428   }
430   // Clean up.
431   destFile.remove(false);
434 add_task(function test_setTarget_fast()
436   // This test checks a fast rename of the target file.
437   let destFile1 = getTempFile(TEST_FILE_NAME_1);
438   let destFile2 = getTempFile(TEST_FILE_NAME_2);
439   let saver = new BackgroundFileSaverOutputStream();
440   let completionPromise = promiseSaverComplete(saver);
442   // Set the initial name after the stream is closed, then rename immediately.
443   yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
444   saver.setTarget(destFile1, false);
445   saver.setTarget(destFile2, false);
447   // Wait for all the operations to complete.
448   saver.finish(Cr.NS_OK);
449   yield completionPromise;
451   // Verify results and clean up.
452   do_check_false(destFile1.exists());
453   yield promiseVerifyContents(destFile2, TEST_DATA_SHORT);
454   destFile2.remove(false);
457 add_task(function test_setTarget_multiple()
459   // This test checks multiple renames of the target file.
460   let destFile = getTempFile(TEST_FILE_NAME_1);
461   let saver = new BackgroundFileSaverOutputStream();
462   let completionPromise = promiseSaverComplete(saver);
464   // Rename both before and after the stream is closed.
465   saver.setTarget(getTempFile(TEST_FILE_NAME_2), false);
466   saver.setTarget(getTempFile(TEST_FILE_NAME_3), false);
467   yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
468   saver.setTarget(getTempFile(TEST_FILE_NAME_2), false);
469   saver.setTarget(destFile, false);
471   // Wait for all the operations to complete.
472   saver.finish(Cr.NS_OK);
473   yield completionPromise;
475   // Verify results and clean up.
476   do_check_false(getTempFile(TEST_FILE_NAME_2).exists());
477   do_check_false(getTempFile(TEST_FILE_NAME_3).exists());
478   yield promiseVerifyContents(destFile, TEST_DATA_SHORT);
479   destFile.remove(false);
482 add_task(function test_enableAppend()
484   // This test checks append mode with hashing disabled.
485   let destFile = getTempFile(TEST_FILE_NAME_1);
487   // Test the case where the file does not already exists first, then the case
488   // where the file already exists.
489   for (let i = 0; i < 2; i++) {
490     let saver = new BackgroundFileSaverOutputStream();
491     saver.enableAppend();
492     let completionPromise = promiseSaverComplete(saver);
494     saver.setTarget(destFile, false);
495     yield promiseCopyToSaver(TEST_DATA_LONG, saver, true);
497     saver.finish(Cr.NS_OK);
498     yield completionPromise;
500     // Verify results.
501     let expectedContents = (i == 0 ? TEST_DATA_LONG
502                                    : TEST_DATA_LONG + TEST_DATA_LONG);
503     yield promiseVerifyContents(destFile, expectedContents);
504   }
506   // Clean up.
507   destFile.remove(false);
510 add_task(function test_enableAppend_setTarget_fast()
512   // This test checks a fast rename of the target file in append mode.
513   let destFile1 = getTempFile(TEST_FILE_NAME_1);
514   let destFile2 = getTempFile(TEST_FILE_NAME_2);
516   // Test the case where the file does not already exists first, then the case
517   // where the file already exists.
518   for (let i = 0; i < 2; i++) {
519     let saver = new BackgroundFileSaverOutputStream();
520     saver.enableAppend();
521     let completionPromise = promiseSaverComplete(saver);
523     yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
525     // The first time, we start appending to the first file and rename to the
526     // second file.  The second time, we start appending to the second file,
527     // that was created the first time, and rename back to the first file.
528     let firstFile = (i == 0) ? destFile1 : destFile2;
529     let secondFile = (i == 0) ? destFile2 : destFile1;
530     saver.setTarget(firstFile, false);
531     saver.setTarget(secondFile, false);
533     saver.finish(Cr.NS_OK);
534     yield completionPromise;
536     // Verify results.
537     do_check_false(firstFile.exists());
538     let expectedContents = (i == 0 ? TEST_DATA_SHORT
539                                    : TEST_DATA_SHORT + TEST_DATA_SHORT);
540     yield promiseVerifyContents(secondFile, expectedContents);
541   }
543   // Clean up.
544   destFile1.remove(false);
547 add_task(function test_enableAppend_hash()
549   // This test checks append mode, also verifying that the computed hash
550   // includes the contents of the existing data.
551   let destFile = getTempFile(TEST_FILE_NAME_1);
553   // Test the case where the file does not already exists first, then the case
554   // where the file already exists.
555   for (let i = 0; i < 2; i++) {
556     let saver = new BackgroundFileSaverOutputStream();
557     saver.enableAppend();
558     saver.enableSha256();
559     let completionPromise = promiseSaverComplete(saver);
561     saver.setTarget(destFile, false);
562     yield promiseCopyToSaver(TEST_DATA_LONG, saver, true);
564     saver.finish(Cr.NS_OK);
565     yield completionPromise;
567     // Verify results.
568     let expectedContents = (i == 0 ? TEST_DATA_LONG
569                                    : TEST_DATA_LONG + TEST_DATA_LONG);
570     yield promiseVerifyContents(destFile, expectedContents);
571     do_check_eq(EXPECTED_HASHES[expectedContents.length],
572                 toHex(saver.sha256Hash));
573   }
575   // Clean up.
576   destFile.remove(false);
579 add_task(function test_finish_only()
581   // This test checks creating the object and doing nothing.
582   let destFile = getTempFile(TEST_FILE_NAME_1);
583   let saver = new BackgroundFileSaverOutputStream();
584   function onTargetChange(aTarget) {
585     do_throw("Should not receive the onTargetChange notification.");
586   }
587   let completionPromise = promiseSaverComplete(saver, onTargetChange);
588   saver.finish(Cr.NS_OK);
589   yield completionPromise;
592 add_task(function test_empty()
594   // This test checks we still create an empty file when no data is fed.
595   let destFile = getTempFile(TEST_FILE_NAME_1);
597   let saver = new BackgroundFileSaverOutputStream();
598   let completionPromise = promiseSaverComplete(saver);
600   saver.setTarget(destFile, false);
601   yield promiseCopyToSaver("", saver, true);
603   saver.finish(Cr.NS_OK);
604   yield completionPromise;
606   // Verify results.
607   do_check_true(destFile.exists());
608   do_check_eq(destFile.fileSize, 0);
610   // Clean up.
611   destFile.remove(false);
614 add_task(function test_empty_hash()
616   // This test checks the hash of an empty file, both in normal and append mode.
617   let destFile = getTempFile(TEST_FILE_NAME_1);
619   // Test normal mode first, then append mode.
620   for (let i = 0; i < 2; i++) {
621     let saver = new BackgroundFileSaverOutputStream();
622     if (i == 1) {
623       saver.enableAppend();
624     }
625     saver.enableSha256();
626     let completionPromise = promiseSaverComplete(saver);
628     saver.setTarget(destFile, false);
629     yield promiseCopyToSaver("", saver, true);
631     saver.finish(Cr.NS_OK);
632     yield completionPromise;
634     // Verify results.
635     do_check_eq(destFile.fileSize, 0);
636     do_check_eq(EXPECTED_HASHES[0], toHex(saver.sha256Hash));
637   }
639   // Clean up.
640   destFile.remove(false);
643 add_task(function test_invalid_hash()
645   let saver = new BackgroundFileSaverStreamListener();
646   let completionPromise = promiseSaverComplete(saver);
647   // We shouldn't be able to get the hash if hashing hasn't been enabled
648   try {
649     let hash = saver.sha256Hash;
650     do_throw("Shouldn't be able to get hash if hashing not enabled");
651   } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { }
652   // Enable hashing, but don't feed any data to saver
653   saver.enableSha256();
654   let destFile = getTempFile(TEST_FILE_NAME_1);
655   saver.setTarget(destFile, false);
656   // We don't wait on promiseSaverComplete, so the hash getter can run before
657   // or after onSaveComplete is called. However, the expected behavior is the
658   // same in both cases since the hash is only valid when the save completes
659   // successfully.
660   saver.finish(Cr.NS_ERROR_FAILURE);
661   try {
662     let hash = saver.sha256Hash;
663     do_throw("Shouldn't be able to get hash if save did not succeed");
664   } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { }
665   // Wait for completion so that the worker thread finishes dealing with the
666   // target file. We expect it to fail.
667   try {
668     yield completionPromise;
669     do_throw("completionPromise should throw");
670   } catch (ex if ex.result == Cr.NS_ERROR_FAILURE) { }
673 add_task(function test_signature()
675   // Check that we get a signature if the saver is finished.
676   let destFile = getTempFile(TEST_FILE_NAME_1);
678   let saver = new BackgroundFileSaverOutputStream();
679   let completionPromise = promiseSaverComplete(saver);
681   try {
682     let signatureInfo = saver.signatureInfo;
683     do_throw("Can't get signature if saver is not complete");
684   } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { }
686   saver.enableSignatureInfo();
687   saver.setTarget(destFile, false);
688   yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
690   saver.finish(Cr.NS_OK);
691   yield completionPromise;
692   yield promiseVerifyContents(destFile, TEST_DATA_SHORT);
694   // signatureInfo is an empty nsIArray
695   do_check_eq(0, saver.signatureInfo.length);
697   // Clean up.
698   destFile.remove(false);
701 add_task(function test_signature_not_enabled()
703   // Check that we get a signature if the saver is finished on Windows.
704   let destFile = getTempFile(TEST_FILE_NAME_1);
706   let saver = new BackgroundFileSaverOutputStream();
707   let completionPromise = promiseSaverComplete(saver);
708   saver.setTarget(destFile, false);
709   yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
711   saver.finish(Cr.NS_OK);
712   yield completionPromise;
713   try {
714     let signatureInfo = saver.signatureInfo;
715     do_throw("Can't get signature if not enabled");
716   } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { }
718   // Clean up.
719   destFile.remove(false);
722 add_task(function test_teardown()
724   gStillRunning = false;