netwerk/test/unit/test_backgroundfilesaver.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:cea54dc25ebf
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/ */
5
6 /**
7 * This file tests components that implement nsIBackgroundFileSaver.
8 */
9
10 ////////////////////////////////////////////////////////////////////////////////
11 //// Globals
12
13 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
14
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");
23
24 const BackgroundFileSaverOutputStream = Components.Constructor(
25 "@mozilla.org/network/background-file-saver;1?mode=outputstream",
26 "nsIBackgroundFileSaver");
27
28 const BackgroundFileSaverStreamListener = Components.Constructor(
29 "@mozilla.org/network/background-file-saver;1?mode=streamlistener",
30 "nsIBackgroundFileSaver");
31
32 const StringInputStream = Components.Constructor(
33 "@mozilla.org/io/string-input-stream;1",
34 "nsIStringInputStream",
35 "setData");
36
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";
42
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"
55 };
56
57 const gTextDecoder = new TextDecoder();
58
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);
64
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;
77 }
78
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;
92 }
93
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.
101 *
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;
122 }
123
124 /**
125 * Waits for the given saver object to complete.
126 *
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.
131 *
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;
155 }
156
157 /**
158 * Feeds a string to a BackgroundFileSaverOutputStream.
159 *
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.
166 *
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;
190 }
191
192 /**
193 * Feeds a string to a BackgroundFileSaverStreamListener.
194 *
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.
201 *
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;
237 }
238
239 let gStillRunning = true;
240
241 ////////////////////////////////////////////////////////////////////////////////
242 //// Tests
243
244 function run_test()
245 {
246 run_next_test();
247 }
248
249 add_task(function test_setup()
250 {
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 })
257 });
258
259 add_task(function test_normal()
260 {
261 // This test demonstrates the most basic use case.
262 let destFile = getTempFile(TEST_FILE_NAME_1);
263
264 // Create the object implementing the output stream.
265 let saver = new BackgroundFileSaverOutputStream();
266
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);
274
275 // Set the target file.
276 saver.setTarget(destFile, false);
277
278 // Write some data and close the output stream.
279 yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
280
281 // Indicate that we are ready to finish, and wait for a successful callback.
282 saver.finish(Cr.NS_OK);
283 yield completionPromise;
284
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);
288
289 // Clean up.
290 destFile.remove(false);
291 });
292
293 add_task(function test_combinations()
294 {
295 let initialFile = getTempFile(TEST_FILE_NAME_1);
296 let renamedFile = getTempFile(TEST_FILE_NAME_2);
297
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 }
305
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);
314
315 let startTime = Date.now();
316 do_print("Starting keepPartialOnFailure = " + keepPartialOnFailure +
317 ", renameAtSomePoint = " + renameAtSomePoint +
318 ", cancelAtSomePoint = " + cancelAtSomePoint +
319 ", useStreamListener = " + useStreamListener +
320 ", useLongData = " + useLongData);
321
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);
329
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);
336
337 // Set a target output file.
338 saver.setTarget(initialFile, keepPartialOnFailure);
339
340 // Wait for the first chunk of data to be copied.
341 yield feedPromise;
342
343 if (renameAtSomePoint) {
344 saver.setTarget(renamedFile, keepPartialOnFailure);
345 }
346
347 if (cancelAtSomePoint) {
348 saver.finish(Cr.NS_ERROR_FAILURE);
349 }
350
351 // Feed the second chunk of data to the saver.
352 if (!useStreamListener) {
353 yield promiseCopyToSaver(testData, saver, true);
354 }
355
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) { }
366
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);
375
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 }
395
396 do_print("Test case completed in " + (Date.now() - startTime) + " ms.");
397 }
398 });
399
400 add_task(function test_setTarget_after_close_stream()
401 {
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);
405
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);
412
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);
418
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;
423
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 }
429
430 // Clean up.
431 destFile.remove(false);
432 });
433
434 add_task(function test_setTarget_fast()
435 {
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);
441
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);
446
447 // Wait for all the operations to complete.
448 saver.finish(Cr.NS_OK);
449 yield completionPromise;
450
451 // Verify results and clean up.
452 do_check_false(destFile1.exists());
453 yield promiseVerifyContents(destFile2, TEST_DATA_SHORT);
454 destFile2.remove(false);
455 });
456
457 add_task(function test_setTarget_multiple()
458 {
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);
463
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);
470
471 // Wait for all the operations to complete.
472 saver.finish(Cr.NS_OK);
473 yield completionPromise;
474
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);
480 });
481
482 add_task(function test_enableAppend()
483 {
484 // This test checks append mode with hashing disabled.
485 let destFile = getTempFile(TEST_FILE_NAME_1);
486
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);
493
494 saver.setTarget(destFile, false);
495 yield promiseCopyToSaver(TEST_DATA_LONG, saver, true);
496
497 saver.finish(Cr.NS_OK);
498 yield completionPromise;
499
500 // Verify results.
501 let expectedContents = (i == 0 ? TEST_DATA_LONG
502 : TEST_DATA_LONG + TEST_DATA_LONG);
503 yield promiseVerifyContents(destFile, expectedContents);
504 }
505
506 // Clean up.
507 destFile.remove(false);
508 });
509
510 add_task(function test_enableAppend_setTarget_fast()
511 {
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);
515
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);
522
523 yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
524
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);
532
533 saver.finish(Cr.NS_OK);
534 yield completionPromise;
535
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 }
542
543 // Clean up.
544 destFile1.remove(false);
545 });
546
547 add_task(function test_enableAppend_hash()
548 {
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);
552
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);
560
561 saver.setTarget(destFile, false);
562 yield promiseCopyToSaver(TEST_DATA_LONG, saver, true);
563
564 saver.finish(Cr.NS_OK);
565 yield completionPromise;
566
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 }
574
575 // Clean up.
576 destFile.remove(false);
577 });
578
579 add_task(function test_finish_only()
580 {
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;
590 });
591
592 add_task(function test_empty()
593 {
594 // This test checks we still create an empty file when no data is fed.
595 let destFile = getTempFile(TEST_FILE_NAME_1);
596
597 let saver = new BackgroundFileSaverOutputStream();
598 let completionPromise = promiseSaverComplete(saver);
599
600 saver.setTarget(destFile, false);
601 yield promiseCopyToSaver("", saver, true);
602
603 saver.finish(Cr.NS_OK);
604 yield completionPromise;
605
606 // Verify results.
607 do_check_true(destFile.exists());
608 do_check_eq(destFile.fileSize, 0);
609
610 // Clean up.
611 destFile.remove(false);
612 });
613
614 add_task(function test_empty_hash()
615 {
616 // This test checks the hash of an empty file, both in normal and append mode.
617 let destFile = getTempFile(TEST_FILE_NAME_1);
618
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);
627
628 saver.setTarget(destFile, false);
629 yield promiseCopyToSaver("", saver, true);
630
631 saver.finish(Cr.NS_OK);
632 yield completionPromise;
633
634 // Verify results.
635 do_check_eq(destFile.fileSize, 0);
636 do_check_eq(EXPECTED_HASHES[0], toHex(saver.sha256Hash));
637 }
638
639 // Clean up.
640 destFile.remove(false);
641 });
642
643 add_task(function test_invalid_hash()
644 {
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) { }
671 });
672
673 add_task(function test_signature()
674 {
675 // Check that we get a signature if the saver is finished.
676 let destFile = getTempFile(TEST_FILE_NAME_1);
677
678 let saver = new BackgroundFileSaverOutputStream();
679 let completionPromise = promiseSaverComplete(saver);
680
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) { }
685
686 saver.enableSignatureInfo();
687 saver.setTarget(destFile, false);
688 yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
689
690 saver.finish(Cr.NS_OK);
691 yield completionPromise;
692 yield promiseVerifyContents(destFile, TEST_DATA_SHORT);
693
694 // signatureInfo is an empty nsIArray
695 do_check_eq(0, saver.signatureInfo.length);
696
697 // Clean up.
698 destFile.remove(false);
699 });
700
701 add_task(function test_signature_not_enabled()
702 {
703 // Check that we get a signature if the saver is finished on Windows.
704 let destFile = getTempFile(TEST_FILE_NAME_1);
705
706 let saver = new BackgroundFileSaverOutputStream();
707 let completionPromise = promiseSaverComplete(saver);
708 saver.setTarget(destFile, false);
709 yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
710
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) { }
717
718 // Clean up.
719 destFile.remove(false);
720 });
721
722 add_task(function test_teardown()
723 {
724 gStillRunning = false;
725 });

mercurial