summaryrefslogtreecommitdiffstats
path: root/testing/modules/MockRegistry.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'testing/modules/MockRegistry.sys.mjs')
-rw-r--r--testing/modules/MockRegistry.sys.mjs347
1 files changed, 269 insertions, 78 deletions
diff --git a/testing/modules/MockRegistry.sys.mjs b/testing/modules/MockRegistry.sys.mjs
index b64d1ce2a8..acb17d790b 100644
--- a/testing/modules/MockRegistry.sys.mjs
+++ b/testing/modules/MockRegistry.sys.mjs
@@ -4,81 +4,231 @@
import { MockRegistrar } from "resource://testing-common/MockRegistrar.sys.mjs";
-export class MockRegistry {
- constructor() {
- // Three level structure of Maps pointing to Maps pointing to Maps
- // this.roots is the top of the structure and has ROOT_KEY_* values
- // as keys. Maps at the second level are the values of the first
- // level Map, they have registry keys (also called paths) as keys.
- // Third level maps are the values in second level maps, they have
- // map registry names to corresponding values (which in this implementation
- // are always strings).
- this.roots = new Map([
- [Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, new Map()],
- [Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, new Map()],
- [Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT, new Map()],
- ]);
+class MockWindowsRegKey {
+ key = null;
- let registry = this;
-
- /**
- * This is a mock nsIWindowsRegistry implementation. It only implements a
- * subset of the interface used in tests. In particular, only values
- * of type string are supported.
- */
- function MockWindowsRegKey() {}
- MockWindowsRegKey.prototype = {
- values: null,
-
- // --- Overridden nsISupports interface functions ---
- QueryInterface: ChromeUtils.generateQI(["nsIWindowsRegKey"]),
-
- // --- Overridden nsIWindowsRegKey interface functions ---
- open(root, path) {
- let rootKey = registry.getRoot(root);
- if (!rootKey.has(path)) {
- rootKey.set(path, new Map());
- }
- this.values = rootKey.get(path);
- },
+ // --- Overridden nsISupports interface functions ---
+ QueryInterface = ChromeUtils.generateQI(["nsIWindowsRegKey"]);
- close() {
- this.values = null;
- },
+ #assertKey() {
+ if (this.key) {
+ return;
+ }
+ throw Components.Exception("invalid registry path", Cr.NS_ERROR_FAILURE);
+ }
- get valueCount() {
- if (!this.values) {
- throw Components.Exception("", Cr.NS_ERROR_FAILURE);
- }
- return this.values.size;
- },
+ #findOrMaybeCreateKey(root, path, mode, maybeCreateCallback) {
+ let rootKey = MockRegistry.getRoot(root);
+ let parts = path.split("\\");
+ if (parts.some(part => !part.length)) {
+ throw Components.Exception("", Cr.NS_ERROR_FAILURE);
+ }
- hasValue(name) {
- if (!this.values) {
- return false;
- }
- return this.values.has(name);
- },
+ let key = rootKey;
+ for (let part of parts) {
+ if (!key.subkeys.has(part)) {
+ maybeCreateCallback(key.subkeys, part);
+ }
+ key = key.subkeys.get(part);
+ }
+ this.key = key;
+ }
- getValueType() {
- return Ci.nsIWindowsRegKey.TYPE_STRING;
- },
+ // --- Overridden nsIWindowsRegKey interface functions ---
+ open(root, path, mode) {
+ // eslint-disable-next-line no-unused-vars
+ this.#findOrMaybeCreateKey(root, path, mode, (subkeys, part) => {
+ throw Components.Exception("", Cr.NS_ERROR_FAILURE);
+ });
+ }
- getValueName(index) {
- if (!this.values || index >= this.values.size) {
- throw Components.Exception("", Cr.NS_ERROR_FAILURE);
- }
- let names = Array.from(this.values.keys());
- return names[index];
- },
+ create(root, path, mode) {
+ this.#findOrMaybeCreateKey(root, path, mode, (subkeys, part) =>
+ subkeys.set(part, { subkeys: new Map(), values: new Map() })
+ );
+ }
- readStringValue(name) {
- if (!this.values) {
- throw new Error("invalid registry path");
- }
- return this.values.get(name);
- },
- };
+ close() {
+ this.key = null;
+ }
+
+ get valueCount() {
+ this.#assertKey();
+ return this.key.values.size;
+ }
+
+ hasValue(name) {
+ this.#assertKey();
+ return this.key.values.has(name);
+ }
+
+ #getValuePair(name, expectedType = null) {
+ this.#assertKey();
+ if (!this.key.values.has(name)) {
+ throw Components.Exception("invalid value name", Cr.NS_ERROR_FAILURE);
+ }
+ let [value, type] = this.key.values.get(name);
+ if (expectedType && type !== expectedType) {
+ throw Components.Exception("unexpected value type", Cr.NS_ERROR_FAILURE);
+ }
+ return [value, type];
+ }
+
+ getValueType(name) {
+ let [, type] = this.#getValuePair(name);
+ return type;
+ }
+
+ getValueName(index) {
+ if (!this.key || index >= this.key.values.size) {
+ throw Components.Exception("", Cr.NS_ERROR_FAILURE);
+ }
+ let names = Array.from(this.key.values.keys());
+ return names[index];
+ }
+
+ readStringValue(name) {
+ let [value] = this.#getValuePair(name, Ci.nsIWindowsRegKey.TYPE_STRING);
+ return value;
+ }
+
+ readIntValue(name) {
+ let [value] = this.#getValuePair(name, Ci.nsIWindowsRegKey.TYPE_INT);
+ return value;
+ }
+
+ readInt64Value(name) {
+ let [value] = this.#getValuePair(name, Ci.nsIWindowsRegKey.TYPE_INT64);
+ return value;
+ }
+
+ readBinaryValue(name) {
+ let [value] = this.#getValuePair(name, Ci.nsIWindowsRegKey.TYPE_BINARY);
+ return value;
+ }
+
+ #writeValuePair(name, value, type) {
+ this.#assertKey();
+ this.key.values.set(name, [value, type]);
+ }
+
+ writeStringValue(name, value) {
+ this.#writeValuePair(name, value, Ci.nsIWindowsRegKey.TYPE_STRING);
+ }
+
+ writeIntValue(name, value) {
+ this.#writeValuePair(name, value, Ci.nsIWindowsRegKey.TYPE_INT);
+ }
+
+ writeInt64Value(name, value) {
+ this.#writeValuePair(name, value, Ci.nsIWindowsRegKey.TYPE_INT64);
+ }
+
+ writeBinaryValue(name, value) {
+ this.#writeValuePair(name, value, Ci.nsIWindowsRegKey.TYPE_BINARY);
+ }
+
+ removeValue(name) {
+ this.#assertKey();
+ this.key.values.delete(name);
+ }
+
+ get childCount() {
+ this.#assertKey();
+ return this.key.subkeys.size;
+ }
+
+ getChildName(index) {
+ if (!this.key || index >= this.key.values.size) {
+ throw Components.Exception("", Cr.NS_ERROR_FAILURE);
+ }
+ let names = Array.from(this.key.subkeys.keys());
+ return names[index];
+ }
+
+ hasChild(name) {
+ this.#assertKey();
+ return this.key.subkeys.has(name);
+ }
+
+ removeChild(name) {
+ this.#assertKey();
+ let child = this.key.subkeys.get(name);
+
+ if (!child) {
+ throw Components.Exception("", Cr.NS_ERROR_FAILURE);
+ }
+ if (child.subkeys.size > 0) {
+ throw Components.Exception("", Cr.NS_ERROR_FAILURE);
+ }
+
+ this.key.subkeys.delete(name);
+ }
+
+ #findOrMaybeCreateChild(name, mode, maybeCreateCallback) {
+ this.#assertKey();
+ if (name.split("\\").length > 1) {
+ throw Components.Exception("", Cr.NS_ERROR_FAILURE);
+ }
+ if (!this.key.subkeys.has(name)) {
+ maybeCreateCallback(this.key.subkeys, name);
+ }
+ // This won't wrap in the same way as `Cc["@mozilla.org/windows-registry-key;1"].createInstance(nsIWindowsRegKey);`.
+ let subKey = new MockWindowsRegKey();
+ subKey.key = this.key.subkeys.get(name);
+ return subKey;
+ }
+
+ openChild(name, mode) {
+ // eslint-disable-next-line no-unused-vars
+ return this.#findOrMaybeCreateChild(name, mode, (subkeys, part) => {
+ throw Components.Exception("", Cr.NS_ERROR_FAILURE);
+ });
+ }
+
+ createChild(name, mode) {
+ return this.#findOrMaybeCreateChild(name, mode, (subkeys, part) =>
+ subkeys.set(part, { subkeys: new Map(), values: new Map() })
+ );
+ }
+}
+
+export class MockRegistry {
+ // All instances of `MockRegistry` share a single data-store; this is
+ // conceptually parallel to the Windows registry, which is a shared global
+ // resource. It would be possible to have separate data-stores for separate
+ // instances with a little adjustment to `MockWindowsRegKey`.
+ //
+ // Top-level map is indexed by roots. A "key" is an object that has
+ // `subkeys` and `values`, both maps indexed by strings. Subkey items are
+ // again "key" objects. Value items are `[value, type]` pairs.
+ //
+ // In pseudo-code:
+ //
+ // {Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER:
+ // {subkeys:
+ // {child: {subkeys: {}, values: {key: ["string_value", Ci.nsIWindowsRegKey.TYPE_STRING]}}},
+ // values: {}
+ // },
+ // ...
+ // }
+ static roots;
+
+ constructor() {
+ MockRegistry.roots = new Map([
+ [
+ Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ { subkeys: new Map(), values: new Map() },
+ ],
+ [
+ Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ { subkeys: new Map(), values: new Map() },
+ ],
+ [
+ Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT,
+ { subkeys: new Map(), values: new Map() },
+ ],
+ ]);
// See bug 1688838 - nsNotifyAddrListener::CheckAdaptersAddresses might
// attempt to use the registry off the main thread, so we disable that
@@ -100,7 +250,7 @@ export class MockRegistry {
this.cid = MockRegistrar.register(
"@mozilla.org/windows-registry-key;1",
- MockWindowsRegKey
+ () => new MockWindowsRegKey()
);
}
@@ -121,7 +271,7 @@ export class MockRegistry {
this.cid = null;
}
- getRoot(root) {
+ static getRoot(root) {
if (!this.roots.has(root)) {
throw new Error(`No such root ${root}`);
}
@@ -129,17 +279,58 @@ export class MockRegistry {
}
setValue(root, path, name, value) {
- let rootKey = this.getRoot(root);
-
- if (!rootKey.has(path)) {
- rootKey.set(path, new Map());
+ let key = new MockWindowsRegKey();
+ key.create(root, path, Ci.nsIWindowsRegKey.ACCESS_ALL);
+ if (value == null) {
+ try {
+ key.removeValue(name);
+ } catch (e) {
+ if (
+ !(e instanceof Ci.nsIException && e.result == Cr.NS_ERROR_FAILURE)
+ ) {
+ throw e;
+ }
+ }
+ } else {
+ key.writeStringValue(name, value);
}
+ }
- let pathmap = rootKey.get(path);
- if (value == null) {
- pathmap.delete(name);
+ /**
+ * Dump given `key` (or, if not given, all roots), and all its value and its
+ * subkeys recursively, using the given function to `printOneLine`.
+ */
+ static dump(key = null, indent = "", printOneLine = console.log) {
+ let types = new Map([
+ [1, "REG_SZ"],
+ [3, "REG_BINARY"],
+ [4, "REG_DWORD"],
+ [11, "REG_QWORD"],
+ ]);
+
+ if (!key) {
+ let roots = [
+ "ROOT_KEY_LOCAL_MACHINE",
+ "ROOT_KEY_CURRENT_USER",
+ "ROOT_KEY_CLASSES_ROOT",
+ ];
+ for (let root of roots) {
+ printOneLine(indent + root);
+ this.dump(
+ this.roots.get(Ci.nsIWindowsRegKey[root]),
+ " " + indent,
+ printOneLine
+ );
+ }
} else {
- pathmap.set(name, value);
+ for (let [k, v] of key.values.entries()) {
+ let [value, type] = v;
+ printOneLine(`${indent}${k}: ${value} (${types.get(type)})`);
+ }
+ for (let [k, child] of key.subkeys.entries()) {
+ printOneLine(indent + k);
+ this.dump(child, " " + indent, printOneLine);
+ }
}
}
}