Bug 961665 - Tests for OS.File.read. r=froydnj
☠☠ backed out by f547db6d8bc8 ☠ ☠
authorDavid Rajchenbach-Teller <dteller@mozilla.com>
Thu, 13 Mar 2014 09:52:07 -0400
changeset 191678 c6ca1aa3887a9b20452025a25a0c5d9616b77cae
parent 191677 d741e117a0332b13e60a9cb38b7cda2ace659e85
child 191679 6cecd6870c6925c023c74aa53d679f2b24af21de
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs961665
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 961665 - Tests for OS.File.read. r=froydnj
toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
toolkit/components/osfile/tests/xpcshell/head.js
toolkit/components/osfile/tests/xpcshell/test_creationDate.js
toolkit/components/osfile/tests/xpcshell/test_exception.js
toolkit/components/osfile/tests/xpcshell/test_open.js
toolkit/components/osfile/tests/xpcshell/test_path_constants.js
toolkit/components/osfile/tests/xpcshell/test_read_write.js
toolkit/components/osfile/tests/xpcshell/xpcshell.ini
toolkit/crashreporter/test/unit/test_crash_AsyncShutdown.js
--- a/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
+++ b/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
@@ -150,17 +150,16 @@ let test = maketest("Main", function mai
   return Task.spawn(function() {
     SimpleTest.waitForExplicitFinish();
     yield test_constants();
     yield test_path();
     yield test_stat();
     yield test_debug();
     yield test_info_features_detect();
     yield test_read_write();
-    yield test_read_write_all();
     yield test_position();
     yield test_iter();
     yield test_exists();
     yield test_debug_test();
     info("Test is over");
     SimpleTest.finish();
   });
 });
@@ -298,102 +297,16 @@ let test_read_write = maketest("read_wri
     test.is(stat.size, size, "Both files have the same size");
     yield reference_compare_files(pathSource, pathDest, test);
 
     // Cleanup.
     OS.File.remove(pathDest);
   });
 });
 
