summaryrefslogtreecommitdiff
path: root/toolkit/components/contentprefs/tests/unit_cps2/head.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/contentprefs/tests/unit_cps2/head.js')
-rw-r--r--toolkit/components/contentprefs/tests/unit_cps2/head.js401
1 files changed, 401 insertions, 0 deletions
diff --git a/toolkit/components/contentprefs/tests/unit_cps2/head.js b/toolkit/components/contentprefs/tests/unit_cps2/head.js
new file mode 100644
index 0000000000..b86abe208a
--- /dev/null
+++ b/toolkit/components/contentprefs/tests/unit_cps2/head.js
@@ -0,0 +1,401 @@
+/* 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/. */
+
+var { interfaces: Ci, classes: Cc, results: Cr, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+var cps;
+var asyncRunner;
+var next;
+
+(function init() {
+ // There has to be a profile directory before the CPS service is gotten.
+ do_get_profile();
+})();
+
+function runAsyncTests(tests, dontResetBefore = false) {
+ do_test_pending();
+
+ cps = Cc["@mozilla.org/content-pref/service;1"].
+ getService(Ci.nsIContentPrefService2);
+
+ let s = {};
+ Cu.import("resource://test/AsyncRunner.jsm", s);
+ asyncRunner = new s.AsyncRunner({
+ done: do_test_finished,
+ error: function (err) {
+ // xpcshell test functions like equal throw NS_ERROR_ABORT on
+ // failure. Ignore those and catch only uncaught exceptions.
+ if (err !== Cr.NS_ERROR_ABORT) {
+ if (err.stack) {
+ err = err + "\n\nTraceback (most recent call first):\n" + err.stack +
+ "\nUseless do_throw stack:";
+ }
+ do_throw(err);
+ }
+ },
+ consoleError: function (scriptErr) {
+ // Previously, this code checked for console errors related to the test,
+ // and treated them as failures. This was problematic, because our current
+ // very-broken exception reporting machinery in XPCWrappedJSClass reports
+ // errors to the console even if there's actually JS on the stack above
+ // that will catch them. And a lot of the tests here intentionally trigger
+ // error conditions on the JS-implemented XPCOM component (see erroneous()
+ // in test_getSubdomains.js, for example). In the old world, we got lucky,
+ // and the errors were never reported to the console due to happenstantial
+ // JSContext reasons that aren't really worth going into.
+ //
+ // So. We make sure to dump this stuff so that it shows up in the logs, but
+ // don't turn them into duplicate failures of the exception that was already
+ // propagated to the caller.
+ dump("AsyncRunner.jsm observed console error: " + scriptErr + "\n");
+ }
+ });
+
+ next = asyncRunner.next.bind(asyncRunner);
+
+ do_register_cleanup(function () {
+ asyncRunner.destroy();
+ asyncRunner = null;
+ });
+
+ tests.forEach(function (test) {
+ function* gen() {
+ do_print("Running " + test.name);
+ yield test();
+ yield reset();
+ }
+ asyncRunner.appendIterator(gen());
+ });
+
+ // reset() ends up calling asyncRunner.next(), starting the tests.
+ if (dontResetBefore) {
+ next();
+ } else {
+ reset();
+ }
+}
+
+function makeCallback(callbacks, success = null) {
+ callbacks = callbacks || {};
+ if (!callbacks.handleError) {
+ callbacks.handleError = function (error) {
+ do_throw("handleError call was not expected, error: " + error);
+ };
+ }
+ if (!callbacks.handleResult) {
+ callbacks.handleResult = function() {
+ do_throw("handleResult call was not expected");
+ };
+ }
+ if (!callbacks.handleCompletion)
+ callbacks.handleCompletion = function (reason) {
+ equal(reason, Ci.nsIContentPrefCallback2.COMPLETE_OK);
+ if (success) {
+ success();
+ } else {
+ next();
+ }
+ };
+ return callbacks;
+}
+
+function do_check_throws(fn) {
+ let threw = false;
+ try {
+ fn();
+ }
+ catch (err) {
+ threw = true;
+ }
+ ok(threw);
+}
+
+function sendMessage(msg, callback) {
+ let obj = callback || {};
+ let ref = Cu.getWeakReference(obj);
+ cps.QueryInterface(Ci.nsIObserver).observe(ref, "test:" + msg, null);
+ return "value" in obj ? obj.value : undefined;
+}
+
+function reset() {
+ sendMessage("reset", next);
+}
+
+function setWithDate(group, name, val, timestamp, context) {
+ function updateDate() {
+ let db = sendMessage("db");
+ let stmt = db.createAsyncStatement(`
+ UPDATE prefs SET timestamp = :timestamp
+ WHERE
+ settingID = (SELECT id FROM settings WHERE name = :name)
+ AND groupID = (SELECT id FROM groups WHERE name = :group)
+ `);
+ stmt.params.timestamp = timestamp / 1000;
+ stmt.params.name = name;
+ stmt.params.group = group;
+
+ stmt.executeAsync({
+ handleCompletion: function (reason) {
+ next();
+ },
+ handleError: function (err) {
+ do_throw(err);
+ }
+ });
+ stmt.finalize();
+ }
+
+ cps.set(group, name, val, context, makeCallback(null, updateDate));
+}
+
+function getDate(group, name, context) {
+ let db = sendMessage("db");
+ let stmt = db.createAsyncStatement(`
+ SELECT timestamp FROM prefs
+ WHERE
+ settingID = (SELECT id FROM settings WHERE name = :name)
+ AND groupID = (SELECT id FROM groups WHERE name = :group)
+ `);
+ stmt.params.name = name;
+ stmt.params.group = group;
+
+ let res;
+ stmt.executeAsync({
+ handleResult: function (results) {
+ let row = results.getNextRow();
+ res = row.getResultByName("timestamp");
+ },
+ handleCompletion: function (reason) {
+ next(res * 1000);
+ },
+ handleError: function (err) {
+ do_throw(err);
+ }
+ });
+ stmt.finalize();
+}
+
+function set(group, name, val, context) {
+ cps.set(group, name, val, context, makeCallback());
+}
+
+function setGlobal(name, val, context) {
+ cps.setGlobal(name, val, context, makeCallback());
+}
+
+function prefOK(actual, expected, strict) {
+ ok(actual instanceof Ci.nsIContentPref);
+ equal(actual.domain, expected.domain);
+ equal(actual.name, expected.name);
+ if (strict)
+ strictEqual(actual.value, expected.value);
+ else
+ equal(actual.value, expected.value);
+}
+
+function* getOK(args, expectedVal, expectedGroup, strict) {
+ if (args.length == 2)
+ args.push(undefined);
+ let expectedPrefs = expectedVal === undefined ? [] :
+ [{ domain: expectedGroup || args[0],
+ name: args[1],
+ value: expectedVal }];
+ yield getOKEx("getByDomainAndName", args, expectedPrefs, strict);
+}
+
+function* getSubdomainsOK(args, expectedGroupValPairs) {
+ if (args.length == 2)
+ args.push(undefined);
+ let expectedPrefs = expectedGroupValPairs.map(function ([group, val]) {
+ return { domain: group, name: args[1], value: val };
+ });
+ yield getOKEx("getBySubdomainAndName", args, expectedPrefs);
+}
+
+function* getGlobalOK(args, expectedVal) {
+ if (args.length == 1)
+ args.push(undefined);
+ let expectedPrefs = expectedVal === undefined ? [] :
+ [{ domain: null, name: args[0], value: expectedVal }];
+ yield getOKEx("getGlobal", args, expectedPrefs);
+}
+
+function* getOKEx(methodName, args, expectedPrefs, strict, context) {
+ let actualPrefs = [];
+ args.push(makeCallback({
+ handleResult: pref => actualPrefs.push(pref)
+ }));
+ yield cps[methodName].apply(cps, args);
+ arraysOfArraysOK([actualPrefs], [expectedPrefs], function (actual, expected) {
+ prefOK(actual, expected, strict);
+ });
+}
+
+function getCachedOK(args, expectedIsCached, expectedVal, expectedGroup,
+ strict) {
+ if (args.length == 2)
+ args.push(undefined);
+ let expectedPref = !expectedIsCached ? null : {
+ domain: expectedGroup || args[0],
+ name: args[1],
+ value: expectedVal
+ };
+ getCachedOKEx("getCachedByDomainAndName", args, expectedPref, strict);
+}
+
+function getCachedSubdomainsOK(args, expectedGroupValPairs) {
+ if (args.length == 2)
+ args.push(undefined);
+ let len = {};
+ args.push(len);
+ let actualPrefs = cps.getCachedBySubdomainAndName.apply(cps, args);
+ actualPrefs = actualPrefs.sort(function (a, b) {
+ return a.domain.localeCompare(b.domain);
+ });
+ equal(actualPrefs.length, len.value);
+ let expectedPrefs = expectedGroupValPairs.map(function ([group, val]) {
+ return { domain: group, name: args[1], value: val };
+ });
+ arraysOfArraysOK([actualPrefs], [expectedPrefs], prefOK);
+}
+
+function getCachedGlobalOK(args, expectedIsCached, expectedVal) {
+ if (args.length == 1)
+ args.push(undefined);
+ let expectedPref = !expectedIsCached ? null : {
+ domain: null,
+ name: args[0],
+ value: expectedVal
+ };
+ getCachedOKEx("getCachedGlobal", args, expectedPref);
+}
+
+function getCachedOKEx(methodName, args, expectedPref, strict) {
+ let actualPref = cps[methodName].apply(cps, args);
+ if (expectedPref)
+ prefOK(actualPref, expectedPref, strict);
+ else
+ strictEqual(actualPref, null);
+}
+
+function arraysOK(actual, expected, cmp) {
+ if (actual.length != expected.length) {
+ do_throw("Length is not equal: " + JSON.stringify(actual) + "==" + JSON.stringify(expected));
+ } else {
+ actual.forEach(function (actualElt, j) {
+ let expectedElt = expected[j];
+ cmp(actualElt, expectedElt);
+ });
+ }
+}
+
+function arraysOfArraysOK(actual, expected, cmp) {
+ cmp = cmp || equal;
+ arraysOK(actual, expected, function (act, exp) {
+ arraysOK(act, exp, cmp)
+ });
+}
+
+function dbOK(expectedRows) {
+ let db = sendMessage("db");
+ let stmt = db.createAsyncStatement(`
+ SELECT groups.name AS grp, settings.name AS name, prefs.value AS value
+ FROM prefs
+ LEFT JOIN groups ON groups.id = prefs.groupID
+ LEFT JOIN settings ON settings.id = prefs.settingID
+ UNION
+
+ /*
+ These second two SELECTs get the rows of the groups and settings tables
+ that aren't referenced by the prefs table. Neither should return any
+ rows if the component is working properly.
+ */
+ SELECT groups.name AS grp, NULL AS name, NULL AS value
+ FROM groups
+ WHERE id NOT IN (
+ SELECT DISTINCT groupID
+ FROM prefs
+ WHERE groupID NOTNULL
+ )
+ UNION
+ SELECT NULL AS grp, settings.name AS name, NULL AS value
+ FROM settings
+ WHERE id NOT IN (
+ SELECT DISTINCT settingID
+ FROM prefs
+ WHERE settingID NOTNULL
+ )
+
+ ORDER BY value ASC, grp ASC, name ASC
+ `);
+
+ let actualRows = [];
+ let cols = ["grp", "name", "value"];
+
+ db.executeAsync([stmt], 1, {
+ handleCompletion: function (reason) {
+ arraysOfArraysOK(actualRows, expectedRows);
+ next();
+ },
+ handleResult: function (results) {
+ let row = null;
+ while (row = results.getNextRow()) {
+ actualRows.push(cols.map(c => row.getResultByName(c)));
+ }
+ },
+ handleError: function (err) {
+ do_throw(err);
+ }
+ });
+ stmt.finalize();
+}
+
+function on(event, names, dontRemove) {
+ let args = {
+ reset: function () {
+ for (let prop in this) {
+ if (Array.isArray(this[prop]))
+ this[prop].splice(0, this[prop].length);
+ }
+ },
+ };
+
+ let observers = {};
+
+ names.forEach(function (name) {
+ let obs = {};
+ ["onContentPrefSet", "onContentPrefRemoved"].forEach(function (meth) {
+ obs[meth] = () => do_throw(meth + " should not be called");
+ });
+ obs["onContentPref" + event] = function () {
+ args[name].push(Array.slice(arguments));
+ };
+ observers[name] = obs;
+ args[name] = [];
+ args[name].observer = obs;
+ cps.addObserverForName(name, obs);
+ });
+
+ do_execute_soon(function () {
+ if (!dontRemove)
+ names.forEach(n => cps.removeObserverForName(n, observers[n]));
+ next(args);
+ });
+}
+
+function schemaVersionIs(expectedVersion) {
+ let db = sendMessage("db");
+ equal(db.schemaVersion, expectedVersion);
+}
+
+function wait() {
+ do_execute_soon(next);
+}
+
+function observerArgsOK(actualArgs, expectedArgs) {
+ notEqual(actualArgs, undefined);
+ arraysOfArraysOK(actualArgs, expectedArgs);
+}