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 120614 58b601eae9a7d4cac946c3d12eb310c00fe95929
parent 120613 8d651840659725707b6a140bef13cac7d1c494d0
child 120798 a9e70ab45ca22123b157e59777a23d5aadd96760
push id805
push userrnewman@mozilla.com
push dateSun, 03 Feb 2013 08:52:00 +0000
treeherderservices-central@58b601eae9a7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs836206
milestone21.0a1
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]