diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js | 1085 |
1 files changed, 1085 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js b/toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js new file mode 100644 index 0000000000..720c7f539a --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js @@ -0,0 +1,1085 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + ExtensionSettingsStore: + "resource://gre/modules/ExtensionSettingsStore.sys.mjs", +}); + +const { createAppInfo, promiseShutdownManager, promiseStartupManager } = + AddonTestUtils; + +AddonTestUtils.init(this); + +// Allow for unsigned addons. +AddonTestUtils.overrideCertDB(); + +createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42"); + +const ITEMS = { + key1: [ + { key: "key1", value: "val1", id: "@first" }, + { key: "key1", value: "val2", id: "@second" }, + { key: "key1", value: "val3", id: "@third" }, + ], + key2: [ + { key: "key2", value: "val1-2", id: "@first" }, + { key: "key2", value: "val2-2", id: "@second" }, + { key: "key2", value: "val3-2", id: "@third" }, + ], +}; +const KEY_LIST = Object.keys(ITEMS); +const TEST_TYPE = "myType"; + +let callbackCount = 0; + +function initialValue(key) { + callbackCount++; + return `key:${key}`; +} + +add_task(async function test_settings_store() { + await promiseStartupManager(); + + // Create an array of test framework extension wrappers to install. + let testExtensions = [ + ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { gecko: { id: "@first" } }, + }, + }), + ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { gecko: { id: "@second" } }, + }, + }), + ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { gecko: { id: "@third" } }, + }, + }), + ]; + + for (let extension of testExtensions) { + await extension.startup(); + } + + // Create an array actual Extension objects which correspond to the + // test framework extension wrappers. + let extensions = testExtensions.map(extension => extension.extension); + + let expectedCallbackCount = 0; + + await Assert.rejects( + ExtensionSettingsStore.getLevelOfControl(1, TEST_TYPE, "key"), + /The ExtensionSettingsStore was accessed before the initialize promise resolved/, + "Accessing the SettingsStore before it is initialized throws an error." + ); + + // Initialize the SettingsStore. + await ExtensionSettingsStore.initialize(); + + // Add a setting for the second oldest extension, where it is the only setting for a key. + for (let key of KEY_LIST) { + let extensionIndex = 1; + let itemToAdd = ITEMS[key][extensionIndex]; + let levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[extensionIndex].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controllable_by_this_extension", + "getLevelOfControl returns correct levelOfControl with no settings set for a key." + ); + let item = await ExtensionSettingsStore.addSetting( + extensions[extensionIndex].id, + TEST_TYPE, + itemToAdd.key, + itemToAdd.value, + initialValue + ); + expectedCallbackCount++; + equal( + callbackCount, + expectedCallbackCount, + "initialValueCallback called the expected number of times." + ); + deepEqual( + item, + itemToAdd, + "Adding initial item for a key returns that item." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual( + item, + itemToAdd, + "getSetting returns correct item with only one item in the list." + ); + levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[extensionIndex].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controlled_by_this_extension", + "getLevelOfControl returns correct levelOfControl with only one item in the list." + ); + ok( + ExtensionSettingsStore.hasSetting( + extensions[extensionIndex].id, + TEST_TYPE, + key + ), + "hasSetting returns the correct value when an extension has a setting set." + ); + item = await ExtensionSettingsStore.getSetting( + TEST_TYPE, + key, + extensions[extensionIndex].id + ); + deepEqual( + item, + itemToAdd, + "getSetting with id returns correct item with only one item in the list." + ); + } + + // Add a setting for the oldest extension. + for (let key of KEY_LIST) { + let extensionIndex = 0; + let itemToAdd = ITEMS[key][extensionIndex]; + let item = await ExtensionSettingsStore.addSetting( + extensions[extensionIndex].id, + TEST_TYPE, + itemToAdd.key, + itemToAdd.value, + initialValue + ); + equal( + callbackCount, + expectedCallbackCount, + "initialValueCallback called the expected number of times." + ); + equal( + item, + null, + "An older extension adding a setting for a key returns null" + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual( + item, + ITEMS[key][1], + "getSetting returns correct item with more than one item in the list." + ); + let levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[extensionIndex].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controlled_by_other_extensions", + "getLevelOfControl returns correct levelOfControl when another extension is in control." + ); + item = await ExtensionSettingsStore.getSetting( + TEST_TYPE, + key, + extensions[extensionIndex].id + ); + deepEqual( + item, + itemToAdd, + "getSetting with id returns correct item with more than one item in the list." + ); + } + + // Reload the settings store to emulate a browser restart. + await ExtensionSettingsStore._reloadFile(); + + // Add a setting for the newest extension. + for (let key of KEY_LIST) { + let extensionIndex = 2; + let itemToAdd = ITEMS[key][extensionIndex]; + let levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[extensionIndex].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controllable_by_this_extension", + "getLevelOfControl returns correct levelOfControl for a more recent extension." + ); + let item = await ExtensionSettingsStore.addSetting( + extensions[extensionIndex].id, + TEST_TYPE, + itemToAdd.key, + itemToAdd.value, + initialValue + ); + equal( + callbackCount, + expectedCallbackCount, + "initialValueCallback called the expected number of times." + ); + deepEqual( + item, + itemToAdd, + "Adding item for most recent extension returns that item." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual( + item, + itemToAdd, + "getSetting returns correct item with more than one item in the list." + ); + levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[extensionIndex].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controlled_by_this_extension", + "getLevelOfControl returns correct levelOfControl when this extension is in control." + ); + item = await ExtensionSettingsStore.getSetting( + TEST_TYPE, + key, + extensions[extensionIndex].id + ); + deepEqual( + item, + itemToAdd, + "getSetting with id returns correct item with more than one item in the list." + ); + } + + for (let extension of extensions) { + let items = await ExtensionSettingsStore.getAllForExtension( + extension.id, + TEST_TYPE + ); + deepEqual(items, KEY_LIST, "getAllForExtension returns expected keys."); + } + + // Attempting to remove a setting that has not been set should *not* throw an exception. + let removeResult = await ExtensionSettingsStore.removeSetting( + extensions[0].id, + "myType", + "unset_key" + ); + equal( + removeResult, + null, + "Removing a setting that was not previously set returns null." + ); + + // Attempting to disable a setting that has not been set should throw an exception. + Assert.throws( + () => + ExtensionSettingsStore.disable(extensions[0].id, "myType", "unset_key"), + /Cannot alter the setting for myType:unset_key as it does not exist/, + "disable rejects with an unset key." + ); + + // Attempting to enable a setting that has not been set should throw an exception. + Assert.throws( + () => + ExtensionSettingsStore.enable(extensions[0].id, "myType", "unset_key"), + /Cannot alter the setting for myType:unset_key as it does not exist/, + "enable rejects with an unset key." + ); + + let expectedKeys = KEY_LIST; + // Disable the non-top item for a key. + for (let key of KEY_LIST) { + let extensionIndex = 0; + let item = await ExtensionSettingsStore.addSetting( + extensions[extensionIndex].id, + TEST_TYPE, + key, + "new value", + initialValue + ); + equal( + callbackCount, + expectedCallbackCount, + "initialValueCallback called the expected number of times." + ); + equal(item, null, "Updating non-top item for a key returns null"); + item = await ExtensionSettingsStore.disable( + extensions[extensionIndex].id, + TEST_TYPE, + key + ); + equal(item, null, "Disabling non-top item for a key returns null."); + let allForExtension = await ExtensionSettingsStore.getAllForExtension( + extensions[extensionIndex].id, + TEST_TYPE + ); + deepEqual( + allForExtension, + expectedKeys, + "getAllForExtension returns expected keys after a disable." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual( + item, + ITEMS[key][2], + "getSetting returns correct item after a disable." + ); + let levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[extensionIndex].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controlled_by_other_extensions", + "getLevelOfControl returns correct levelOfControl after disabling of non-top item." + ); + } + + // Re-enable the non-top item for a key. + for (let key of KEY_LIST) { + let extensionIndex = 0; + let item = await ExtensionSettingsStore.enable( + extensions[extensionIndex].id, + TEST_TYPE, + key + ); + equal(item, null, "Enabling non-top item for a key returns null."); + let allForExtension = await ExtensionSettingsStore.getAllForExtension( + extensions[extensionIndex].id, + TEST_TYPE + ); + deepEqual( + allForExtension, + expectedKeys, + "getAllForExtension returns expected keys after an enable." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual( + item, + ITEMS[key][2], + "getSetting returns correct item after an enable." + ); + let levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[extensionIndex].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controlled_by_other_extensions", + "getLevelOfControl returns correct levelOfControl after enabling of non-top item." + ); + } + + // Remove the non-top item for a key. + for (let key of KEY_LIST) { + let extensionIndex = 0; + let item = await ExtensionSettingsStore.removeSetting( + extensions[extensionIndex].id, + TEST_TYPE, + key + ); + equal(item, null, "Removing non-top item for a key returns null."); + expectedKeys = expectedKeys.filter(expectedKey => expectedKey != key); + let allForExtension = await ExtensionSettingsStore.getAllForExtension( + extensions[extensionIndex].id, + TEST_TYPE + ); + deepEqual( + allForExtension, + expectedKeys, + "getAllForExtension returns expected keys after a removal." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual( + item, + ITEMS[key][2], + "getSetting returns correct item after a removal." + ); + let levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[extensionIndex].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controlled_by_other_extensions", + "getLevelOfControl returns correct levelOfControl after removal of non-top item." + ); + ok( + !ExtensionSettingsStore.hasSetting( + extensions[extensionIndex].id, + TEST_TYPE, + key + ), + "hasSetting returns the correct value when an extension does not have a setting set." + ); + } + + for (let key of KEY_LIST) { + // Disable the top item for a key. + let item = await ExtensionSettingsStore.disable( + extensions[2].id, + TEST_TYPE, + key + ); + deepEqual( + item, + ITEMS[key][1], + "Disabling top item for a key returns the new top item." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual( + item, + ITEMS[key][1], + "getSetting returns correct item after a disable." + ); + let levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[2].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controllable_by_this_extension", + "getLevelOfControl returns correct levelOfControl after disabling of top item." + ); + + // Re-enable the top item for a key. + item = await ExtensionSettingsStore.enable( + extensions[2].id, + TEST_TYPE, + key + ); + deepEqual( + item, + ITEMS[key][2], + "Re-enabling top item for a key returns the old top item." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual( + item, + ITEMS[key][2], + "getSetting returns correct item after an enable." + ); + levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[2].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controlled_by_this_extension", + "getLevelOfControl returns correct levelOfControl after re-enabling top item." + ); + + // Remove the top item for a key. + item = await ExtensionSettingsStore.removeSetting( + extensions[2].id, + TEST_TYPE, + key + ); + deepEqual( + item, + ITEMS[key][1], + "Removing top item for a key returns the new top item." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual( + item, + ITEMS[key][1], + "getSetting returns correct item after a removal." + ); + levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[2].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controllable_by_this_extension", + "getLevelOfControl returns correct levelOfControl after removal of top item." + ); + + // Add a setting for the current top item. + let itemToAdd = { key, value: `new-${key}`, id: "@second" }; + item = await ExtensionSettingsStore.addSetting( + extensions[1].id, + TEST_TYPE, + itemToAdd.key, + itemToAdd.value, + initialValue + ); + equal( + callbackCount, + expectedCallbackCount, + "initialValueCallback called the expected number of times." + ); + deepEqual( + item, + itemToAdd, + "Updating top item for a key returns that item." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual( + item, + itemToAdd, + "getSetting returns correct item after updating." + ); + levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[1].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controlled_by_this_extension", + "getLevelOfControl returns correct levelOfControl after updating." + ); + + // Disable the last remaining item for a key. + let expectedItem = { key, initialValue: initialValue(key) }; + // We're using the callback to set the expected value, so we need to increment the + // expectedCallbackCount. + expectedCallbackCount++; + item = await ExtensionSettingsStore.disable( + extensions[1].id, + TEST_TYPE, + key + ); + deepEqual( + item, + expectedItem, + "Disabling last item for a key returns the initial value." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual( + item, + expectedItem, + "getSetting returns the initial value after all are disabled." + ); + levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[1].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controllable_by_this_extension", + "getLevelOfControl returns correct levelOfControl after all are disabled." + ); + + // Re-enable the last remaining item for a key. + item = await ExtensionSettingsStore.enable( + extensions[1].id, + TEST_TYPE, + key + ); + deepEqual( + item, + itemToAdd, + "Re-enabling last item for a key returns the old value." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual( + item, + itemToAdd, + "getSetting returns expected value after re-enabling." + ); + levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[1].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controlled_by_this_extension", + "getLevelOfControl returns correct levelOfControl after re-enabling." + ); + + // Remove the last remaining item for a key. + item = await ExtensionSettingsStore.removeSetting( + extensions[1].id, + TEST_TYPE, + key + ); + deepEqual( + item, + expectedItem, + "Removing last item for a key returns the initial value." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key); + deepEqual(item, null, "getSetting returns null after all are removed."); + levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[1].id, + TEST_TYPE, + key + ); + equal( + levelOfControl, + "controllable_by_this_extension", + "getLevelOfControl returns correct levelOfControl after all are removed." + ); + + // Attempting to remove a setting that has had all extensions removed should *not* throw an exception. + removeResult = await ExtensionSettingsStore.removeSetting( + extensions[1].id, + TEST_TYPE, + key + ); + equal( + removeResult, + null, + "Removing a setting that has had all extensions removed returns null." + ); + } + + // Test adding a setting with a value in callbackArgument. + let extensionIndex = 0; + let testKey = "callbackArgumentKey"; + let callbackArgumentValue = Date.now(); + // Add the setting. + let item = await ExtensionSettingsStore.addSetting( + extensions[extensionIndex].id, + TEST_TYPE, + testKey, + 1, + initialValue, + callbackArgumentValue + ); + expectedCallbackCount++; + equal( + callbackCount, + expectedCallbackCount, + "initialValueCallback called the expected number of times." + ); + // Remove the setting which should return the initial value. + let expectedItem = { + key: testKey, + initialValue: initialValue(callbackArgumentValue), + }; + // We're using the callback to set the expected value, so we need to increment the + // expectedCallbackCount. + expectedCallbackCount++; + item = await ExtensionSettingsStore.removeSetting( + extensions[extensionIndex].id, + TEST_TYPE, + testKey + ); + deepEqual( + item, + expectedItem, + "Removing last item for a key returns the initial value." + ); + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, testKey); + deepEqual(item, null, "getSetting returns null after all are removed."); + + item = await ExtensionSettingsStore.getSetting(TEST_TYPE, "not a key"); + equal( + item, + null, + "getSetting returns a null item if the setting does not have any records." + ); + let levelOfControl = await ExtensionSettingsStore.getLevelOfControl( + extensions[1].id, + TEST_TYPE, + "not a key" + ); + equal( + levelOfControl, + "controllable_by_this_extension", + "getLevelOfControl returns correct levelOfControl if the setting does not have any records." + ); + + for (let extension of testExtensions) { + await extension.unload(); + } + + await promiseShutdownManager(); +}); + +add_task(async function test_settings_store_setByUser() { + await promiseStartupManager(); + + // Create an array of test framework extension wrappers to install. + let testExtensions = [ + ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { gecko: { id: "@first" } }, + }, + }), + ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { gecko: { id: "@second" } }, + }, + }), + ]; + + let type = "some_type"; + let key = "some_key"; + + for (let extension of testExtensions) { + await extension.startup(); + } + + // Create an array actual Extension objects which correspond to the + // test framework extension wrappers. + let [one, two] = testExtensions.map(extension => extension.extension); + let initialCallback = () => "initial"; + + // Initialize the SettingsStore. + await ExtensionSettingsStore.initialize(); + + equal( + null, + ExtensionSettingsStore.getSetting(type, key), + "getSetting is initially null" + ); + + let item = await ExtensionSettingsStore.addSetting( + one.id, + type, + key, + "one", + initialCallback + ); + deepEqual( + { key, value: "one", id: one.id }, + item, + "addSetting returns the first set item" + ); + + item = await ExtensionSettingsStore.addSetting( + two.id, + type, + key, + "two", + initialCallback + ); + deepEqual( + { key, value: "two", id: two.id }, + item, + "addSetting returns the second set item" + ); + + // a user-set selection reverts to precedence order when new + // extension sets the setting. + ExtensionSettingsStore.select( + ExtensionSettingsStore.SETTING_USER_SET, + type, + key + ); + deepEqual( + { key, initialValue: "initial" }, + ExtensionSettingsStore.getSetting(type, key), + "getSetting returns the initial value after being set by user" + ); + + let three = ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { gecko: { id: "@third" } }, + }, + }); + await three.startup(); + + item = await ExtensionSettingsStore.addSetting( + three.id, + type, + key, + "three", + initialCallback + ); + deepEqual( + { key, value: "three", id: three.id }, + item, + "addSetting returns the third set item" + ); + deepEqual( + item, + ExtensionSettingsStore.getSetting(type, key), + "getSetting returns the third set item" + ); + + ExtensionSettingsStore.select( + ExtensionSettingsStore.SETTING_USER_SET, + type, + key + ); + deepEqual( + { key, initialValue: "initial" }, + ExtensionSettingsStore.getSetting(type, key), + "getSetting returns the initial value after being set by user" + ); + + item = ExtensionSettingsStore.select(one.id, type, key); + deepEqual( + { key, value: "one", id: one.id }, + item, + "selecting an extension returns the first set item after enable" + ); + + // Disabling a selected item returns to precedence order + ExtensionSettingsStore.disable(one.id, type, key); + deepEqual( + { key, value: "three", id: three.id }, + ExtensionSettingsStore.getSetting(type, key), + "returning to precedence order sets the third set item" + ); + + // Test that disabling all then enabling one does not take over a user-set setting. + ExtensionSettingsStore.select( + ExtensionSettingsStore.SETTING_USER_SET, + type, + key + ); + deepEqual( + { key, initialValue: "initial" }, + ExtensionSettingsStore.getSetting(type, key), + "getSetting returns the initial value after being set by user" + ); + + ExtensionSettingsStore.disable(three.id, type, key); + ExtensionSettingsStore.disable(two.id, type, key); + deepEqual( + { key, initialValue: "initial" }, + ExtensionSettingsStore.getSetting(type, key), + "getSetting returns the initial value after disabling all extensions" + ); + + ExtensionSettingsStore.enable(three.id, type, key); + deepEqual( + { key, initialValue: "initial" }, + ExtensionSettingsStore.getSetting(type, key), + "getSetting returns the initial value after enabling one extension" + ); + + // Ensure that calling addSetting again will not reset a user-set value when + // the extension install date is older than the user-set date. + item = await ExtensionSettingsStore.addSetting( + three.id, + type, + key, + "three", + initialCallback + ); + deepEqual( + { key, initialValue: "initial" }, + ExtensionSettingsStore.getSetting(type, key), + "getSetting returns the initial value after calling addSetting for old addon" + ); + + item = ExtensionSettingsStore.enable(three.id, type, key); + equal(undefined, item, "enabling the active item does not return an item"); + deepEqual( + { key, initialValue: "initial" }, + ExtensionSettingsStore.getSetting(type, key), + "getSetting returns the initial value after enabling one extension" + ); + + ExtensionSettingsStore.removeSetting(three.id, type, key); + ExtensionSettingsStore.removeSetting(two.id, type, key); + ExtensionSettingsStore.removeSetting(one.id, type, key); + + equal( + null, + ExtensionSettingsStore.getSetting(type, key), + "getSetting returns null after removing all settings" + ); + + for (let extension of testExtensions) { + await extension.unload(); + } + + await promiseShutdownManager(); +}); + +add_task(async function test_settings_store_add_disabled() { + await promiseStartupManager(); + + let id = "@add-on-disable"; + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { gecko: { id } }, + }, + }); + + await extension.startup(); + await ExtensionSettingsStore.initialize(); + + await ExtensionSettingsStore.addSetting( + id, + "foo", + "bar", + "set", + () => "not set" + ); + + let item = ExtensionSettingsStore.getSetting("foo", "bar"); + equal(item.id, id, "The add-on is in control"); + equal(item.value, "set", "The value is set"); + + ExtensionSettingsStore.disable(id, "foo", "bar"); + item = ExtensionSettingsStore.getSetting("foo", "bar"); + equal(item.id, undefined, "The add-on is not in control"); + equal(item.initialValue, "not set", "The value is not set"); + + await ExtensionSettingsStore.addSetting( + id, + "foo", + "bar", + "set", + () => "not set" + ); + item = ExtensionSettingsStore.getSetting("foo", "bar"); + equal(item.id, id, "The add-on is in control"); + equal(item.value, "set", "The value is set"); + + await extension.unload(); + + await promiseShutdownManager(); +}); + +add_task(async function test_settings_uninstall_remove() { + await promiseStartupManager(); + + let id = "@add-on-uninstall"; + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { gecko: { id } }, + }, + }); + + await extension.startup(); + await ExtensionSettingsStore.initialize(); + + await ExtensionSettingsStore.addSetting( + id, + "foo", + "bar", + "set", + () => "not set" + ); + + let item = ExtensionSettingsStore.getSetting("foo", "bar"); + equal(item.id, id, "The add-on is in control"); + equal(item.value, "set", "The value is set"); + + await extension.unload(); + + await promiseShutdownManager(); + + item = ExtensionSettingsStore.getSetting("foo", "bar"); + equal(item, null, "The add-on setting was removed"); +}); + +add_task(async function test_exceptions() { + await ExtensionSettingsStore.initialize(); + + await Assert.rejects( + ExtensionSettingsStore.addSetting( + 1, + TEST_TYPE, + "key_not_a_function", + "val1", + "not a function" + ), + /initialValueCallback must be a function/, + "addSetting rejects with a callback that is not a function." + ); +}); + +add_task(async function test_get_all_settings() { + await promiseStartupManager(); + + let testExtensions = [ + ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { gecko: { id: "@first" } }, + }, + }), + ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { gecko: { id: "@second" } }, + }, + }), + ]; + + for (let extension of testExtensions) { + await extension.startup(); + } + + await ExtensionSettingsStore.initialize(); + + let items = ExtensionSettingsStore.getAllSettings("foo", "bar"); + equal(items.length, 0, "There are no addons controlling this setting yet"); + + await ExtensionSettingsStore.addSetting( + "@first", + "foo", + "bar", + "set", + () => "not set" + ); + + items = ExtensionSettingsStore.getAllSettings("foo", "bar"); + equal(items.length, 1, "The add-on setting has 1 addon trying to control it"); + + await ExtensionSettingsStore.addSetting( + "@second", + "foo", + "bar", + "setting", + () => "not set" + ); + + let item = ExtensionSettingsStore.getSetting("foo", "bar"); + equal(item.id, "@second", "The second add-on is in control"); + equal(item.value, "setting", "The second value is set"); + + items = ExtensionSettingsStore.getAllSettings("foo", "bar"); + equal( + items.length, + 2, + "The add-on setting has 2 addons trying to control it" + ); + + await ExtensionSettingsStore.removeSetting("@first", "foo", "bar"); + + items = ExtensionSettingsStore.getAllSettings("foo", "bar"); + equal(items.length, 1, "There is only 1 addon controlling this setting"); + + await ExtensionSettingsStore.removeSetting("@second", "foo", "bar"); + + items = ExtensionSettingsStore.getAllSettings("foo", "bar"); + equal( + items.length, + 0, + "There is no longer any addon controlling this setting" + ); + + for (let extension of testExtensions) { + await extension.unload(); + } + + await promiseShutdownManager(); +}); |