-/**
- * Test OS.File.writeAtomic
- */
-let test_read_write_all = maketest("read_write_all", function read_write_all(test) {
-  return Task.spawn(function() {
-    let pathDest = OS.Path.join(OS.Constants.Path.tmpDir,
-      "osfile async test read writeAtomic.tmp");
-    let tmpPath = pathDest + ".tmp";
-
-    let test_with_options = function(options, suffix) {
-      return Task.spawn(function() {
-        let optionsBackup = JSON.parse(JSON.stringify(options));
-
-        // Check that read + writeAtomic performs a correct copy
-        let currentDir = yield OS.File.getCurrentDirectory();
-        let pathSource = OS.Path.join(currentDir, EXISTING_FILE);
-        let contents = yield OS.File.read(pathSource);
-        test.ok(contents, "Obtained contents");
-        let bytesWritten = yield OS.File.writeAtomic(pathDest, contents, options);
-        test.is(contents.byteLength, bytesWritten, "Wrote the correct number of bytes (" + suffix + ")");
-
-        // Check that options are not altered
-        test.is(Object.keys(options).length, Object.keys(optionsBackup).length,
-          "The number of options was not changed");
-        for (let k in options) {
-          test.is(options[k], optionsBackup[k], "Option was not changed (" + suffix + ")");
-        }
-        yield reference_compare_files(pathSource, pathDest, test);
-
-        // Check that temporary file was removed or doesn't exist
-        test.info("Compare complete");
-        test.ok(!(new FileUtils.File(tmpPath).exists()), "No temporary file at the end of the run (" + suffix + ")");
-
-        // Check that writeAtomic fails if noOverwrite is true and the destination
-        // file already exists!
-        let view = new Uint8Array(contents.buffer, 10, 200);
-        try {
-          let opt = JSON.parse(JSON.stringify(options));
-          opt.noOverwrite = true;
-          yield OS.File.writeAtomic(pathDest, view, opt);
-          test.fail("With noOverwrite, writeAtomic should have refused to overwrite file (" + suffix + ")");
-        } catch (err) {
-          test.info("With noOverwrite, writeAtomic correctly failed (" + suffix + ")");
-          test.ok(err instanceof OS.File.Error, "writeAtomic correctly failed with a file error (" + suffix + ")");
-          test.ok(err.becauseExists, "writeAtomic file error confirmed that the file already exists (" + suffix + ")");
-        }
-        yield reference_compare_files(pathSource, pathDest, test);
-        test.ok(!(new FileUtils.File(tmpPath).exists()), "Temporary file was removed");
-
-        // Now write a subset
-        let START = 10;
-        let LENGTH = 100;
-        view = new Uint8Array(contents.buffer, START, LENGTH);
-        bytesWritten = yield OS.File.writeAtomic(pathDest, view, options);
-        test.is(bytesWritten, LENGTH, "Partial write wrote the correct number of bytes (" + suffix + ")");
-        let array2 = yield OS.File.read(pathDest);
-        let view1 = new Uint8Array(contents.buffer, START, LENGTH);
-        test.is(view1.length, array2.length, "Re-read partial write with the correct number of bytes (" + suffix + ")");
-        let decoder = new TextDecoder();
-        test.is(decoder.decode(view1), decoder.decode(array2), "Comparing re-read of partial write (" + suffix + ")");
-
-        // Write strings, default encoding
-        let ARBITRARY_STRING = "aeiouyâêîôûçß•";
-        yield OS.File.writeAtomic(pathDest, ARBITRARY_STRING, options);
-        let array = yield OS.File.read(pathDest);
-        let IN_STRING = decoder.decode(array);
-        test.is(ARBITRARY_STRING, IN_STRING, "String write + read with default encoding works (" + suffix + ")");
-
-        let opt16 = JSON.parse(JSON.stringify(options));
-        opt16.encoding = "utf-16";
-        yield OS.File.writeAtomic(pathDest, ARBITRARY_STRING, opt16);
-        array = yield OS.File.read(pathDest);
-        IN_STRING = (new TextDecoder("utf-16")).decode(array);
-        test.is(ARBITRARY_STRING, IN_STRING, "String write + read with utf-16 encoding works (" + suffix + ")");
-
-        // Cleanup.
-        OS.File.remove(pathDest);
-      });
-    };
-
-    yield test_with_options({tmpPath: tmpPath}, "Renaming, not flushing");
-    yield test_with_options({tmpPath: tmpPath, flush: true}, "Renaming, flushing");
-    yield test_with_options({}, "Not renaming, not flushing");
-    yield test_with_options({flush: true}, "Not renaming, flushing");
-  });
-});
 
 /**
  * Test file.{getPosition, setPosition}
  */
 let test_position = maketest("position", function position(test) {
   return Task.spawn(function() {
     let file = yield OS.File.open(EXISTING_FILE);
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/osfile/tests/xpcshell/head.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let {utils: Cu, interfaces: Ci} = Components;
+
+let {OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
+let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+let {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
+let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
+let {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
+let {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
+
+Services.prefs.setBoolPref("toolkit.osfile.log", true);
+
+/**
+ * As add_task, but execute the test both with native operations and
+ * without.
+ */
+function add_test_pair(generator) {
+  add_task(function*() {
+    do_print("Executing test " + generator.name + " with native operations");
+    Services.prefs.setBoolPref("toolkit.osfile.native", true);
+    return Task.spawn(generator);
+  });
+  add_task(function*() {
+    do_print("Executing test " + generator.name + " without native operations");
+    Services.prefs.setBoolPref("toolkit.osfile.native", false);
+    return Task.spawn(generator);
+  });
+}
+
+/**
+ * Fetch asynchronously the contents of a file using xpcom.
+ *
+ * Used for comparing xpcom-based results to os.file-based results.
+ *
+ * @param {string} path The _absolute_ path to the file.
+ * @return {promise}
+ * @resolves {string} The contents of the file.
+ */
+function reference_fetch_file(path, test) {
+  do_print("Fetching file " + path);
+  let deferred = Promise.defer();
+  let file = new FileUtils.File(path);
+  NetUtil.asyncFetch(file,
+    function(stream, status) {
+      if (!Components.isSuccessCode(status)) {
+        deferred.reject(status);
+        return;
+      }
+      let result, reject;
+      try {
+        result = NetUtil.readInputStreamToString(stream, stream.available());
+      } catch (x) {
+        reject = x;
+      }
+      stream.close();
+      if (reject) {
+        deferred.reject(reject);
+      } else {
+        deferred.resolve(result);
+      }
+  });
+  return deferred.promise;
+};
+
+/**
+ * Compare asynchronously the contents two files using xpcom.
+ *
+ * Used for comparing xpcom-based results to os.file-based results.
+ *
+ * @param {string} a The _absolute_ path to the first file.
+ * @param {string} b The _absolute_ path to the second file.
+ *
+ * @resolves {null}
+ */
+function reference_compare_files(a, b, test) {
+  return Task.spawn(function*() {
+    do_print("Comparing files " + a + " and " + b);
+    let a_contents = yield reference_fetch_file(a, test);
+    let b_contents = yield reference_fetch_file(b, test);
+    do_check_eq(a_contents, b_contents);
+  });
+};
--- a/toolkit/components/osfile/tests/xpcshell/test_creationDate.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_creationDate.js
@@ -1,18 +1,10 @@
 "use strict";
 
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/osfile.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
-
 function run_test() {
   do_test_pending();
   run_next_test();
 }
 
 /**
  * Test to ensure that deprecation warning is issued on use
  * of creationDate.
--- a/toolkit/components/osfile/tests/xpcshell/test_exception.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_exception.js
@@ -1,27 +1,89 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/**
+ * Test that functions throw the appropriate exceptions.
+ */
+
 "use strict";
 
-Components.utils.import("resource://gre/modules/osfile.jsm");
+let EXISTING_FILE = do_get_file("xpcshell.ini").path;
+
 
-function run_test() {
-  do_test_pending();
-  run_next_test();
-}
+// Tests on |open|
 
-add_task(function test_typeerror() {
+add_test_pair(function test_typeerror() {
   let exn;
   try {
     let fd = yield OS.File.open("/tmp", {no_such_key: 1});
     do_print("Fd: " + fd);
   } catch (ex) {
     exn = ex;
   }
   do_print("Exception: " + exn);
   do_check_true(exn.constructor.name == "TypeError");
 });
 
-add_task(function() {
-  do_test_finished();
+// Tests on |read|
+
+add_test_pair(function* test_bad_encoding() {
+  do_print("Testing with a wrong encoding");
+  try {
+    yield OS.File.read(EXISTING_FILE, { encoding: "baby-speak-encoded" });
+    do_throw("Should have thrown with an ex.becauseInvalidArgument");
+  } catch (ex if ex.becauseInvalidArgument) {
+    do_print("Wrong encoding caused the correct exception");
+  }
+
+  try {
+    yield OS.File.read(EXISTING_FILE, { encoding: 4 });
+    do_throw("Should have thrown a TypeError");
+  } catch (ex if ex.constructor.name == "TypeError") {
+    // Note that TypeError doesn't carry across compartments
+    do_print("Non-string encoding caused the correct exception");
+  }
+ });
+
+add_test_pair(function* test_bad_compression() {
+  do_print("Testing with a non-existing compression");
+  try {
+    yield OS.File.read(EXISTING_FILE, { compression: "mmmh-crunchy" });
+    do_throw("Should have thrown with an ex.becauseInvalidArgument");
+  } catch (ex if ex.becauseInvalidArgument) {
+    do_print("Wrong encoding caused the correct exception");
+  }
+
+  do_print("Testing with a bad type for option compression");
+  try {
+    yield OS.File.read(EXISTING_FILE, { compression: 5 });
+    do_throw("Should have thrown a TypeError");
+  } catch (ex if ex.constructor.name == "TypeError") {
+    // Note that TypeError doesn't carry across compartments
+    do_print("Non-string encoding caused the correct exception");
+  }
 });
+
+add_test_pair(function* test_bad_bytes() {
+  do_print("Testing with a bad type for option bytes");
+  try {
+    yield OS.File.read(EXISTING_FILE, { bytes: "five" });
+    do_throw("Should have thrown a TypeError");
+  } catch (ex if ex.constructor.name == "TypeError") {
+    // Note that TypeError doesn't carry across compartments
+    do_print("Non-number bytes caused the correct exception");
+  }
+});
+
+add_test_pair(function* read_non_existent() {
+  do_print("Testing with a non-existent file");
+  try {
+    yield OS.File.read("I/do/not/exist");
+    do_throw("Should have thrown with an ex.becauseNoSuchFile");
+  } catch (ex if ex.becauseNoSuchFile) {
+    do_print("Correct exceptions");
+  }
+});
+
+function run_test() {
+  run_next_test();
+}
--- a/toolkit/components/osfile/tests/xpcshell/test_open.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_open.js
@@ -1,17 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 Components.utils.import("resource://gre/modules/osfile.jsm");
 
 function run_test() {
-  do_test_pending();
   run_next_test();
 }
 
 /**
  * Test OS.File.open for reading:
  * - with an existing file (should succeed);
  * - with a non-existing file (should fail);
  * - with inconsistent arguments (should fail).
@@ -64,12 +63,8 @@ add_task(function test_error_attributes 
   try {
     yield OS.File.open(fpath, {truncate: true}, {});
     do_check_true(false, "Opening path suceeded (it should fail) " + fpath);
   } catch (err) {
     do_check_true(err instanceof OS.File.Error);
     do_check_true(err.becauseNoSuchFile);
   }
 });
-
-add_task(function() {
-  do_test_finished();
-});
--- a/toolkit/components/osfile/tests/xpcshell/test_path_constants.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_path_constants.js
@@ -1,18 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/osfile.jsm", this);
-Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/ctypes.jsm", this);
 Cu.import("resource://testing-common/AppData.jsm", this);
 
 
 function run_test() {
   run_next_test();
 }
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/osfile/tests/xpcshell/test_read_write.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let {utils: Cu} = Components;
+
+let SHARED_PATH;
+
+let EXISTING_FILE = do_get_file("xpcshell.ini").path;
+
+add_task(function* init() {
+  do_get_profile();
+  SHARED_PATH = OS.Path.join(OS.Constants.Path.profileDir, "test_osfile_read.tmp");
+});
+
+
+// Check that OS.File.read() is executed after the previous operation
+add_test_pair(function* ordering() {
+  let string1 = "Initial state " + Math.random();
+  let string2 = "After writing " + Math.random();
+  yield OS.File.writeAtomic(SHARED_PATH, string1);
+  OS.File.writeAtomic(SHARED_PATH, string2);
+  let string3 = yield OS.File.read(SHARED_PATH, { encoding: "utf-8" });
+  do_check_eq(string3, string2);
+});
+
+add_test_pair(function* read_write_all() {
+  let DEST_PATH = SHARED_PATH + Math.random();
+  let TMP_PATH = DEST_PATH + ".tmp";
+
+  let test_with_options = function(options, suffix) {
+    return Task.spawn(function*() {
+      do_print("Running test read_write_all with options " + JSON.stringify(options));
+      let TEST = "read_write_all " + suffix;
+
+      let optionsBackup = JSON.parse(JSON.stringify(options));
+
+      // Check that read + writeAtomic performs a correct copy
+      let currentDir = yield OS.File.getCurrentDirectory();
+      let pathSource = OS.Path.join(currentDir, EXISTING_FILE);
+      let contents = yield OS.File.read(pathSource);
+      do_check_true(!!contents); // Content is not empty
+
+      let bytesWritten = yield OS.File.writeAtomic(DEST_PATH, contents, options);
+      do_check_eq(contents.byteLength, bytesWritten); // Correct number of bytes written
+
+      // Check that options are not altered
+      do_check_eq(JSON.stringify(options), JSON.stringify(optionsBackup));
+      yield reference_compare_files(pathSource, DEST_PATH, TEST);
+
+      // Check that temporary file was removed or never created exist
+      do_check_false(new FileUtils.File(TMP_PATH).exists());
+
+      // Check that writeAtomic fails if noOverwrite is true and the destination
+      // file already exists!
+      let view = new Uint8Array(contents.buffer, 10, 200);
+      try {
+        let opt = JSON.parse(JSON.stringify(options));
+        opt.noOverwrite = true;
+        yield OS.File.writeAtomic(DEST_PATH, view, opt);
+        do_throw("With noOverwrite, writeAtomic should have refused to overwrite file (" + suffix + ")");
+      } catch (err if err instanceof OS.File.Error && err.becauseExists) {
+        do_print("With noOverwrite, writeAtomic correctly failed (" + suffix + ")");
+      }
+      yield reference_compare_files(pathSource, DEST_PATH, TEST);
+
+      // Check that temporary file was removed or never created
+      do_check_false(new FileUtils.File(TMP_PATH).exists());
+
+      // Now write a subset
+      let START = 10;
+      let LENGTH = 100;
+      view = new Uint8Array(contents.buffer, START, LENGTH);
+      bytesWritten = yield OS.File.writeAtomic(DEST_PATH, view, options);
+      do_check_eq(bytesWritten, LENGTH);
+
+      let array2 = yield OS.File.read(DEST_PATH);
+      let view1 = new Uint8Array(contents.buffer, START, LENGTH);
+      do_check_eq(view1.length, array2.length);
+      let decoder = new TextDecoder();
+      do_check_eq(decoder.decode(view1), decoder.decode(array2));
+
+
+      // Cleanup.
+      yield OS.File.remove(DEST_PATH);
+      yield OS.File.remove(TMP_PATH);
+    });
+  };
+
+  yield test_with_options({tmpPath: TMP_PATH}, "Renaming, not flushing");
+  yield test_with_options({tmpPath: TMP_PATH, flush: true}, "Renaming, flushing");
+  yield test_with_options({}, "Not renaming, not flushing");
+  yield test_with_options({flush: true}, "Not renaming, flushing");
+});
+
+
+function run_test() {
+  run_next_test();
+}
--- a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini
+++ b/toolkit/components/osfile/tests/xpcshell/xpcshell.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-head =
+head = head.js
 tail =
 
 [test_available_free_space.js]
 [test_osfile_closed.js]
 [test_path.js]
 [test_osfile_async.js]
 [test_osfile_async_append.js]
 [test_osfile_async_bytes.js]
@@ -20,11 +20,12 @@ tail =
 [test_path_constants.js]
 [test_removeDir.js]
 [test_reset.js]
 [test_shutdown.js]
 [test_unique.js]
 [test_open.js]
 [test_telemetry.js]
 [test_duration.js]
+[test_read_write.js]
 [test_compression.js]
 [test_osfile_writeAtomic_backupTo_option.js]
 [test_osfile_error.js]
--- a/toolkit/crashreporter/test/unit/test_crash_AsyncShutdown.js
+++ b/toolkit/crashreporter/test/unit/test_crash_AsyncShutdown.js
@@ -35,16 +35,17 @@ function after_crash(mdump, extra) {
 // the latest operation succeeded
 
 function setup_osfile_crash_noerror() {
   Components.utils.import("resource://gre/modules/Services.jsm", this);
   Components.utils.import("resource://gre/modules/osfile.jsm", this);
 
   Services.prefs.setBoolPref("toolkit.osfile.debug.failshutdown", true);
   Services.prefs.setIntPref("toolkit.asyncshutdown.crash_timeout", 1);
+  Services.prefs.setBoolPref("toolkit.osfile.native", false);
 
   OS.File.getCurrentDirectory();
   Services.obs.notifyObservers(null, "profile-before-change", null);
   dump("Waiting for crash\n");
 };
 
 function after_osfile_crash_noerror(mdump, extra) {
   do_print("after OS.File crash: " + JSON.stringify(extra.AsyncShutdownTimeout));
@@ -63,29 +64,29 @@ function after_osfile_crash_noerror(mdum
 // the latest operation failed
 
 function setup_osfile_crash_exn() {
   Components.utils.import("resource://gre/modules/Services.jsm", this);
   Components.utils.import("resource://gre/modules/osfile.jsm", this);
 
   Services.prefs.setBoolPref("toolkit.osfile.debug.failshutdown", true);
   Services.prefs.setIntPref("toolkit.asyncshutdown.crash_timeout", 1);
+  Services.prefs.setBoolPref("toolkit.osfile.native", false);
 
   OS.File.read("I do not exist");
   Services.obs.notifyObservers(null, "profile-before-change", null);
   dump("Waiting for crash\n");
 };
 
 function after_osfile_crash_exn(mdump, extra) {
   do_print("after OS.File crash: " + JSON.stringify(extra.AsyncShutdownTimeout));
   let info = JSON.parse(extra.AsyncShutdownTimeout);
   let state = info.conditions[0].state;
   do_print("Keys: " + Object.keys(state).join(", "));
   do_check_eq(info.phase, "profile-before-change");
-  do_check_true(state.launched);
   do_check_false(state.shutdown);
   do_check_true(state.worker);
   do_check_true(!!state.latestSent);
   do_check_eq(state.latestSent[1], "read");
 }
 
 function run_test() {
   do_crash(setup_crash, after_crash);