michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const file = require("sdk/io/file"); michael@0: const prefs = require("sdk/preferences/service"); michael@0: michael@0: const QUOTA_PREF = "extensions.addon-sdk.simple-storage.quota"; michael@0: const WRITE_PERIOD_PREF = "extensions.addon-sdk.simple-storage.writePeriod"; michael@0: michael@0: let {Cc,Ci} = require("chrome"); michael@0: michael@0: const { Loader } = require("sdk/test/loader"); michael@0: const { id } = require("sdk/self"); michael@0: michael@0: let storeFile = Cc["@mozilla.org/file/directory_service;1"]. michael@0: getService(Ci.nsIProperties). michael@0: get("ProfD", Ci.nsIFile); michael@0: storeFile.append("jetpack"); michael@0: storeFile.append(id); michael@0: storeFile.append("simple-storage"); michael@0: file.mkpath(storeFile.path); michael@0: storeFile.append("store.json"); michael@0: let storeFilename = storeFile.path; michael@0: michael@0: function manager(loader) loader.sandbox("sdk/simple-storage").manager; michael@0: michael@0: exports.testSetGet = function (assert, done) { michael@0: // Load the module once, set a value. michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.ok(file.exists(storeFilename), "Store file should exist"); michael@0: michael@0: // Load the module again and make sure the value stuck. michael@0: loader = Loader(module); michael@0: ss = loader.require("sdk/simple-storage"); michael@0: assert.equal(ss.storage.foo, val, "Value should persist"); michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.fail("Nothing should be written since `storage` was not changed."); michael@0: }; michael@0: loader.unload(); michael@0: file.remove(storeFilename); michael@0: done(); michael@0: }; michael@0: let val = "foo"; michael@0: ss.storage.foo = val; michael@0: assert.equal(ss.storage.foo, val, "Value read should be value set"); michael@0: loader.unload(); michael@0: }; michael@0: michael@0: exports.testSetGetRootArray = function (assert, done) { michael@0: setGetRoot(assert, done, [1, 2, 3], function (arr1, arr2) { michael@0: if (arr1.length !== arr2.length) michael@0: return false; michael@0: for (let i = 0; i < arr1.length; i++) { michael@0: if (arr1[i] !== arr2[i]) michael@0: return false; michael@0: } michael@0: return true; michael@0: }); michael@0: }; michael@0: michael@0: exports.testSetGetRootBool = function (assert, done) { michael@0: setGetRoot(assert, done, true); michael@0: }; michael@0: michael@0: exports.testSetGetRootFunction = function (assert, done) { michael@0: setGetRootError(assert, done, function () {}, michael@0: "Setting storage to a function should fail"); michael@0: }; michael@0: michael@0: exports.testSetGetRootNull = function (assert, done) { michael@0: setGetRoot(assert, done, null); michael@0: }; michael@0: michael@0: exports.testSetGetRootNumber = function (assert, done) { michael@0: setGetRoot(assert, done, 3.14); michael@0: }; michael@0: michael@0: exports.testSetGetRootObject = function (assert, done) { michael@0: setGetRoot(assert, done, { foo: 1, bar: 2 }, function (obj1, obj2) { michael@0: for (let prop in obj1) { michael@0: if (!(prop in obj2) || obj2[prop] !== obj1[prop]) michael@0: return false; michael@0: } michael@0: for (let prop in obj2) { michael@0: if (!(prop in obj1) || obj1[prop] !== obj2[prop]) michael@0: return false; michael@0: } michael@0: return true; michael@0: }); michael@0: }; michael@0: michael@0: exports.testSetGetRootString = function (assert, done) { michael@0: setGetRoot(assert, done, "sho' 'nuff"); michael@0: }; michael@0: michael@0: exports.testSetGetRootUndefined = function (assert, done) { michael@0: setGetRootError(assert, done, undefined, "Setting storage to undefined should fail"); michael@0: }; michael@0: michael@0: exports.testEmpty = function (assert) { michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: loader.unload(); michael@0: assert.ok(!file.exists(storeFilename), "Store file should not exist"); michael@0: }; michael@0: michael@0: exports.testStorageDataRecovery = function(assert) { michael@0: const data = { michael@0: a: true, michael@0: b: [3, 13], michael@0: c: "guilty!", michael@0: d: { e: 1, f: 2 } michael@0: }; michael@0: let stream = file.open(storeFilename, "w"); michael@0: stream.write(JSON.stringify(data)); michael@0: stream.close(); michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: assert.deepEqual(ss.storage, data, "Recovered data should be the same as written"); michael@0: file.remove(storeFilename); michael@0: loader.unload(); michael@0: } michael@0: michael@0: exports.testMalformed = function (assert) { michael@0: let stream = file.open(storeFilename, "w"); michael@0: stream.write("i'm not json"); michael@0: stream.close(); michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: let empty = true; michael@0: for (let key in ss.storage) { michael@0: empty = false; michael@0: break; michael@0: } michael@0: assert.ok(empty, "Malformed storage should cause root to be empty"); michael@0: file.remove(storeFilename); michael@0: loader.unload(); michael@0: }; michael@0: michael@0: // Go over quota and handle it by listener. michael@0: exports.testQuotaExceededHandle = function (assert, done) { michael@0: prefs.set(QUOTA_PREF, 18); michael@0: michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: ss.on("OverQuota", function () { michael@0: assert.pass("OverQuota was emitted as expected"); michael@0: assert.equal(this, ss, "`this` should be simple storage"); michael@0: ss.storage = { x: 4, y: 5 }; michael@0: michael@0: manager(loader).jsonStore.onWrite = function () { michael@0: loader = Loader(module); michael@0: ss = loader.require("sdk/simple-storage"); michael@0: let numProps = 0; michael@0: for (let prop in ss.storage) michael@0: numProps++; michael@0: assert.ok(numProps, 2, michael@0: "Store should contain 2 values: " + ss.storage.toSource()); michael@0: assert.equal(ss.storage.x, 4, "x value should be correct"); michael@0: assert.equal(ss.storage.y, 5, "y value should be correct"); michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.fail("Nothing should be written since `storage` was not changed."); michael@0: }; michael@0: loader.unload(); michael@0: prefs.reset(QUOTA_PREF); michael@0: done(); michael@0: }; michael@0: loader.unload(); michael@0: }); michael@0: // This will be JSON.stringify()ed to: {"a":1,"b":2,"c":3} (19 bytes) michael@0: ss.storage = { a: 1, b: 2, c: 3 }; michael@0: manager(loader).jsonStore.write(); michael@0: }; michael@0: michael@0: // Go over quota but don't handle it. The last good state should still persist. michael@0: exports.testQuotaExceededNoHandle = function (assert, done) { michael@0: prefs.set(QUOTA_PREF, 5); michael@0: michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: loader = Loader(module); michael@0: ss = loader.require("sdk/simple-storage"); michael@0: assert.equal(ss.storage, val, michael@0: "Value should have persisted: " + ss.storage); michael@0: ss.storage = "some very long string that is very long"; michael@0: ss.on("OverQuota", function () { michael@0: assert.pass("OverQuota emitted as expected"); michael@0: manager(loader).jsonStore.onWrite = function () { michael@0: assert.fail("Over-quota value should not have been written"); michael@0: }; michael@0: loader.unload(); michael@0: michael@0: loader = Loader(module); michael@0: ss = loader.require("sdk/simple-storage"); michael@0: assert.equal(ss.storage, val, michael@0: "Over-quota value should not have been written, " + michael@0: "old value should have persisted: " + ss.storage); michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.fail("Nothing should be written since `storage` was not changed."); michael@0: }; michael@0: loader.unload(); michael@0: prefs.reset(QUOTA_PREF); michael@0: done(); michael@0: }); michael@0: manager(loader).jsonStore.write(); michael@0: }; michael@0: michael@0: let val = "foo"; michael@0: ss.storage = val; michael@0: loader.unload(); michael@0: }; michael@0: michael@0: exports.testQuotaUsage = function (assert, done) { michael@0: let quota = 21; michael@0: prefs.set(QUOTA_PREF, quota); michael@0: michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: michael@0: // {"a":1} (7 bytes) michael@0: ss.storage = { a: 1 }; michael@0: assert.equal(ss.quotaUsage, 7 / quota, "quotaUsage should be correct"); michael@0: michael@0: // {"a":1,"bb":2} (14 bytes) michael@0: ss.storage = { a: 1, bb: 2 }; michael@0: assert.equal(ss.quotaUsage, 14 / quota, "quotaUsage should be correct"); michael@0: michael@0: // {"a":1,"bb":2,"cc":3} (21 bytes) michael@0: ss.storage = { a: 1, bb: 2, cc: 3 }; michael@0: assert.equal(ss.quotaUsage, 21 / quota, "quotaUsage should be correct"); michael@0: michael@0: manager(loader).jsonStore.onWrite = function () { michael@0: prefs.reset(QUOTA_PREF); michael@0: done(); michael@0: }; michael@0: loader.unload(); michael@0: }; michael@0: michael@0: exports.testUninstall = function (assert, done) { michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: manager(loader).jsonStore.onWrite = function () { michael@0: assert.ok(file.exists(storeFilename), "Store file should exist"); michael@0: michael@0: loader = Loader(module); michael@0: ss = loader.require("sdk/simple-storage"); michael@0: loader.unload("uninstall"); michael@0: assert.ok(!file.exists(storeFilename), "Store file should be removed"); michael@0: done(); michael@0: }; michael@0: ss.storage.foo = "foo"; michael@0: loader.unload(); michael@0: }; michael@0: michael@0: exports.testChangeInnerArray = function(assert, done) { michael@0: prefs.set(WRITE_PERIOD_PREF, 10); michael@0: michael@0: let expected = { michael@0: x: [5, 7], michael@0: y: [7, 28], michael@0: z: [6, 2] michael@0: }; michael@0: michael@0: // Load the module, set a value. michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.ok(file.exists(storeFilename), "Store file should exist"); michael@0: michael@0: // Load the module again and check the result michael@0: loader = Loader(module); michael@0: ss = loader.require("sdk/simple-storage"); michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: michael@0: // Add a property michael@0: ss.storage.x.push(["bar"]); michael@0: expected.x.push(["bar"]); michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: michael@0: // Modify a property michael@0: ss.storage.y[0] = 42; michael@0: expected.y[0] = 42; michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: michael@0: // Delete a property michael@0: delete ss.storage.z[1]; michael@0: delete expected.z[1]; michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: michael@0: // Modify the new inner-object michael@0: ss.storage.x[2][0] = "baz"; michael@0: expected.x[2][0] = "baz"; michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.fail("Nothing should be written since `storage` was not changed."); michael@0: }; michael@0: loader.unload(); michael@0: michael@0: // Load the module again and check the result michael@0: loader = Loader(module); michael@0: ss = loader.require("sdk/simple-storage"); michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: loader.unload(); michael@0: file.remove(storeFilename); michael@0: prefs.reset(WRITE_PERIOD_PREF); michael@0: done(); michael@0: }; michael@0: }; michael@0: }; michael@0: }; michael@0: }; michael@0: michael@0: ss.storage = { michael@0: x: [5, 7], michael@0: y: [7, 28], michael@0: z: [6, 2] michael@0: }; michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: michael@0: loader.unload(); michael@0: }; michael@0: michael@0: exports.testChangeInnerObject = function(assert, done) { michael@0: prefs.set(WRITE_PERIOD_PREF, 10); michael@0: michael@0: let expected = { michael@0: x: { michael@0: a: 5, michael@0: b: 7 michael@0: }, michael@0: y: { michael@0: c: 7, michael@0: d: 28 michael@0: }, michael@0: z: { michael@0: e: 6, michael@0: f: 2 michael@0: } michael@0: }; michael@0: michael@0: // Load the module, set a value. michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.ok(file.exists(storeFilename), "Store file should exist"); michael@0: michael@0: // Load the module again and check the result michael@0: loader = Loader(module); michael@0: ss = loader.require("sdk/simple-storage"); michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: michael@0: // Add a property michael@0: ss.storage.x.g = {foo: "bar"}; michael@0: expected.x.g = {foo: "bar"}; michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: michael@0: // Modify a property michael@0: ss.storage.y.c = 42; michael@0: expected.y.c = 42; michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: michael@0: // Delete a property michael@0: delete ss.storage.z.f; michael@0: delete expected.z.f; michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: michael@0: // Modify the new inner-object michael@0: ss.storage.x.g.foo = "baz"; michael@0: expected.x.g.foo = "baz"; michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.fail("Nothing should be written since `storage` was not changed."); michael@0: }; michael@0: loader.unload(); michael@0: michael@0: // Load the module again and check the result michael@0: loader = Loader(module); michael@0: ss = loader.require("sdk/simple-storage"); michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: loader.unload(); michael@0: file.remove(storeFilename); michael@0: prefs.reset(WRITE_PERIOD_PREF); michael@0: done(); michael@0: }; michael@0: }; michael@0: }; michael@0: }; michael@0: }; michael@0: michael@0: ss.storage = { michael@0: x: { michael@0: a: 5, michael@0: b: 7 michael@0: }, michael@0: y: { michael@0: c: 7, michael@0: d: 28 michael@0: }, michael@0: z: { michael@0: e: 6, michael@0: f: 2 michael@0: } michael@0: }; michael@0: assert.equal(JSON.stringify(ss.storage), michael@0: JSON.stringify(expected), "Should see the expected object"); michael@0: michael@0: loader.unload(); michael@0: }; michael@0: michael@0: exports.testSetNoSetRead = function (assert, done) { michael@0: // Load the module, set a value. michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.ok(file.exists(storeFilename), "Store file should exist"); michael@0: michael@0: // Load the module again but don't access ss.storage. michael@0: loader = Loader(module); michael@0: ss = loader.require("sdk/simple-storage"); michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.fail("Nothing should be written since `storage` was not accessed."); michael@0: }; michael@0: loader.unload(); michael@0: michael@0: // Load the module a third time and make sure the value stuck. michael@0: loader = Loader(module); michael@0: ss = loader.require("sdk/simple-storage"); michael@0: assert.equal(ss.storage.foo, val, "Value should persist"); michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.fail("Nothing should be written since `storage` was not changed."); michael@0: }; michael@0: loader.unload(); michael@0: file.remove(storeFilename); michael@0: done(); michael@0: }; michael@0: let val = "foo"; michael@0: ss.storage.foo = val; michael@0: assert.equal(ss.storage.foo, val, "Value read should be value set"); michael@0: loader.unload(); michael@0: }; michael@0: michael@0: michael@0: function setGetRoot(assert, done, val, compare) { michael@0: compare = compare || function (a, b) a === b; michael@0: michael@0: // Load the module once, set a value. michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: manager(loader).jsonStore.onWrite = function () { michael@0: assert.ok(file.exists(storeFilename), "Store file should exist"); michael@0: michael@0: // Load the module again and make sure the value stuck. michael@0: loader = Loader(module); michael@0: ss = loader.require("sdk/simple-storage"); michael@0: assert.ok(compare(ss.storage, val), "Value should persist"); michael@0: manager(loader).jsonStore.onWrite = function (storage) { michael@0: assert.fail("Nothing should be written since `storage` was not changed."); michael@0: }; michael@0: loader.unload(); michael@0: file.remove(storeFilename); michael@0: done(); michael@0: }; michael@0: ss.storage = val; michael@0: assert.ok(compare(ss.storage, val), "Value read should be value set"); michael@0: loader.unload(); michael@0: } michael@0: michael@0: function setGetRootError(assert, done, val, msg) { michael@0: let pred = new RegExp("storage must be one of the following types: " + michael@0: "array, boolean, null, number, object, string"); michael@0: let loader = Loader(module); michael@0: let ss = loader.require("sdk/simple-storage"); michael@0: assert.throws(function () ss.storage = val, pred, msg); michael@0: done(); michael@0: loader.unload(); michael@0: } michael@0: michael@0: require('sdk/test').run(exports);