Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 const { interfaces: Ci, classes: Cc, results: Cr, utils: Cu } = Components;
7 Cu.import("resource://gre/modules/Services.jsm");
9 var cps;
10 var asyncRunner;
11 var next;
13 (function init() {
14 // There has to be a profile directory before the CPS service is gotten.
15 do_get_profile();
16 })();
18 function runAsyncTests(tests) {
19 do_test_pending();
21 cps = Cc["@mozilla.org/content-pref/service;1"].
22 getService(Ci.nsIContentPrefService2);
24 // Without this the private-browsing service tries to open a dialog when you
25 // change its enabled state.
26 Services.prefs.setBoolPref("browser.privatebrowsing.keep_current_session",
27 true);
29 let s = {};
30 Cu.import("resource://test/AsyncRunner.jsm", s);
31 asyncRunner = new s.AsyncRunner({
32 done: do_test_finished,
33 error: function (err) {
34 // xpcshell test functions like do_check_eq throw NS_ERROR_ABORT on
35 // failure. Ignore those and catch only uncaught exceptions.
36 if (err !== Cr.NS_ERROR_ABORT) {
37 if (err.stack) {
38 err = err + "\n\nTraceback (most recent call first):\n" + err.stack +
39 "\nUseless do_throw stack:";
40 }
41 do_throw(err);
42 }
43 },
44 consoleError: function (scriptErr) {
45 // As much as possible make sure the error is related to the test. On the
46 // other hand if this fails to catch a test-related error, we'll hang.
47 let filename = scriptErr.sourceName || scriptErr.toString() || "";
48 if (/contentpref/i.test(filename))
49 do_throw(scriptErr);
50 }
51 });
53 next = asyncRunner.next.bind(asyncRunner);
55 do_register_cleanup(function () {
56 asyncRunner.destroy();
57 asyncRunner = null;
58 });
60 tests.forEach(function (test) {
61 function gen() {
62 do_print("Running " + test.name);
63 yield test();
64 yield reset();
65 }
66 asyncRunner.appendIterator(gen());
67 });
69 // reset() ends up calling asyncRunner.next(), starting the tests.
70 reset();
71 }
73 function makeCallback(callbacks) {
74 callbacks = callbacks || {};
75 ["handleResult", "handleError"].forEach(function (meth) {
76 if (!callbacks[meth])
77 callbacks[meth] = function () {
78 do_throw(meth + " shouldn't be called.");
79 };
80 });
81 if (!callbacks.handleCompletion)
82 callbacks.handleCompletion = function (reason) {
83 do_check_eq(reason, Ci.nsIContentPrefCallback2.COMPLETE_OK);
84 next();
85 };
86 return callbacks;
87 }
89 function do_check_throws(fn) {
90 let threw = false;
91 try {
92 fn();
93 }
94 catch (err) {
95 threw = true;
96 }
97 do_check_true(threw);
98 }
100 function sendMessage(msg, callback) {
101 let obj = callback || {};
102 let ref = Cu.getWeakReference(obj);
103 cps.QueryInterface(Ci.nsIObserver).observe(ref, "test:" + msg, null);
104 return "value" in obj ? obj.value : undefined;
105 }
107 function reset() {
108 sendMessage("reset", next);
109 }
111 function set(group, name, val, context) {
112 cps.set(group, name, val, context, makeCallback());
113 }
115 function setGlobal(name, val, context) {
116 cps.setGlobal(name, val, context, makeCallback());
117 }
119 function prefOK(actual, expected, strict) {
120 do_check_true(actual instanceof Ci.nsIContentPref);
121 do_check_eq(actual.domain, expected.domain);
122 do_check_eq(actual.name, expected.name);
123 if (strict)
124 do_check_true(actual.value === expected.value);
125 else
126 do_check_eq(actual.value, expected.value);
127 }
129 function getOK(args, expectedVal, expectedGroup, strict) {
130 if (args.length == 2)
131 args.push(undefined);
132 let expectedPrefs = expectedVal === undefined ? [] :
133 [{ domain: expectedGroup || args[0],
134 name: args[1],
135 value: expectedVal }];
136 yield getOKEx("getByDomainAndName", args, expectedPrefs, strict);
137 }
139 function getSubdomainsOK(args, expectedGroupValPairs) {
140 if (args.length == 2)
141 args.push(undefined);
142 let expectedPrefs = expectedGroupValPairs.map(function ([group, val]) {
143 return { domain: group, name: args[1], value: val };
144 });
145 yield getOKEx("getBySubdomainAndName", args, expectedPrefs);
146 }
148 function getGlobalOK(args, expectedVal) {
149 if (args.length == 1)
150 args.push(undefined);
151 let expectedPrefs = expectedVal === undefined ? [] :
152 [{ domain: null, name: args[0], value: expectedVal }];
153 yield getOKEx("getGlobal", args, expectedPrefs);
154 }
156 function getOKEx(methodName, args, expectedPrefs, strict, context) {
157 let actualPrefs = [];
158 args.push(makeCallback({
159 handleResult: function (pref) actualPrefs.push(pref)
160 }));
161 yield cps[methodName].apply(cps, args);
162 arraysOfArraysOK([actualPrefs], [expectedPrefs], function (actual, expected) {
163 prefOK(actual, expected, strict);
164 });
165 }
167 function getCachedOK(args, expectedIsCached, expectedVal, expectedGroup,
168 strict) {
169 if (args.length == 2)
170 args.push(undefined);
171 let expectedPref = !expectedIsCached ? null : {
172 domain: expectedGroup || args[0],
173 name: args[1],
174 value: expectedVal
175 };
176 getCachedOKEx("getCachedByDomainAndName", args, expectedPref, strict);
177 }
179 function getCachedSubdomainsOK(args, expectedGroupValPairs) {
180 if (args.length == 2)
181 args.push(undefined);
182 let len = {};
183 args.push(len);
184 let actualPrefs = cps.getCachedBySubdomainAndName.apply(cps, args);
185 actualPrefs = actualPrefs.sort(function (a, b) {
186 return a.domain.localeCompare(b.domain);
187 });
188 do_check_eq(actualPrefs.length, len.value);
189 let expectedPrefs = expectedGroupValPairs.map(function ([group, val]) {
190 return { domain: group, name: args[1], value: val };
191 });
192 arraysOfArraysOK([actualPrefs], [expectedPrefs], prefOK);
193 }
195 function getCachedGlobalOK(args, expectedIsCached, expectedVal) {
196 if (args.length == 1)
197 args.push(undefined);
198 let expectedPref = !expectedIsCached ? null : {
199 domain: null,
200 name: args[0],
201 value: expectedVal
202 };
203 getCachedOKEx("getCachedGlobal", args, expectedPref);
204 }
206 function getCachedOKEx(methodName, args, expectedPref, strict) {
207 let actualPref = cps[methodName].apply(cps, args);
208 if (expectedPref)
209 prefOK(actualPref, expectedPref, strict);
210 else
211 do_check_true(actualPref === null);
212 }
214 function arraysOfArraysOK(actual, expected, cmp) {
215 cmp = cmp || function (a, b) do_check_eq(a, b);
216 do_check_eq(actual.length, expected.length);
217 actual.forEach(function (actualChildArr, i) {
218 let expectedChildArr = expected[i];
219 do_check_eq(actualChildArr.length, expectedChildArr.length);
220 actualChildArr.forEach(function (actualElt, j) {
221 let expectedElt = expectedChildArr[j];
222 cmp(actualElt, expectedElt);
223 });
224 });
225 }
227 function dbOK(expectedRows) {
228 let db = sendMessage("db");
229 let stmt = db.createAsyncStatement(
230 "SELECT groups.name AS grp, settings.name AS name, prefs.value AS value " +
231 "FROM prefs " +
232 "LEFT JOIN groups ON groups.id = prefs.groupID " +
233 "LEFT JOIN settings ON settings.id = prefs.settingID " +
234 "UNION " +
236 // These second two SELECTs get the rows of the groups and settings tables
237 // that aren't referenced by the prefs table. Neither should return any
238 // rows if the component is working properly.
239 "SELECT groups.name AS grp, NULL AS name, NULL AS value " +
240 "FROM groups " +
241 "WHERE id NOT IN (" +
242 "SELECT DISTINCT groupID " +
243 "FROM prefs " +
244 "WHERE groupID NOTNULL" +
245 ") " +
246 "UNION " +
247 "SELECT NULL AS grp, settings.name AS name, NULL AS value " +
248 "FROM settings " +
249 "WHERE id NOT IN (" +
250 "SELECT DISTINCT settingID " +
251 "FROM prefs " +
252 "WHERE settingID NOTNULL" +
253 ") " +
255 "ORDER BY value ASC, grp ASC, name ASC"
256 );
258 let actualRows = [];
259 let cols = ["grp", "name", "value"];
261 db.executeAsync([stmt], 1, {
262 handleCompletion: function (reason) {
263 arraysOfArraysOK(actualRows, expectedRows);
264 next();
265 },
266 handleResult: function (results) {
267 let row = null;
268 while (row = results.getNextRow()) {
269 actualRows.push(cols.map(function (c) row.getResultByName(c)));
270 }
271 },
272 handleError: function (err) {
273 do_throw(err);
274 }
275 });
276 stmt.finalize();
277 }
279 function on(event, names, dontRemove) {
280 let args = {
281 reset: function () {
282 for (let prop in this) {
283 if (Array.isArray(this[prop]))
284 this[prop].splice(0, this[prop].length);
285 }
286 },
287 };
289 let observers = {};
291 names.forEach(function (name) {
292 let obs = {};
293 ["onContentPrefSet", "onContentPrefRemoved"].forEach(function (meth) {
294 obs[meth] = function () do_throw(meth + " should not be called");
295 });
296 obs["onContentPref" + event] = function () {
297 args[name].push(Array.slice(arguments));
298 };
299 observers[name] = obs;
300 args[name] = [];
301 args[name].observer = obs;
302 cps.addObserverForName(name, obs);
303 });
305 do_execute_soon(function () {
306 if (!dontRemove)
307 names.forEach(function (n) cps.removeObserverForName(n, observers[n]));
308 next(args);
309 });
310 }
312 function wait() {
313 do_execute_soon(next);
314 }
316 function observerArgsOK(actualArgs, expectedArgs) {
317 do_check_neq(actualArgs, undefined);
318 arraysOfArraysOK(actualArgs, expectedArgs);
319 }