|
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/. */ |
|
4 |
|
5 const { interfaces: Ci, classes: Cc, results: Cr, utils: Cu } = Components; |
|
6 |
|
7 Cu.import("resource://gre/modules/Services.jsm"); |
|
8 |
|
9 var cps; |
|
10 var asyncRunner; |
|
11 var next; |
|
12 |
|
13 (function init() { |
|
14 // There has to be a profile directory before the CPS service is gotten. |
|
15 do_get_profile(); |
|
16 })(); |
|
17 |
|
18 function runAsyncTests(tests) { |
|
19 do_test_pending(); |
|
20 |
|
21 cps = Cc["@mozilla.org/content-pref/service;1"]. |
|
22 getService(Ci.nsIContentPrefService2); |
|
23 |
|
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); |
|
28 |
|
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 }); |
|
52 |
|
53 next = asyncRunner.next.bind(asyncRunner); |
|
54 |
|
55 do_register_cleanup(function () { |
|
56 asyncRunner.destroy(); |
|
57 asyncRunner = null; |
|
58 }); |
|
59 |
|
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 }); |
|
68 |
|
69 // reset() ends up calling asyncRunner.next(), starting the tests. |
|
70 reset(); |
|
71 } |
|
72 |
|
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 } |
|
88 |
|
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 } |
|
99 |
|
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 } |
|
106 |
|
107 function reset() { |
|
108 sendMessage("reset", next); |
|
109 } |
|
110 |
|
111 function set(group, name, val, context) { |
|
112 cps.set(group, name, val, context, makeCallback()); |
|
113 } |
|
114 |
|
115 function setGlobal(name, val, context) { |
|
116 cps.setGlobal(name, val, context, makeCallback()); |
|
117 } |
|
118 |
|
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 } |
|
128 |
|
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 } |
|
138 |
|
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 } |
|
147 |
|
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 } |
|
155 |
|
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 } |
|
166 |
|
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 } |
|
178 |
|
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 } |
|
194 |
|
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 } |
|
205 |
|
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 } |
|
213 |
|
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 } |
|
226 |
|
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 " + |
|
235 |
|
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 ") " + |
|
254 |
|
255 "ORDER BY value ASC, grp ASC, name ASC" |
|
256 ); |
|
257 |
|
258 let actualRows = []; |
|
259 let cols = ["grp", "name", "value"]; |
|
260 |
|
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 } |
|
278 |
|
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 }; |
|
288 |
|
289 let observers = {}; |
|
290 |
|
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 }); |
|
304 |
|
305 do_execute_soon(function () { |
|
306 if (!dontRemove) |
|
307 names.forEach(function (n) cps.removeObserverForName(n, observers[n])); |
|
308 next(args); |
|
309 }); |
|
310 } |
|
311 |
|
312 function wait() { |
|
313 do_execute_soon(next); |
|
314 } |
|
315 |
|
316 function observerArgsOK(actualArgs, expectedArgs) { |
|
317 do_check_neq(actualArgs, undefined); |
|
318 arraysOfArraysOK(actualArgs, expectedArgs); |
|
319 } |