Bug 836206 - Split out or rewrite CommonUtils.jsonLoad/jsonSave. r=gps
authorRichard Newman <rnewman@mozilla.com>
Sun, 03 Feb 2013 00:51:26 -0800
changeset 120926 58b601eae9a7d4cac946c3d12eb310c00fe95929
parent 120925 8d651840659725707b6a140bef13cac7d1c494d0
child 120927 a9e70ab45ca22123b157e59777a23d5aadd96760
push id24268
push usergszorc@mozilla.com
push dateWed, 06 Feb 2013 06:42:42 +0000
treeherdermozilla-central@bc108d2ce8d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs836206
milestone21.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 836206 - Split out or rewrite CommonUtils.jsonLoad/jsonSave. r=gps
services/common/tests/unit/test_utils_json.js
services/common/utils.js
services/sync/modules/util.js
services/sync/tests/unit/test_utils_json.js
services/sync/tests/unit/xpcshell.ini
--- a/services/common/tests/unit/test_utils_json.js
+++ b/services/common/tests/unit/test_utils_json.js
@@ -1,120 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://gre/modules/osfile.jsm")
 
 function run_test() {
   initTestLogging();
   run_next_test();
 }
 
-add_test(function test_roundtrip() {
-  _("Do a simple write of an array to json and read");
-  CommonUtils.jsonSave("foo", {}, ["v1", "v2"], ensureThrows(function(error) {
-    do_check_eq(error, null);
-
-    CommonUtils.jsonLoad("foo", {}, ensureThrows(function(val) {
-      let foo = val;
-      do_check_eq(typeof foo, "object");
-      do_check_eq(foo.length, 2);
-      do_check_eq(foo[0], "v1");
-      do_check_eq(foo[1], "v2");
-      run_next_test();
-    }));
-  }));
-});
-
-add_test(function test_string() {
-  _("Try saving simple strings");
-  CommonUtils.jsonSave("str", {}, "hi", ensureThrows(function(error) {
-    do_check_eq(error, null);
-
-    CommonUtils.jsonLoad("str", {}, ensureThrows(function(val) {
-      let str = val;
-      do_check_eq(typeof str, "string");
-      do_check_eq(str.length, 2);
-      do_check_eq(str[0], "h");
-      do_check_eq(str[1], "i");
-      run_next_test();
-    }));
-  }));
-});
-
-add_test(function test_number() {
-  _("Try saving a number");
-  CommonUtils.jsonSave("num", {}, 42, ensureThrows(function(error) {
-    do_check_eq(error, null);
-
-    CommonUtils.jsonLoad("num", {}, ensureThrows(function(val) {
-      let num = val;
-      do_check_eq(typeof num, "number");
-      do_check_eq(num, 42);
-      run_next_test();
-    }));
-  }));
-});
-
-add_test(function test_nonexistent_file() {
-  _("Try loading a non-existent file.");
-  CommonUtils.jsonLoad("non-existent", {}, ensureThrows(function(val) {
-    do_check_eq(val, undefined);
-    run_next_test();
-  }));
-});
-
-add_test(function test_save_logging() {
-  _("Verify that writes are logged.");
-  let trace;
-  CommonUtils.jsonSave("log", {_log: {trace: function(msg) { trace = msg; }}},
-                       "hi", ensureThrows(function () {
-    do_check_true(!!trace);
-    run_next_test();
-  }));
-});
-
-add_test(function test_load_logging() {
-  _("Verify that reads and read errors are logged.");
-
-  // Write a file with some invalid JSON
-  let filePath = "log.json";
-  let file = FileUtils.getFile("ProfD", filePath.split("/"), true);
-  let fos = Cc["@mozilla.org/network/file-output-stream;1"]
-              .createInstance(Ci.nsIFileOutputStream);
-  let flags = FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE
-              | FileUtils.MODE_TRUNCATE;
-  fos.init(file, flags, FileUtils.PERMS_FILE, fos.DEFER_OPEN);
-  let stream = Cc["@mozilla.org/intl/converter-output-stream;1"]
-                 .createInstance(Ci.nsIConverterOutputStream);
-  stream.init(fos, "UTF-8", 4096, 0x0000);
-  stream.writeString("invalid json!");
-  stream.close();
-
-  let trace, debug;
-  let obj = {
-    _log: {
-      trace: function(msg) {
-        trace = msg;
-      },
-      debug: function(msg) {
-        debug = msg;
-      }
-    }
-  };
-  CommonUtils.jsonLoad("log", obj, ensureThrows(function(val) {
-    do_check_true(!val);
-    do_check_true(!!trace);
-    do_check_true(!!debug);
-    run_next_test();
-  }));
-});
-
 add_test(function test_writeJSON_readJSON() {
   _("Round-trip some JSON through the promise-based JSON writer.");
 
   let contents = {
     "a": 12345.67,
     "b": {
       "c": "héllö",
     },
--- a/services/common/utils.js
+++ b/services/common/utils.js
@@ -1,18 +1,16 @@
 /* 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/. */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 this.EXPORTED_SYMBOLS = ["CommonUtils"];
 
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/osfile.jsm")
 Cu.import("resource://services-common/log4moz.js");
 
 this.CommonUtils = {
   exceptionStr: function exceptionStr(e) {
     let message = e.message ? e.message : e;
@@ -352,100 +350,16 @@ this.CommonUtils = {
    * @return a promise, as produced by OS.File.writeAtomic.
    */
   writeJSON: function(contents, path) {
     let encoder = new TextEncoder();
     let array = encoder.encode(JSON.stringify(contents));
     return OS.File.writeAtomic(path, array, {tmpPath: path + ".tmp"});
   },
 
-  /**
-   * Load a JSON file from disk in the profile directory.
-   *
-   * @param filePath
-   *        JSON file path load from profile. Loaded file will be
-   *        <profile>/<filePath>.json. i.e. Do not specify the ".json"
-   *        extension.
-   * @param that
-   *        Object to use for logging and "this" for callback.
-   * @param callback
-   *        Function to process json object as its first argument. If the file
-   *        could not be loaded, the first argument will be undefined.
-   */
-  jsonLoad: function jsonLoad(filePath, that, callback) {
-    let path = filePath + ".json";
-
-    if (that._log) {
-      that._log.trace("Loading json from disk: " + filePath);
-    }
-
-    let file = FileUtils.getFile("ProfD", path.split("/"), true);
-    if (!file.exists()) {
-      callback.call(that);
-      return;
-    }
-
-    let channel = NetUtil.newChannel(file);
-    channel.contentType = "application/json";
-
-    NetUtil.asyncFetch(channel, function (is, result) {
-      if (!Components.isSuccessCode(result)) {
-        callback.call(that);
-        return;
-      }
-      let string = NetUtil.readInputStreamToString(is, is.available());
-      is.close();
-      let json;
-      try {
-        json = JSON.parse(string);
-      } catch (ex) {
-        if (that._log) {
-          that._log.debug("Failed to load json: " +
-                          CommonUtils.exceptionStr(ex));
-        }
-      }
-      callback.call(that, json);
-    });
-  },
-
-  /**
-   * Save a json-able object to disk in the profile directory.
-   *
-   * @param filePath
-   *        JSON file path save to <filePath>.json
-   * @param that
-   *        Object to use for logging and "this" for callback
-   * @param obj
-   *        Function to provide json-able object to save. If this isn't a
-   *        function, it'll be used as the object to make a json string.
-   * @param callback
-   *        Function called when the write has been performed. Optional.
-   *        The first argument will be a Components.results error
-   *        constant on error or null if no error was encountered (and
-   *        the file saved successfully).
-   */
-  jsonSave: function jsonSave(filePath, that, obj, callback) {
-    let path = filePath + ".json";
-    if (that._log) {
-      that._log.trace("Saving json to disk: " + path);
-    }
-
-    let file = FileUtils.getFile("ProfD", path.split("/"), true);
-    let json = typeof obj == "function" ? obj.call(that) : obj;
-    let out = JSON.stringify(json);
-
-    let fos = FileUtils.openSafeFileOutputStream(file);
-    let is = this._utf8Converter.convertToInputStream(out);
-    NetUtil.asyncCopy(is, fos, function (result) {
-      if (typeof callback == "function") {
-        let error = (result == Cr.NS_OK) ? null : result;
-        callback.call(that, error);
-      }
-    });
-  },
 
   /**
    * Ensure that the specified value is defined in integer milliseconds since
    * UNIX epoch.
    *
    * This throws an error if the value is not an integer, is negative, or looks
    * like seconds, not milliseconds.
    *
--- a/services/sync/modules/util.js
+++ b/services/sync/modules/util.js
@@ -319,22 +319,99 @@ this.Utils = {
    * Take a base64-encoded 128-bit AES key, returning it as five groups of five
    * uppercase alphanumeric characters, separated by hyphens.
    * A.K.A. base64-to-base32 encoding.
    */
   presentEncodedKeyAsSyncKey : function presentEncodedKeyAsSyncKey(encodedKey) {
     return Utils.encodeKeyBase32(atob(encodedKey));
   },
 
-  jsonLoad: function jsonLoad(path, that, callback) {
-    CommonUtils.jsonLoad("weave/" + path, that, callback);
+  /**
+   * Load a JSON file from disk in the profile directory.
+   *
+   * @param filePath
+   *        JSON file path load from profile. Loaded file will be
+   *        <profile>/<filePath>.json. i.e. Do not specify the ".json"
+   *        extension.
+   * @param that
+   *        Object to use for logging and "this" for callback.
+   * @param callback
+   *        Function to process json object as its first argument. If the file
+   *        could not be loaded, the first argument will be undefined.
+   */
+  jsonLoad: function jsonLoad(filePath, that, callback) {
+    let path = "weave/" + filePath + ".json";
+
+    if (that._log) {
+      that._log.trace("Loading json from disk: " + filePath);
+    }
+
+    let file = FileUtils.getFile("ProfD", path.split("/"), true);
+    if (!file.exists()) {
+      callback.call(that);
+      return;
+    }
+
+    let channel = NetUtil.newChannel(file);
+    channel.contentType = "application/json";
+
+    NetUtil.asyncFetch(channel, function (is, result) {
+      if (!Components.isSuccessCode(result)) {
+        callback.call(that);
+        return;
+      }
+      let string = NetUtil.readInputStreamToString(is, is.available());
+      is.close();
+      let json;
+      try {
+        json = JSON.parse(string);
+      } catch (ex) {
+        if (that._log) {
+          that._log.debug("Failed to load json: " +
+                          CommonUtils.exceptionStr(ex));
+        }
+      }
+      callback.call(that, json);
+    });
   },
 
-  jsonSave: function jsonSave(path, that, obj, callback) {
-    CommonUtils.jsonSave("weave/" + path, that, obj, callback);
+  /**
+   * Save a json-able object to disk in the profile directory.
+   *
+   * @param filePath
+   *        JSON file path save to <filePath>.json
+   * @param that
+   *        Object to use for logging and "this" for callback
+   * @param obj
+   *        Function to provide json-able object to save. If this isn't a
+   *        function, it'll be used as the object to make a json string.
+   * @param callback
+   *        Function called when the write has been performed. Optional.
+   *        The first argument will be a Components.results error
+   *        constant on error or null if no error was encountered (and
+   *        the file saved successfully).
+   */
+  jsonSave: function jsonSave(filePath, that, obj, callback) {
+    let path = "weave/" + filePath + ".json";
+    if (that._log) {
+      that._log.trace("Saving json to disk: " + path);
+    }
+
+    let file = FileUtils.getFile("ProfD", path.split("/"), true);
+    let json = typeof obj == "function" ? obj.call(that) : obj;
+    let out = JSON.stringify(json);
+
+    let fos = FileUtils.openSafeFileOutputStream(file);
+    let is = this._utf8Converter.convertToInputStream(out);
+    NetUtil.asyncCopy(is, fos, function (result) {
+      if (typeof callback == "function") {
+        let error = (result == Cr.NS_OK) ? null : result;
+        callback.call(that, error);
+      }
+    });
   },
 
   getIcon: function(iconUri, defaultIcon) {
     try {
       let iconURI = Utils.makeURI(iconUri);
       return PlacesUtils.favicons.getFaviconLinkForIcon(iconURI).spec;
     }
     catch(ex) {}
copy from services/common/tests/unit/test_utils_json.js
copy to services/sync/tests/unit/test_utils_json.js
--- a/services/common/tests/unit/test_utils_json.js
+++ b/services/sync/tests/unit/test_utils_json.js
@@ -1,89 +1,88 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://services-common/utils.js");
-Cu.import("resource://gre/modules/osfile.jsm")
+Cu.import("resource://services-sync/util.js");
 
 function run_test() {
   initTestLogging();
   run_next_test();
 }
 
 add_test(function test_roundtrip() {
   _("Do a simple write of an array to json and read");
-  CommonUtils.jsonSave("foo", {}, ["v1", "v2"], ensureThrows(function(error) {
+  Utils.jsonSave("foo", {}, ["v1", "v2"], ensureThrows(function(error) {
     do_check_eq(error, null);
 
-    CommonUtils.jsonLoad("foo", {}, ensureThrows(function(val) {
+    Utils.jsonLoad("foo", {}, ensureThrows(function(val) {
       let foo = val;
       do_check_eq(typeof foo, "object");
       do_check_eq(foo.length, 2);
       do_check_eq(foo[0], "v1");
       do_check_eq(foo[1], "v2");
       run_next_test();
     }));
   }));
 });
 
 add_test(function test_string() {
   _("Try saving simple strings");
-  CommonUtils.jsonSave("str", {}, "hi", ensureThrows(function(error) {
+  Utils.jsonSave("str", {}, "hi", ensureThrows(function(error) {
     do_check_eq(error, null);
 
-    CommonUtils.jsonLoad("str", {}, ensureThrows(function(val) {
+    Utils.jsonLoad("str", {}, ensureThrows(function(val) {
       let str = val;
       do_check_eq(typeof str, "string");
       do_check_eq(str.length, 2);
       do_check_eq(str[0], "h");
       do_check_eq(str[1], "i");
       run_next_test();
     }));
   }));
 });
 
 add_test(function test_number() {
   _("Try saving a number");
-  CommonUtils.jsonSave("num", {}, 42, ensureThrows(function(error) {
+  Utils.jsonSave("num", {}, 42, ensureThrows(function(error) {
     do_check_eq(error, null);
 
-    CommonUtils.jsonLoad("num", {}, ensureThrows(function(val) {
+    Utils.jsonLoad("num", {}, ensureThrows(function(val) {
       let num = val;
       do_check_eq(typeof num, "number");
       do_check_eq(num, 42);
       run_next_test();
     }));
   }));
 });
 
 add_test(function test_nonexistent_file() {
   _("Try loading a non-existent file.");
-  CommonUtils.jsonLoad("non-existent", {}, ensureThrows(function(val) {
+  Utils.jsonLoad("non-existent", {}, ensureThrows(function(val) {
     do_check_eq(val, undefined);
     run_next_test();
   }));
 });
 
 add_test(function test_save_logging() {
   _("Verify that writes are logged.");
   let trace;
-  CommonUtils.jsonSave("log", {_log: {trace: function(msg) { trace = msg; }}},
+  Utils.jsonSave("log", {_log: {trace: function(msg) { trace = msg; }}},
                        "hi", ensureThrows(function () {
     do_check_true(!!trace);
     run_next_test();
   }));
 });
 
 add_test(function test_load_logging() {
   _("Verify that reads and read errors are logged.");
 
   // Write a file with some invalid JSON
-  let filePath = "log.json";
+  let filePath = "weave/log.json";
   let file = FileUtils.getFile("ProfD", filePath.split("/"), true);
   let fos = Cc["@mozilla.org/network/file-output-stream;1"]
               .createInstance(Ci.nsIFileOutputStream);
   let flags = FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE
               | FileUtils.MODE_TRUNCATE;
   fos.init(file, flags, FileUtils.PERMS_FILE, fos.DEFER_OPEN);
   let stream = Cc["@mozilla.org/intl/converter-output-stream;1"]
                  .createInstance(Ci.nsIConverterOutputStream);
@@ -97,45 +96,16 @@ add_test(function test_load_logging() {
       trace: function(msg) {
         trace = msg;
       },
       debug: function(msg) {
         debug = msg;
       }
     }
   };
-  CommonUtils.jsonLoad("log", obj, ensureThrows(function(val) {
+  Utils.jsonLoad("log", obj, ensureThrows(function(val) {
     do_check_true(!val);
     do_check_true(!!trace);
     do_check_true(!!debug);
     run_next_test();
   }));
 });
 
-add_test(function test_writeJSON_readJSON() {
-  _("Round-trip some JSON through the promise-based JSON writer.");
-
-  let contents = {
-    "a": 12345.67,
-    "b": {
-      "c": "héllö",
-    },
-    "d": undefined,
-    "e": null,
-  };
-
-  function checkJSON(json) {
-    do_check_eq(contents.a, json.a);
-    do_check_eq(contents.b.c, json.b.c);
-    do_check_eq(contents.d, json.d);
-    do_check_eq(contents.e, json.e);
-    run_next_test();
-  };
-
-  function doRead() {
-    CommonUtils.readJSON(path)
-               .then(checkJSON, do_throw);
-  }
-
-  let path = OS.Path.join(OS.Constants.Path.profileDir, "bar.json");
-  CommonUtils.writeJSON(contents, path)
-             .then(doRead, do_throw);
-});
--- a/services/sync/tests/unit/xpcshell.ini
+++ b/services/sync/tests/unit/xpcshell.ini
@@ -12,16 +12,17 @@ tail =
 # util contains a bunch of functionality used throughout.
 [test_utils_catch.js]
 [test_utils_deepEquals.js]
 [test_utils_deferGetSet.js]
 [test_utils_deriveKey.js]
 [test_utils_keyEncoding.js]
 [test_utils_getErrorString.js]
 [test_utils_getIcon.js]
+[test_utils_json.js]
 [test_utils_lazyStrings.js]
 [test_utils_lock.js]
 [test_utils_makeGUID.js]
 [test_utils_notify.js]
 [test_utils_passphrase.js]
 
 # We have a number of other libraries that are pretty much standalone.
 [test_addon_utils.js]