diff options
Diffstat (limited to 'toolkit/components/contentprefs/tests/unit_cps2/head.js')
-rw-r--r-- | toolkit/components/contentprefs/tests/unit_cps2/head.js | 401 |
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); +} |