|
1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- / |
|
2 /* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #if BOOTSTRAP |
|
8 this.EXPORTED_SYMBOLS = ["OnRefTestLoad"]; |
|
9 #endif |
|
10 |
|
11 |
|
12 const CC = Components.classes; |
|
13 const CI = Components.interfaces; |
|
14 const CR = Components.results; |
|
15 |
|
16 const XHTML_NS = "http://www.w3.org/1999/xhtml"; |
|
17 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
|
18 |
|
19 const NS_LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1"; |
|
20 const NS_GFXINFO_CONTRACTID = "@mozilla.org/gfx/info;1"; |
|
21 const IO_SERVICE_CONTRACTID = "@mozilla.org/network/io-service;1"; |
|
22 const DEBUG_CONTRACTID = "@mozilla.org/xpcom/debug;1"; |
|
23 const NS_LOCALFILEINPUTSTREAM_CONTRACTID = |
|
24 "@mozilla.org/network/file-input-stream;1"; |
|
25 const NS_SCRIPTSECURITYMANAGER_CONTRACTID = |
|
26 "@mozilla.org/scriptsecuritymanager;1"; |
|
27 const NS_REFTESTHELPER_CONTRACTID = |
|
28 "@mozilla.org/reftest-helper;1"; |
|
29 const NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX = |
|
30 "@mozilla.org/network/protocol;1?name="; |
|
31 const NS_XREAPPINFO_CONTRACTID = |
|
32 "@mozilla.org/xre/app-info;1"; |
|
33 const NS_DIRECTORY_SERVICE_CONTRACTID = |
|
34 "@mozilla.org/file/directory_service;1"; |
|
35 const NS_OBSERVER_SERVICE_CONTRACTID = |
|
36 "@mozilla.org/observer-service;1"; |
|
37 |
|
38 Components.utils.import("resource://gre/modules/FileUtils.jsm"); |
|
39 |
|
40 var gLoadTimeout = 0; |
|
41 var gTimeoutHook = null; |
|
42 var gRemote = false; |
|
43 var gIgnoreWindowSize = false; |
|
44 var gShuffle = false; |
|
45 var gTotalChunks = 0; |
|
46 var gThisChunk = 0; |
|
47 var gContainingWindow = null; |
|
48 var gURLFilterRegex = null; |
|
49 const FOCUS_FILTER_ALL_TESTS = "all"; |
|
50 const FOCUS_FILTER_NEEDS_FOCUS_TESTS = "needs-focus"; |
|
51 const FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS = "non-needs-focus"; |
|
52 var gFocusFilterMode = FOCUS_FILTER_ALL_TESTS; |
|
53 |
|
54 // "<!--CLEAR-->" |
|
55 const BLANK_URL_FOR_CLEARING = "data:text/html;charset=UTF-8,%3C%21%2D%2DCLEAR%2D%2D%3E"; |
|
56 |
|
57 var gBrowser; |
|
58 // Are we testing web content loaded in a separate process? |
|
59 var gBrowserIsRemote; // bool |
|
60 // Are we using <iframe mozbrowser>? |
|
61 var gBrowserIsIframe; // bool |
|
62 var gBrowserMessageManager; |
|
63 var gCanvas1, gCanvas2; |
|
64 // gCurrentCanvas is non-null between InitCurrentCanvasWithSnapshot and the next |
|
65 // RecordResult. |
|
66 var gCurrentCanvas = null; |
|
67 var gURLs; |
|
68 // Map from URI spec to the number of times it remains to be used |
|
69 var gURIUseCounts; |
|
70 // Map from URI spec to the canvas rendered for that URI |
|
71 var gURICanvases; |
|
72 var gTestResults = { |
|
73 // Successful... |
|
74 Pass: 0, |
|
75 LoadOnly: 0, |
|
76 // Unexpected... |
|
77 Exception: 0, |
|
78 FailedLoad: 0, |
|
79 UnexpectedFail: 0, |
|
80 UnexpectedPass: 0, |
|
81 AssertionUnexpected: 0, |
|
82 AssertionUnexpectedFixed: 0, |
|
83 // Known problems... |
|
84 KnownFail : 0, |
|
85 AssertionKnown: 0, |
|
86 Random : 0, |
|
87 Skip: 0, |
|
88 Slow: 0, |
|
89 }; |
|
90 var gTotalTests = 0; |
|
91 var gState; |
|
92 var gCurrentURL; |
|
93 var gTestLog = []; |
|
94 var gServer; |
|
95 var gCount = 0; |
|
96 var gAssertionCount = 0; |
|
97 |
|
98 var gIOService; |
|
99 var gDebug; |
|
100 var gWindowUtils; |
|
101 |
|
102 var gSlowestTestTime = 0; |
|
103 var gSlowestTestURL; |
|
104 |
|
105 var gDrawWindowFlags; |
|
106 |
|
107 var gExpectingProcessCrash = false; |
|
108 var gExpectedCrashDumpFiles = []; |
|
109 var gUnexpectedCrashDumpFiles = { }; |
|
110 var gCrashDumpDir; |
|
111 var gFailedNoPaint = false; |
|
112 |
|
113 // The enabled-state of the test-plugins, stored so they can be reset later |
|
114 var gTestPluginEnabledStates = null; |
|
115 |
|
116 const TYPE_REFTEST_EQUAL = '=='; |
|
117 const TYPE_REFTEST_NOTEQUAL = '!='; |
|
118 const TYPE_LOAD = 'load'; // test without a reference (just test that it does |
|
119 // not assert, crash, hang, or leak) |
|
120 const TYPE_SCRIPT = 'script'; // test contains individual test results |
|
121 |
|
122 // The order of these constants matters, since when we have a status |
|
123 // listed for a *manifest*, we combine the status with the status for |
|
124 // the test by using the *larger*. |
|
125 // FIXME: In the future, we may also want to use this rule for combining |
|
126 // statuses that are on the same line (rather than making the last one |
|
127 // win). |
|
128 const EXPECTED_PASS = 0; |
|
129 const EXPECTED_FAIL = 1; |
|
130 const EXPECTED_RANDOM = 2; |
|
131 const EXPECTED_DEATH = 3; // test must be skipped to avoid e.g. crash/hang |
|
132 const EXPECTED_FUZZY = 4; |
|
133 |
|
134 // types of preference value we might want to set for a specific test |
|
135 const PREF_BOOLEAN = 0; |
|
136 const PREF_STRING = 1; |
|
137 const PREF_INTEGER = 2; |
|
138 |
|
139 var gPrefsToRestore = []; |
|
140 |
|
141 const gProtocolRE = /^\w+:/; |
|
142 const gPrefItemRE = /^(|test-|ref-)pref\((.+?),(.*)\)$/; |
|
143 |
|
144 var gHttpServerPort = -1; |
|
145 |
|
146 // whether to run slow tests or not |
|
147 var gRunSlowTests = true; |
|
148 |
|
149 // whether we should skip caching canvases |
|
150 var gNoCanvasCache = false; |
|
151 |
|
152 var gRecycledCanvases = new Array(); |
|
153 |
|
154 // By default we just log to stdout |
|
155 var gDumpLog = dump; |
|
156 var gVerbose = false; |
|
157 |
|
158 // Only dump the sandbox once, because it doesn't depend on the |
|
159 // manifest URL (yet!). |
|
160 var gDumpedConditionSandbox = false; |
|
161 |
|
162 function LogWarning(str) |
|
163 { |
|
164 gDumpLog("REFTEST INFO | " + str + "\n"); |
|
165 gTestLog.push(str); |
|
166 } |
|
167 |
|
168 function LogInfo(str) |
|
169 { |
|
170 if (gVerbose) |
|
171 gDumpLog("REFTEST INFO | " + str + "\n"); |
|
172 gTestLog.push(str); |
|
173 } |
|
174 |
|
175 function FlushTestLog() |
|
176 { |
|
177 if (!gVerbose) { |
|
178 // In verbose mode, we've dumped all these messages already. |
|
179 for (var i = 0; i < gTestLog.length; ++i) { |
|
180 gDumpLog("REFTEST INFO | Saved log: " + gTestLog[i] + "\n"); |
|
181 } |
|
182 } |
|
183 gTestLog = []; |
|
184 } |
|
185 |
|
186 function AllocateCanvas() |
|
187 { |
|
188 if (gRecycledCanvases.length > 0) |
|
189 return gRecycledCanvases.shift(); |
|
190 |
|
191 var canvas = gContainingWindow.document.createElementNS(XHTML_NS, "canvas"); |
|
192 var r = gBrowser.getBoundingClientRect(); |
|
193 canvas.setAttribute("width", Math.ceil(r.width)); |
|
194 canvas.setAttribute("height", Math.ceil(r.height)); |
|
195 |
|
196 return canvas; |
|
197 } |
|
198 |
|
199 function ReleaseCanvas(canvas) |
|
200 { |
|
201 // store a maximum of 2 canvases, if we're not caching |
|
202 if (!gNoCanvasCache || gRecycledCanvases.length < 2) |
|
203 gRecycledCanvases.push(canvas); |
|
204 } |
|
205 |
|
206 function IDForEventTarget(event) |
|
207 { |
|
208 try { |
|
209 return "'" + event.target.getAttribute('id') + "'"; |
|
210 } catch (ex) { |
|
211 return "<unknown>"; |
|
212 } |
|
213 } |
|
214 |
|
215 function getTestPlugin(aName) { |
|
216 var ph = CC["@mozilla.org/plugin/host;1"].getService(CI.nsIPluginHost); |
|
217 var tags = ph.getPluginTags(); |
|
218 |
|
219 // Find the test plugin |
|
220 for (var i = 0; i < tags.length; i++) { |
|
221 if (tags[i].name == aName) |
|
222 return tags[i]; |
|
223 } |
|
224 |
|
225 LogWarning("Failed to find the test-plugin."); |
|
226 return null; |
|
227 } |
|
228 |
|
229 this.OnRefTestLoad = function OnRefTestLoad(win) |
|
230 { |
|
231 gCrashDumpDir = CC[NS_DIRECTORY_SERVICE_CONTRACTID] |
|
232 .getService(CI.nsIProperties) |
|
233 .get("ProfD", CI.nsIFile); |
|
234 gCrashDumpDir.append("minidumps"); |
|
235 |
|
236 var env = CC["@mozilla.org/process/environment;1"]. |
|
237 getService(CI.nsIEnvironment); |
|
238 gVerbose = !!env.get("MOZ_REFTEST_VERBOSE"); |
|
239 |
|
240 var prefs = Components.classes["@mozilla.org/preferences-service;1"]. |
|
241 getService(Components.interfaces.nsIPrefBranch); |
|
242 try { |
|
243 gBrowserIsRemote = prefs.getBoolPref("browser.tabs.remote.autostart"); |
|
244 } catch (e) { |
|
245 gBrowserIsRemote = false; |
|
246 } |
|
247 |
|
248 try { |
|
249 gBrowserIsIframe = prefs.getBoolPref("reftest.browser.iframe.enabled"); |
|
250 } catch (e) { |
|
251 gBrowserIsIframe = false; |
|
252 } |
|
253 |
|
254 if (win === undefined || win == null) { |
|
255 win = window; |
|
256 } |
|
257 if (gContainingWindow == null && win != null) { |
|
258 gContainingWindow = win; |
|
259 } |
|
260 |
|
261 if (gBrowserIsIframe) { |
|
262 gBrowser = gContainingWindow.document.createElementNS(XHTML_NS, "iframe"); |
|
263 gBrowser.setAttribute("mozbrowser", ""); |
|
264 gBrowser.setAttribute("mozapp", prefs.getCharPref("browser.manifestURL")); |
|
265 } else { |
|
266 gBrowser = gContainingWindow.document.createElementNS(XUL_NS, "xul:browser"); |
|
267 } |
|
268 gBrowser.setAttribute("id", "browser"); |
|
269 gBrowser.setAttribute("type", "content-primary"); |
|
270 gBrowser.setAttribute("remote", gBrowserIsRemote ? "true" : "false"); |
|
271 gBrowser.setAttribute("mozasyncpanzoom", "true"); |
|
272 // Make sure the browser element is exactly 800x1000, no matter |
|
273 // what size our window is |
|
274 gBrowser.setAttribute("style", "min-width: 800px; min-height: 1000px; max-width: 800px; max-height: 1000px"); |
|
275 |
|
276 #ifdef BOOTSTRAP |
|
277 #ifdef REFTEST_B2G |
|
278 var doc = gContainingWindow.document.getElementsByTagName("html")[0]; |
|
279 #else |
|
280 var doc = gContainingWindow.document.getElementById('main-window'); |
|
281 #endif |
|
282 while (doc.hasChildNodes()) { |
|
283 doc.removeChild(doc.firstChild); |
|
284 } |
|
285 doc.appendChild(gBrowser); |
|
286 #else |
|
287 document.getElementById("reftest-window").appendChild(gBrowser); |
|
288 #endif |
|
289 |
|
290 // reftests should have the test plugins enabled, not click-to-play |
|
291 let plugin1 = getTestPlugin("Test Plug-in"); |
|
292 let plugin2 = getTestPlugin("Second Test Plug-in"); |
|
293 if (plugin1 && plugin2) { |
|
294 gTestPluginEnabledStates = [plugin1.enabledState, plugin2.enabledState]; |
|
295 plugin1.enabledState = CI.nsIPluginTag.STATE_ENABLED; |
|
296 plugin2.enabledState = CI.nsIPluginTag.STATE_ENABLED; |
|
297 } else { |
|
298 LogWarning("Could not get test plugin tags."); |
|
299 } |
|
300 |
|
301 gBrowserMessageManager = gBrowser.QueryInterface(CI.nsIFrameLoaderOwner) |
|
302 .frameLoader.messageManager; |
|
303 // The content script waits for the initial onload, then notifies |
|
304 // us. |
|
305 RegisterMessageListenersAndLoadContentScript(); |
|
306 } |
|
307 |
|
308 function InitAndStartRefTests() |
|
309 { |
|
310 /* These prefs are optional, so we don't need to spit an error to the log */ |
|
311 try { |
|
312 var prefs = Components.classes["@mozilla.org/preferences-service;1"]. |
|
313 getService(Components.interfaces.nsIPrefBranch); |
|
314 } catch(e) { |
|
315 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + e + "\n"); |
|
316 } |
|
317 |
|
318 try { |
|
319 prefs.setBoolPref("android.widget_paints_background", false); |
|
320 } catch (e) {} |
|
321 |
|
322 /* set the gLoadTimeout */ |
|
323 try { |
|
324 gLoadTimeout = prefs.getIntPref("reftest.timeout"); |
|
325 } catch(e) { |
|
326 gLoadTimeout = 5 * 60 * 1000; //5 minutes as per bug 479518 |
|
327 } |
|
328 |
|
329 /* Get the logfile for android tests */ |
|
330 try { |
|
331 var logFile = prefs.getCharPref("reftest.logFile"); |
|
332 if (logFile) { |
|
333 try { |
|
334 var f = FileUtils.File(logFile); |
|
335 var mfl = FileUtils.openFileOutputStream(f, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE); |
|
336 // Set to mirror to stdout as well as the file |
|
337 gDumpLog = function (msg) { |
|
338 #ifdef BOOTSTRAP |
|
339 #ifdef REFTEST_B2G |
|
340 dump(msg); |
|
341 #else |
|
342 //NOTE: on android-xul, we have a libc crash if we do a dump with %7s in the string |
|
343 #endif |
|
344 #else |
|
345 dump(msg); |
|
346 #endif |
|
347 mfl.write(msg, msg.length); |
|
348 }; |
|
349 } |
|
350 catch(e) { |
|
351 // If there is a problem, just use stdout |
|
352 gDumpLog = dump; |
|
353 } |
|
354 } |
|
355 } catch(e) {} |
|
356 |
|
357 try { |
|
358 gRemote = prefs.getBoolPref("reftest.remote"); |
|
359 } catch(e) { |
|
360 gRemote = false; |
|
361 } |
|
362 |
|
363 try { |
|
364 gIgnoreWindowSize = prefs.getBoolPref("reftest.ignoreWindowSize"); |
|
365 } catch(e) { |
|
366 gIgnoreWindowSize = false; |
|
367 } |
|
368 |
|
369 /* Support for running a chunk (subset) of tests. In separate try as this is optional */ |
|
370 try { |
|
371 gTotalChunks = prefs.getIntPref("reftest.totalChunks"); |
|
372 gThisChunk = prefs.getIntPref("reftest.thisChunk"); |
|
373 } |
|
374 catch(e) { |
|
375 gTotalChunks = 0; |
|
376 gThisChunk = 0; |
|
377 } |
|
378 |
|
379 try { |
|
380 gURLFilterRegex = new RegExp(prefs.getCharPref("reftest.filter")); |
|
381 } catch(e) {} |
|
382 |
|
383 try { |
|
384 gFocusFilterMode = prefs.getCharPref("reftest.focusFilterMode"); |
|
385 } catch(e) {} |
|
386 |
|
387 gWindowUtils = gContainingWindow.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils); |
|
388 if (!gWindowUtils || !gWindowUtils.compareCanvases) |
|
389 throw "nsIDOMWindowUtils inteface missing"; |
|
390 |
|
391 gIOService = CC[IO_SERVICE_CONTRACTID].getService(CI.nsIIOService); |
|
392 gDebug = CC[DEBUG_CONTRACTID].getService(CI.nsIDebug2); |
|
393 |
|
394 RegisterProcessCrashObservers(); |
|
395 |
|
396 if (gRemote) { |
|
397 gServer = null; |
|
398 } else { |
|
399 // not all gecko applications autoregister xpcom components |
|
400 if (CC["@mozilla.org/server/jshttp;1"] === undefined) { |
|
401 var file = CC["@mozilla.org/file/directory_service;1"]. |
|
402 getService(CI.nsIProperties).get("ProfD", CI.nsIFile); |
|
403 file.appendRelativePath("extensions/reftest@mozilla.org/chrome.manifest"); |
|
404 |
|
405 registrar = Components.manager.QueryInterface(CI.nsIComponentRegistrar); |
|
406 registrar.autoRegister(file); |
|
407 } |
|
408 gServer = CC["@mozilla.org/server/jshttp;1"]. |
|
409 createInstance(CI.nsIHttpServer); |
|
410 } |
|
411 try { |
|
412 if (gServer) |
|
413 StartHTTPServer(); |
|
414 } catch (ex) { |
|
415 //gBrowser.loadURI('data:text/plain,' + ex); |
|
416 ++gTestResults.Exception; |
|
417 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + ex + "\n"); |
|
418 DoneTests(); |
|
419 } |
|
420 |
|
421 // Focus the content browser. |
|
422 if (gFocusFilterMode != FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS) { |
|
423 gBrowser.focus(); |
|
424 } |
|
425 |
|
426 StartTests(); |
|
427 } |
|
428 |
|
429 function StartHTTPServer() |
|
430 { |
|
431 gServer.registerContentType("sjs", "sjs"); |
|
432 gServer.start(-1); |
|
433 gHttpServerPort = gServer.identity.primaryPort; |
|
434 } |
|
435 |
|
436 // Perform a Fisher-Yates shuffle of the array. |
|
437 function Shuffle(array) |
|
438 { |
|
439 for (var i = array.length - 1; i > 0; i--) { |
|
440 var j = Math.floor(Math.random() * (i + 1)); |
|
441 var temp = array[i]; |
|
442 array[i] = array[j]; |
|
443 array[j] = temp; |
|
444 } |
|
445 } |
|
446 |
|
447 function StartTests() |
|
448 { |
|
449 var uri; |
|
450 #if BOOTSTRAP |
|
451 /* These prefs are optional, so we don't need to spit an error to the log */ |
|
452 try { |
|
453 var prefs = Components.classes["@mozilla.org/preferences-service;1"]. |
|
454 getService(Components.interfaces.nsIPrefBranch); |
|
455 } catch(e) { |
|
456 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + e + "\n"); |
|
457 } |
|
458 |
|
459 try { |
|
460 gNoCanvasCache = prefs.getIntPref("reftest.nocache"); |
|
461 } catch(e) { |
|
462 gNoCanvasCache = false; |
|
463 } |
|
464 |
|
465 try { |
|
466 gShuffle = prefs.getBoolPref("reftest.shuffle"); |
|
467 } catch (e) { |
|
468 gShuffle = false; |
|
469 } |
|
470 |
|
471 try { |
|
472 gRunSlowTests = prefs.getIntPref("reftest.skipslowtests"); |
|
473 } catch(e) { |
|
474 gRunSlowTests = false; |
|
475 } |
|
476 |
|
477 try { |
|
478 uri = prefs.getCharPref("reftest.uri"); |
|
479 } catch(e) { |
|
480 uri = ""; |
|
481 } |
|
482 |
|
483 if (uri == "") { |
|
484 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | Unable to find reftest.uri pref. Please ensure your profile is setup properly\n"); |
|
485 DoneTests(); |
|
486 } |
|
487 #else |
|
488 try { |
|
489 // Need to read the manifest once we have gHttpServerPort.. |
|
490 var args = window.arguments[0].wrappedJSObject; |
|
491 |
|
492 if ("nocache" in args && args["nocache"]) |
|
493 gNoCanvasCache = true; |
|
494 |
|
495 if ("skipslowtests" in args && args.skipslowtests) |
|
496 gRunSlowTests = false; |
|
497 |
|
498 uri = args.uri; |
|
499 } catch (e) { |
|
500 ++gTestResults.Exception; |
|
501 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + ex + "\n"); |
|
502 DoneTests(); |
|
503 } |
|
504 #endif |
|
505 |
|
506 if (gShuffle) { |
|
507 gNoCanvasCache = true; |
|
508 } |
|
509 |
|
510 try { |
|
511 ReadTopManifest(uri); |
|
512 BuildUseCounts(); |
|
513 |
|
514 // Filter tests which will be skipped to get a more even distribution when chunking |
|
515 // tURLs is a temporary array containing all active tests |
|
516 var tURLs = new Array(); |
|
517 for (var i = 0; i < gURLs.length; ++i) { |
|
518 if (gURLs[i].expected == EXPECTED_DEATH) |
|
519 continue; |
|
520 |
|
521 if (gURLs[i].needsFocus && !Focus()) |
|
522 continue; |
|
523 |
|
524 if (gURLs[i].slow && !gRunSlowTests) |
|
525 continue; |
|
526 |
|
527 tURLs.push(gURLs[i]); |
|
528 } |
|
529 |
|
530 gDumpLog("REFTEST INFO | Discovered " + gURLs.length + " tests, after filtering SKIP tests, we have " + tURLs.length + "\n"); |
|
531 |
|
532 if (gTotalChunks > 0 && gThisChunk > 0) { |
|
533 // Calculate start and end indices of this chunk if tURLs array were |
|
534 // divided evenly |
|
535 var testsPerChunk = tURLs.length / gTotalChunks; |
|
536 var start = Math.round((gThisChunk-1) * testsPerChunk); |
|
537 var end = Math.round(gThisChunk * testsPerChunk); |
|
538 |
|
539 // Map these indices onto the gURLs array. This avoids modifying the |
|
540 // gURLs array which prevents skipped tests from showing up in the log |
|
541 start = gThisChunk == 1 ? 0 : gURLs.indexOf(tURLs[start]); |
|
542 end = gThisChunk == gTotalChunks ? gURLs.length : gURLs.indexOf(tURLs[end + 1]) - 1; |
|
543 gURLs = gURLs.slice(start, end); |
|
544 |
|
545 gDumpLog("REFTEST INFO | Running chunk " + gThisChunk + " out of " + gTotalChunks + " chunks. "); |
|
546 gDumpLog("tests " + (start+1) + "-" + end + "/" + gURLs.length + "\n"); |
|
547 } |
|
548 |
|
549 if (gShuffle) { |
|
550 Shuffle(gURLs); |
|
551 } |
|
552 |
|
553 gTotalTests = gURLs.length; |
|
554 |
|
555 if (!gTotalTests) |
|
556 throw "No tests to run"; |
|
557 |
|
558 gURICanvases = {}; |
|
559 StartCurrentTest(); |
|
560 } catch (ex) { |
|
561 //gBrowser.loadURI('data:text/plain,' + ex); |
|
562 ++gTestResults.Exception; |
|
563 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + ex + "\n"); |
|
564 DoneTests(); |
|
565 } |
|
566 } |
|
567 |
|
568 function OnRefTestUnload() |
|
569 { |
|
570 let plugin1 = getTestPlugin("Test Plug-in"); |
|
571 let plugin2 = getTestPlugin("Second Test Plug-in"); |
|
572 if (plugin1 && plugin2) { |
|
573 plugin1.enabledState = gTestPluginEnabledStates[0]; |
|
574 plugin2.enabledState = gTestPluginEnabledStates[1]; |
|
575 } else { |
|
576 LogWarning("Failed to get test plugin tags."); |
|
577 } |
|
578 } |
|
579 |
|
580 // Read all available data from an input stream and return it |
|
581 // as a string. |
|
582 function getStreamContent(inputStream) |
|
583 { |
|
584 var streamBuf = ""; |
|
585 var sis = CC["@mozilla.org/scriptableinputstream;1"]. |
|
586 createInstance(CI.nsIScriptableInputStream); |
|
587 sis.init(inputStream); |
|
588 |
|
589 var available; |
|
590 while ((available = sis.available()) != 0) { |
|
591 streamBuf += sis.read(available); |
|
592 } |
|
593 |
|
594 return streamBuf; |
|
595 } |
|
596 |
|
597 // Build the sandbox for fails-if(), etc., condition evaluation. |
|
598 function BuildConditionSandbox(aURL) { |
|
599 var sandbox = new Components.utils.Sandbox(aURL.spec); |
|
600 var xr = CC[NS_XREAPPINFO_CONTRACTID].getService(CI.nsIXULRuntime); |
|
601 var appInfo = CC[NS_XREAPPINFO_CONTRACTID].getService(CI.nsIXULAppInfo); |
|
602 sandbox.isDebugBuild = gDebug.isDebugBuild; |
|
603 sandbox.xulRuntime = {widgetToolkit: xr.widgetToolkit, OS: xr.OS, __exposedProps__: { widgetToolkit: "r", OS: "r", XPCOMABI: "r", shell: "r" } }; |
|
604 |
|
605 // xr.XPCOMABI throws exception for configurations without full ABI |
|
606 // support (mobile builds on ARM) |
|
607 try { |
|
608 sandbox.xulRuntime.XPCOMABI = xr.XPCOMABI; |
|
609 } catch(e) { |
|
610 sandbox.xulRuntime.XPCOMABI = ""; |
|
611 } |
|
612 |
|
613 var testRect = gBrowser.getBoundingClientRect(); |
|
614 sandbox.smallScreen = false; |
|
615 if (gContainingWindow.innerWidth < 800 || gContainingWindow.innerHeight < 1000) { |
|
616 sandbox.smallScreen = true; |
|
617 } |
|
618 |
|
619 var gfxInfo = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo); |
|
620 try { |
|
621 sandbox.d2d = gfxInfo.D2DEnabled; |
|
622 } catch (e) { |
|
623 sandbox.d2d = false; |
|
624 } |
|
625 var info = gfxInfo.getInfo(); |
|
626 sandbox.azureQuartz = info.AzureCanvasBackend == "quartz"; |
|
627 sandbox.azureSkia = info.AzureCanvasBackend == "skia"; |
|
628 sandbox.azureSkiaGL = info.AzureSkiaAccelerated; // FIXME: assumes GL right now |
|
629 // true if we are using the same Azure backend for rendering canvas and content |
|
630 sandbox.contentSameGfxBackendAsCanvas = info.AzureContentBackend == info.AzureCanvasBackend |
|
631 || (info.AzureContentBackend == "none" && info.AzureCanvasBackend == "cairo"); |
|
632 |
|
633 sandbox.layersGPUAccelerated = |
|
634 gWindowUtils.layerManagerType != "Basic"; |
|
635 sandbox.layersOpenGL = |
|
636 gWindowUtils.layerManagerType == "OpenGL"; |
|
637 sandbox.layersOMTC = |
|
638 gWindowUtils.layerManagerRemote == true; |
|
639 |
|
640 // Shortcuts for widget toolkits. |
|
641 sandbox.B2G = xr.widgetToolkit == "gonk"; |
|
642 sandbox.B2GDT = appInfo.name.toLowerCase() == "b2g" && !sandbox.B2G; |
|
643 sandbox.Android = xr.OS == "Android" && !sandbox.B2G; |
|
644 sandbox.cocoaWidget = xr.widgetToolkit == "cocoa"; |
|
645 sandbox.gtk2Widget = xr.widgetToolkit == "gtk2"; |
|
646 sandbox.qtWidget = xr.widgetToolkit == "qt"; |
|
647 sandbox.winWidget = xr.widgetToolkit == "windows"; |
|
648 |
|
649 if (sandbox.Android) { |
|
650 var sysInfo = CC["@mozilla.org/system-info;1"].getService(CI.nsIPropertyBag2); |
|
651 |
|
652 // This is currently used to distinguish Android 4.0.3 (SDK version 15) |
|
653 // and later from Android 2.x |
|
654 sandbox.AndroidVersion = sysInfo.getPropertyAsInt32("version"); |
|
655 } |
|
656 |
|
657 #if MOZ_ASAN |
|
658 sandbox.AddressSanitizer = true; |
|
659 #else |
|
660 sandbox.AddressSanitizer = false; |
|
661 #endif |
|
662 |
|
663 #if MOZ_WEBRTC |
|
664 sandbox.webrtc = true; |
|
665 #else |
|
666 sandbox.webrtc = false; |
|
667 #endif |
|
668 |
|
669 var hh = CC[NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX + "http"]. |
|
670 getService(CI.nsIHttpProtocolHandler); |
|
671 sandbox.http = { __exposedProps__: {} }; |
|
672 for each (var prop in [ "userAgent", "appName", "appVersion", |
|
673 "vendor", "vendorSub", |
|
674 "product", "productSub", |
|
675 "platform", "oscpu", "language", "misc" ]) { |
|
676 sandbox.http[prop] = hh[prop]; |
|
677 sandbox.http.__exposedProps__[prop] = "r"; |
|
678 } |
|
679 |
|
680 // Set OSX to the Mac OS X version for Mac, and 0 otherwise. |
|
681 var osxmatch = /Mac OS X (\d+.\d+)$/.exec(hh.oscpu); |
|
682 sandbox.OSX = osxmatch ? parseFloat(osxmatch[1]) : 0; |
|
683 |
|
684 // see if we have the test plugin available, |
|
685 // and set a sandox prop accordingly |
|
686 var navigator = gContainingWindow.navigator; |
|
687 var testPlugin = navigator.plugins["Test Plug-in"]; |
|
688 sandbox.haveTestPlugin = !!testPlugin; |
|
689 |
|
690 // Set a flag on sandbox if the windows default theme is active |
|
691 var box = gContainingWindow.document.createElement("box"); |
|
692 box.setAttribute("id", "_box_windowsDefaultTheme"); |
|
693 gContainingWindow.document.documentElement.appendChild(box); |
|
694 sandbox.windowsDefaultTheme = (gContainingWindow.getComputedStyle(box, null).display == "none"); |
|
695 gContainingWindow.document.documentElement.removeChild(box); |
|
696 |
|
697 var prefs = CC["@mozilla.org/preferences-service;1"]. |
|
698 getService(CI.nsIPrefBranch); |
|
699 try { |
|
700 sandbox.nativeThemePref = !prefs.getBoolPref("mozilla.widget.disable-native-theme"); |
|
701 } catch (e) { |
|
702 sandbox.nativeThemePref = true; |
|
703 } |
|
704 |
|
705 sandbox.prefs = { |
|
706 __exposedProps__: { |
|
707 getBoolPref: 'r', |
|
708 getIntPref: 'r', |
|
709 }, |
|
710 _prefs: prefs, |
|
711 getBoolPref: function(p) { return this._prefs.getBoolPref(p); }, |
|
712 getIntPref: function(p) { return this._prefs.getIntPref(p); } |
|
713 } |
|
714 |
|
715 sandbox.testPluginIsOOP = function () { |
|
716 try { |
|
717 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); |
|
718 } catch (ex) {} |
|
719 |
|
720 var prefservice = Components.classes["@mozilla.org/preferences-service;1"] |
|
721 .getService(CI.nsIPrefBranch); |
|
722 |
|
723 var testPluginIsOOP = false; |
|
724 if (navigator.platform.indexOf("Mac") == 0) { |
|
725 var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"] |
|
726 .getService(CI.nsIXULAppInfo) |
|
727 .QueryInterface(CI.nsIXULRuntime); |
|
728 if (xulRuntime.XPCOMABI.match(/x86-/)) { |
|
729 try { |
|
730 testPluginIsOOP = prefservice.getBoolPref("dom.ipc.plugins.enabled.i386.test.plugin"); |
|
731 } catch (e) { |
|
732 testPluginIsOOP = prefservice.getBoolPref("dom.ipc.plugins.enabled.i386"); |
|
733 } |
|
734 } |
|
735 else if (xulRuntime.XPCOMABI.match(/x86_64-/)) { |
|
736 try { |
|
737 testPluginIsOOP = prefservice.getBoolPref("dom.ipc.plugins.enabled.x86_64.test.plugin"); |
|
738 } catch (e) { |
|
739 testPluginIsOOP = prefservice.getBoolPref("dom.ipc.plugins.enabled.x86_64"); |
|
740 } |
|
741 } |
|
742 } |
|
743 else { |
|
744 testPluginIsOOP = prefservice.getBoolPref("dom.ipc.plugins.enabled"); |
|
745 } |
|
746 |
|
747 return testPluginIsOOP; |
|
748 }; |
|
749 |
|
750 // Tests shouldn't care about this except for when they need to |
|
751 // crash the content process |
|
752 sandbox.browserIsRemote = gBrowserIsRemote; |
|
753 |
|
754 // Distinguish the Fennecs: |
|
755 sandbox.xulFennec = sandbox.Android && sandbox.browserIsRemote; |
|
756 sandbox.nativeFennec = sandbox.Android && !sandbox.browserIsRemote; |
|
757 |
|
758 if (!gDumpedConditionSandbox) { |
|
759 dump("REFTEST INFO | Dumping JSON representation of sandbox \n"); |
|
760 dump("REFTEST INFO | " + JSON.stringify(sandbox) + " \n"); |
|
761 gDumpedConditionSandbox = true; |
|
762 } |
|
763 return sandbox; |
|
764 } |
|
765 |
|
766 function AddPrefSettings(aWhere, aPrefName, aPrefValExpression, aSandbox, aTestPrefSettings, aRefPrefSettings) |
|
767 { |
|
768 var prefVal = Components.utils.evalInSandbox("(" + aPrefValExpression + ")", aSandbox); |
|
769 var prefType; |
|
770 var valType = typeof(prefVal); |
|
771 if (valType == "boolean") { |
|
772 prefType = PREF_BOOLEAN; |
|
773 } else if (valType == "string") { |
|
774 prefType = PREF_STRING; |
|
775 } else if (valType == "number" && (parseInt(prefVal) == prefVal)) { |
|
776 prefType = PREF_INTEGER; |
|
777 } else { |
|
778 return false; |
|
779 } |
|
780 var setting = { name: aPrefName, |
|
781 type: prefType, |
|
782 value: prefVal }; |
|
783 if (aWhere != "ref-") { |
|
784 aTestPrefSettings.push(setting); |
|
785 } |
|
786 if (aWhere != "test-") { |
|
787 aRefPrefSettings.push(setting); |
|
788 } |
|
789 return true; |
|
790 } |
|
791 |
|
792 function ReadTopManifest(aFileURL) |
|
793 { |
|
794 gURLs = new Array(); |
|
795 var url = gIOService.newURI(aFileURL, null, null); |
|
796 if (!url) |
|
797 throw "Expected a file or http URL for the manifest."; |
|
798 ReadManifest(url, EXPECTED_PASS); |
|
799 } |
|
800 |
|
801 function AddTestItem(aTest) |
|
802 { |
|
803 if (gURLFilterRegex && !gURLFilterRegex.test(aTest.url1.spec)) |
|
804 return; |
|
805 if (gFocusFilterMode == FOCUS_FILTER_NEEDS_FOCUS_TESTS && |
|
806 !aTest.needsFocus) |
|
807 return; |
|
808 if (gFocusFilterMode == FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS && |
|
809 aTest.needsFocus) |
|
810 return; |
|
811 gURLs.push(aTest); |
|
812 } |
|
813 |
|
814 // Note: If you materially change the reftest manifest parsing, |
|
815 // please keep the parser in print-manifest-dirs.py in sync. |
|
816 function ReadManifest(aURL, inherited_status) |
|
817 { |
|
818 var secMan = CC[NS_SCRIPTSECURITYMANAGER_CONTRACTID] |
|
819 .getService(CI.nsIScriptSecurityManager); |
|
820 |
|
821 var listURL = aURL; |
|
822 var channel = gIOService.newChannelFromURI(aURL); |
|
823 var inputStream = channel.open(); |
|
824 if (channel instanceof Components.interfaces.nsIHttpChannel |
|
825 && channel.responseStatus != 200) { |
|
826 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | HTTP ERROR : " + |
|
827 channel.responseStatus + "\n"); |
|
828 } |
|
829 var streamBuf = getStreamContent(inputStream); |
|
830 inputStream.close(); |
|
831 var lines = streamBuf.split(/\n|\r|\r\n/); |
|
832 |
|
833 // Build the sandbox for fails-if(), etc., condition evaluation. |
|
834 var sandbox = BuildConditionSandbox(aURL); |
|
835 var lineNo = 0; |
|
836 var urlprefix = ""; |
|
837 var defaultTestPrefSettings = [], defaultRefPrefSettings = []; |
|
838 for each (var str in lines) { |
|
839 ++lineNo; |
|
840 if (str.charAt(0) == "#") |
|
841 continue; // entire line was a comment |
|
842 var i = str.search(/\s+#/); |
|
843 if (i >= 0) |
|
844 str = str.substring(0, i); |
|
845 // strip leading and trailing whitespace |
|
846 str = str.replace(/^\s*/, '').replace(/\s*$/, ''); |
|
847 if (!str || str == "") |
|
848 continue; |
|
849 var items = str.split(/\s+/); // split on whitespace |
|
850 |
|
851 if (items[0] == "url-prefix") { |
|
852 if (items.length != 2) |
|
853 throw "url-prefix requires one url in manifest file " + aURL.spec + " line " + lineNo; |
|
854 urlprefix = items[1]; |
|
855 continue; |
|
856 } |
|
857 |
|
858 if (items[0] == "default-preferences") { |
|
859 var m; |
|
860 var item; |
|
861 defaultTestPrefSettings = []; |
|
862 defaultRefPrefSettings = []; |
|
863 items.shift(); |
|
864 while ((item = items.shift())) { |
|
865 if (!(m = item.match(gPrefItemRE))) { |
|
866 throw "Unexpected item in default-preferences list in manifest file " + aURL.spec + " line " + lineNo; |
|
867 } |
|
868 if (!AddPrefSettings(m[1], m[2], m[3], sandbox, defaultTestPrefSettings, defaultRefPrefSettings)) { |
|
869 throw "Error in pref value in manifest file " + aURL.spec + " line " + lineNo; |
|
870 } |
|
871 } |
|
872 continue; |
|
873 } |
|
874 |
|
875 var expected_status = EXPECTED_PASS; |
|
876 var allow_silent_fail = false; |
|
877 var minAsserts = 0; |
|
878 var maxAsserts = 0; |
|
879 var needs_focus = false; |
|
880 var slow = false; |
|
881 var testPrefSettings = defaultTestPrefSettings.concat(); |
|
882 var refPrefSettings = defaultRefPrefSettings.concat(); |
|
883 var fuzzy_max_delta = 2; |
|
884 var fuzzy_max_pixels = 1; |
|
885 |
|
886 while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|test-pref|ref-pref|fuzzy)/)) { |
|
887 var item = items.shift(); |
|
888 var stat; |
|
889 var cond; |
|
890 var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/); |
|
891 if (m) { |
|
892 stat = m[1]; |
|
893 // Note: m[2] contains the parentheses, and we want them. |
|
894 cond = Components.utils.evalInSandbox(m[2], sandbox); |
|
895 } else if (item.match(/^(fails|random|skip)$/)) { |
|
896 stat = item; |
|
897 cond = true; |
|
898 } else if (item == "needs-focus") { |
|
899 needs_focus = true; |
|
900 cond = false; |
|
901 } else if ((m = item.match(/^asserts\((\d+)(-\d+)?\)$/))) { |
|
902 cond = false; |
|
903 minAsserts = Number(m[1]); |
|
904 maxAsserts = (m[2] == undefined) ? minAsserts |
|
905 : Number(m[2].substring(1)); |
|
906 } else if ((m = item.match(/^asserts-if\((.*?),(\d+)(-\d+)?\)$/))) { |
|
907 cond = false; |
|
908 if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox)) { |
|
909 minAsserts = Number(m[2]); |
|
910 maxAsserts = |
|
911 (m[3] == undefined) ? minAsserts |
|
912 : Number(m[3].substring(1)); |
|
913 } |
|
914 } else if (item == "slow") { |
|
915 cond = false; |
|
916 slow = true; |
|
917 } else if ((m = item.match(/^require-or\((.*?)\)$/))) { |
|
918 var args = m[1].split(/,/); |
|
919 if (args.length != 2) { |
|
920 throw "Error 7 in manifest file " + aURL.spec + " line " + lineNo + ": wrong number of args to require-or"; |
|
921 } |
|
922 var [precondition_str, fallback_action] = args; |
|
923 var preconditions = precondition_str.split(/&&/); |
|
924 cond = false; |
|
925 for each (var precondition in preconditions) { |
|
926 if (precondition === "debugMode") { |
|
927 // Currently unimplemented. Requires asynchronous |
|
928 // JSD call + getting an event while no JS is running |
|
929 stat = fallback_action; |
|
930 cond = true; |
|
931 break; |
|
932 } else if (precondition === "true") { |
|
933 // For testing |
|
934 } else { |
|
935 // Unknown precondition. Assume it is unimplemented. |
|
936 stat = fallback_action; |
|
937 cond = true; |
|
938 break; |
|
939 } |
|
940 } |
|
941 } else if ((m = item.match(/^slow-if\((.*?)\)$/))) { |
|
942 cond = false; |
|
943 if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox)) |
|
944 slow = true; |
|
945 } else if (item == "silentfail") { |
|
946 cond = false; |
|
947 allow_silent_fail = true; |
|
948 } else if ((m = item.match(gPrefItemRE))) { |
|
949 cond = false; |
|
950 if (!AddPrefSettings(m[1], m[2], m[3], sandbox, testPrefSettings, refPrefSettings)) { |
|
951 throw "Error in pref value in manifest file " + aURL.spec + " line " + lineNo; |
|
952 } |
|
953 } else if ((m = item.match(/^fuzzy\((\d+),(\d+)\)$/))) { |
|
954 cond = false; |
|
955 expected_status = EXPECTED_FUZZY; |
|
956 fuzzy_max_delta = Number(m[1]); |
|
957 fuzzy_max_pixels = Number(m[2]); |
|
958 } else if ((m = item.match(/^fuzzy-if\((.*?),(\d+),(\d+)\)$/))) { |
|
959 cond = false; |
|
960 if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox)) { |
|
961 expected_status = EXPECTED_FUZZY; |
|
962 fuzzy_max_delta = Number(m[2]); |
|
963 fuzzy_max_pixels = Number(m[3]); |
|
964 } |
|
965 } else { |
|
966 throw "Error 1 in manifest file " + aURL.spec + " line " + lineNo; |
|
967 } |
|
968 |
|
969 if (cond) { |
|
970 if (stat == "fails") { |
|
971 expected_status = EXPECTED_FAIL; |
|
972 } else if (stat == "random") { |
|
973 expected_status = EXPECTED_RANDOM; |
|
974 } else if (stat == "skip") { |
|
975 expected_status = EXPECTED_DEATH; |
|
976 } else if (stat == "silentfail") { |
|
977 allow_silent_fail = true; |
|
978 } |
|
979 } |
|
980 } |
|
981 |
|
982 expected_status = Math.max(expected_status, inherited_status); |
|
983 |
|
984 if (minAsserts > maxAsserts) { |
|
985 throw "Bad range in manifest file " + aURL.spec + " line " + lineNo; |
|
986 } |
|
987 |
|
988 var runHttp = false; |
|
989 var httpDepth; |
|
990 if (items[0] == "HTTP") { |
|
991 runHttp = (aURL.scheme == "file"); // We can't yet run the local HTTP server |
|
992 // for non-local reftests. |
|
993 httpDepth = 0; |
|
994 items.shift(); |
|
995 } else if (items[0].match(/HTTP\(\.\.(\/\.\.)*\)/)) { |
|
996 // Accept HTTP(..), HTTP(../..), HTTP(../../..), etc. |
|
997 runHttp = (aURL.scheme == "file"); // We can't yet run the local HTTP server |
|
998 // for non-local reftests. |
|
999 httpDepth = (items[0].length - 5) / 3; |
|
1000 items.shift(); |
|
1001 } |
|
1002 |
|
1003 // do not prefix the url for include commands or urls specifying |
|
1004 // a protocol |
|
1005 if (urlprefix && items[0] != "include") { |
|
1006 if (items.length > 1 && !items[1].match(gProtocolRE)) { |
|
1007 items[1] = urlprefix + items[1]; |
|
1008 } |
|
1009 if (items.length > 2 && !items[2].match(gProtocolRE)) { |
|
1010 items[2] = urlprefix + items[2]; |
|
1011 } |
|
1012 } |
|
1013 |
|
1014 var principal = secMan.getSimpleCodebasePrincipal(aURL); |
|
1015 |
|
1016 if (items[0] == "include") { |
|
1017 if (items.length != 2 || runHttp) |
|
1018 throw "Error 2 in manifest file " + aURL.spec + " line " + lineNo; |
|
1019 var incURI = gIOService.newURI(items[1], null, listURL); |
|
1020 secMan.checkLoadURIWithPrincipal(principal, incURI, |
|
1021 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT); |
|
1022 ReadManifest(incURI, expected_status); |
|
1023 } else if (items[0] == TYPE_LOAD) { |
|
1024 if (items.length != 2 || |
|
1025 (expected_status != EXPECTED_PASS && |
|
1026 expected_status != EXPECTED_DEATH)) |
|
1027 throw "Error 3 in manifest file " + aURL.spec + " line " + lineNo; |
|
1028 var [testURI] = runHttp |
|
1029 ? ServeFiles(principal, httpDepth, |
|
1030 listURL, [items[1]]) |
|
1031 : [gIOService.newURI(items[1], null, listURL)]; |
|
1032 var prettyPath = runHttp |
|
1033 ? gIOService.newURI(items[1], null, listURL).spec |
|
1034 : testURI.spec; |
|
1035 secMan.checkLoadURIWithPrincipal(principal, testURI, |
|
1036 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT); |
|
1037 AddTestItem({ type: TYPE_LOAD, |
|
1038 expected: expected_status, |
|
1039 allowSilentFail: allow_silent_fail, |
|
1040 prettyPath: prettyPath, |
|
1041 minAsserts: minAsserts, |
|
1042 maxAsserts: maxAsserts, |
|
1043 needsFocus: needs_focus, |
|
1044 slow: slow, |
|
1045 prefSettings1: testPrefSettings, |
|
1046 prefSettings2: refPrefSettings, |
|
1047 fuzzyMaxDelta: fuzzy_max_delta, |
|
1048 fuzzyMaxPixels: fuzzy_max_pixels, |
|
1049 url1: testURI, |
|
1050 url2: null }); |
|
1051 } else if (items[0] == TYPE_SCRIPT) { |
|
1052 if (items.length != 2) |
|
1053 throw "Error 4 in manifest file " + aURL.spec + " line " + lineNo; |
|
1054 var [testURI] = runHttp |
|
1055 ? ServeFiles(principal, httpDepth, |
|
1056 listURL, [items[1]]) |
|
1057 : [gIOService.newURI(items[1], null, listURL)]; |
|
1058 var prettyPath = runHttp |
|
1059 ? gIOService.newURI(items[1], null, listURL).spec |
|
1060 : testURI.spec; |
|
1061 secMan.checkLoadURIWithPrincipal(principal, testURI, |
|
1062 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT); |
|
1063 AddTestItem({ type: TYPE_SCRIPT, |
|
1064 expected: expected_status, |
|
1065 allowSilentFail: allow_silent_fail, |
|
1066 prettyPath: prettyPath, |
|
1067 minAsserts: minAsserts, |
|
1068 maxAsserts: maxAsserts, |
|
1069 needsFocus: needs_focus, |
|
1070 slow: slow, |
|
1071 prefSettings1: testPrefSettings, |
|
1072 prefSettings2: refPrefSettings, |
|
1073 fuzzyMaxDelta: fuzzy_max_delta, |
|
1074 fuzzyMaxPixels: fuzzy_max_pixels, |
|
1075 url1: testURI, |
|
1076 url2: null }); |
|
1077 } else if (items[0] == TYPE_REFTEST_EQUAL || items[0] == TYPE_REFTEST_NOTEQUAL) { |
|
1078 if (items.length != 3) |
|
1079 throw "Error 5 in manifest file " + aURL.spec + " line " + lineNo; |
|
1080 var [testURI, refURI] = runHttp |
|
1081 ? ServeFiles(principal, httpDepth, |
|
1082 listURL, [items[1], items[2]]) |
|
1083 : [gIOService.newURI(items[1], null, listURL), |
|
1084 gIOService.newURI(items[2], null, listURL)]; |
|
1085 var prettyPath = runHttp |
|
1086 ? gIOService.newURI(items[1], null, listURL).spec |
|
1087 : testURI.spec; |
|
1088 secMan.checkLoadURIWithPrincipal(principal, testURI, |
|
1089 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT); |
|
1090 secMan.checkLoadURIWithPrincipal(principal, refURI, |
|
1091 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT); |
|
1092 AddTestItem({ type: items[0], |
|
1093 expected: expected_status, |
|
1094 allowSilentFail: allow_silent_fail, |
|
1095 prettyPath: prettyPath, |
|
1096 minAsserts: minAsserts, |
|
1097 maxAsserts: maxAsserts, |
|
1098 needsFocus: needs_focus, |
|
1099 slow: slow, |
|
1100 prefSettings1: testPrefSettings, |
|
1101 prefSettings2: refPrefSettings, |
|
1102 fuzzyMaxDelta: fuzzy_max_delta, |
|
1103 fuzzyMaxPixels: fuzzy_max_pixels, |
|
1104 url1: testURI, |
|
1105 url2: refURI }); |
|
1106 } else { |
|
1107 throw "Error 6 in manifest file " + aURL.spec + " line " + lineNo; |
|
1108 } |
|
1109 } |
|
1110 } |
|
1111 |
|
1112 function AddURIUseCount(uri) |
|
1113 { |
|
1114 if (uri == null) |
|
1115 return; |
|
1116 |
|
1117 var spec = uri.spec; |
|
1118 if (spec in gURIUseCounts) { |
|
1119 gURIUseCounts[spec]++; |
|
1120 } else { |
|
1121 gURIUseCounts[spec] = 1; |
|
1122 } |
|
1123 } |
|
1124 |
|
1125 function BuildUseCounts() |
|
1126 { |
|
1127 gURIUseCounts = {}; |
|
1128 for (var i = 0; i < gURLs.length; ++i) { |
|
1129 var url = gURLs[i]; |
|
1130 if (url.expected != EXPECTED_DEATH && |
|
1131 (url.type == TYPE_REFTEST_EQUAL || |
|
1132 url.type == TYPE_REFTEST_NOTEQUAL)) { |
|
1133 if (url.prefSettings1.length == 0) { |
|
1134 AddURIUseCount(gURLs[i].url1); |
|
1135 } |
|
1136 if (url.prefSettings2.length == 0) { |
|
1137 AddURIUseCount(gURLs[i].url2); |
|
1138 } |
|
1139 } |
|
1140 } |
|
1141 } |
|
1142 |
|
1143 function ServeFiles(manifestPrincipal, depth, aURL, files) |
|
1144 { |
|
1145 var listURL = aURL.QueryInterface(CI.nsIFileURL); |
|
1146 var directory = listURL.file.parent; |
|
1147 |
|
1148 // Allow serving a tree that's an ancestor of the directory containing |
|
1149 // the files so that they can use resources in ../ (etc.). |
|
1150 var dirPath = "/"; |
|
1151 while (depth > 0) { |
|
1152 dirPath = "/" + directory.leafName + dirPath; |
|
1153 directory = directory.parent; |
|
1154 --depth; |
|
1155 } |
|
1156 |
|
1157 gCount++; |
|
1158 var path = "/" + Date.now() + "/" + gCount; |
|
1159 gServer.registerDirectory(path + "/", directory); |
|
1160 |
|
1161 var secMan = CC[NS_SCRIPTSECURITYMANAGER_CONTRACTID] |
|
1162 .getService(CI.nsIScriptSecurityManager); |
|
1163 |
|
1164 var testbase = gIOService.newURI("http://localhost:" + gHttpServerPort + |
|
1165 path + dirPath, null, null); |
|
1166 |
|
1167 function FileToURI(file) |
|
1168 { |
|
1169 // Only serve relative URIs via the HTTP server, not absolute |
|
1170 // ones like about:blank. |
|
1171 var testURI = gIOService.newURI(file, null, testbase); |
|
1172 |
|
1173 // XXX necessary? manifestURL guaranteed to be file, others always HTTP |
|
1174 secMan.checkLoadURIWithPrincipal(manifestPrincipal, testURI, |
|
1175 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT); |
|
1176 |
|
1177 return testURI; |
|
1178 } |
|
1179 |
|
1180 return files.map(FileToURI); |
|
1181 } |
|
1182 |
|
1183 // Return true iff this window is focused when this function returns. |
|
1184 function Focus() |
|
1185 { |
|
1186 var fm = CC["@mozilla.org/focus-manager;1"].getService(CI.nsIFocusManager); |
|
1187 fm.focusedWindow = gContainingWindow; |
|
1188 #ifdef XP_MACOSX |
|
1189 try { |
|
1190 var dock = CC["@mozilla.org/widget/macdocksupport;1"].getService(CI.nsIMacDockSupport); |
|
1191 dock.activateApplication(true); |
|
1192 } catch(ex) { |
|
1193 } |
|
1194 #endif // XP_MACOSX |
|
1195 return true; |
|
1196 } |
|
1197 |
|
1198 function Blur() |
|
1199 { |
|
1200 // On non-remote reftests, this will transfer focus to the dummy window |
|
1201 // we created to hold focus for non-needs-focus tests. Buggy tests |
|
1202 // (ones which require focus but don't request needs-focus) will then |
|
1203 // fail. |
|
1204 gContainingWindow.blur(); |
|
1205 } |
|
1206 |
|
1207 function StartCurrentTest() |
|
1208 { |
|
1209 gTestLog = []; |
|
1210 |
|
1211 // make sure we don't run tests that are expected to kill the browser |
|
1212 while (gURLs.length > 0) { |
|
1213 var test = gURLs[0]; |
|
1214 if (test.expected == EXPECTED_DEATH) { |
|
1215 ++gTestResults.Skip; |
|
1216 gDumpLog("REFTEST TEST-KNOWN-FAIL | " + test.url1.spec + " | (SKIP)\n"); |
|
1217 gURLs.shift(); |
|
1218 } else if (test.needsFocus && !Focus()) { |
|
1219 // FIXME: Marking this as a known fail is dangerous! What |
|
1220 // if it starts failing all the time? |
|
1221 ++gTestResults.Skip; |
|
1222 gDumpLog("REFTEST TEST-KNOWN-FAIL | " + test.url1.spec + " | (SKIPPED; COULDN'T GET FOCUS)\n"); |
|
1223 gURLs.shift(); |
|
1224 } else if (test.slow && !gRunSlowTests) { |
|
1225 ++gTestResults.Slow; |
|
1226 gDumpLog("REFTEST TEST-KNOWN-SLOW | " + test.url1.spec + " | (SLOW)\n"); |
|
1227 gURLs.shift(); |
|
1228 } else { |
|
1229 break; |
|
1230 } |
|
1231 } |
|
1232 |
|
1233 if (gURLs.length == 0) { |
|
1234 RestoreChangedPreferences(); |
|
1235 DoneTests(); |
|
1236 } |
|
1237 else { |
|
1238 gDumpLog("REFTEST TEST-START | " + gURLs[0].prettyPath + "\n"); |
|
1239 if (!gURLs[0].needsFocus) { |
|
1240 Blur(); |
|
1241 } |
|
1242 var currentTest = gTotalTests - gURLs.length; |
|
1243 gContainingWindow.document.title = "reftest: " + currentTest + " / " + gTotalTests + |
|
1244 " (" + Math.floor(100 * (currentTest / gTotalTests)) + "%)"; |
|
1245 StartCurrentURI(1); |
|
1246 } |
|
1247 } |
|
1248 |
|
1249 function StartCurrentURI(aState) |
|
1250 { |
|
1251 gState = aState; |
|
1252 gCurrentURL = gURLs[0]["url" + aState].spec; |
|
1253 |
|
1254 RestoreChangedPreferences(); |
|
1255 |
|
1256 var prefSettings = gURLs[0]["prefSettings" + aState]; |
|
1257 if (prefSettings.length > 0) { |
|
1258 var prefs = Components.classes["@mozilla.org/preferences-service;1"]. |
|
1259 getService(Components.interfaces.nsIPrefBranch); |
|
1260 var badPref = undefined; |
|
1261 try { |
|
1262 prefSettings.forEach(function(ps) { |
|
1263 var oldVal; |
|
1264 if (ps.type == PREF_BOOLEAN) { |
|
1265 try { |
|
1266 oldVal = prefs.getBoolPref(ps.name); |
|
1267 } catch (e) { |
|
1268 badPref = "boolean preference '" + ps.name + "'"; |
|
1269 throw "bad pref"; |
|
1270 } |
|
1271 } else if (ps.type == PREF_STRING) { |
|
1272 try { |
|
1273 oldVal = prefs.getCharPref(ps.name); |
|
1274 } catch (e) { |
|
1275 badPref = "string preference '" + ps.name + "'"; |
|
1276 throw "bad pref"; |
|
1277 } |
|
1278 } else if (ps.type == PREF_INTEGER) { |
|
1279 try { |
|
1280 oldVal = prefs.getIntPref(ps.name); |
|
1281 } catch (e) { |
|
1282 badPref = "integer preference '" + ps.name + "'"; |
|
1283 throw "bad pref"; |
|
1284 } |
|
1285 } else { |
|
1286 throw "internal error - unknown preference type"; |
|
1287 } |
|
1288 if (oldVal != ps.value) { |
|
1289 gPrefsToRestore.push( { name: ps.name, |
|
1290 type: ps.type, |
|
1291 value: oldVal } ); |
|
1292 var value = ps.value; |
|
1293 if (ps.type == PREF_BOOLEAN) { |
|
1294 prefs.setBoolPref(ps.name, value); |
|
1295 } else if (ps.type == PREF_STRING) { |
|
1296 prefs.setCharPref(ps.name, value); |
|
1297 value = '"' + value + '"'; |
|
1298 } else if (ps.type == PREF_INTEGER) { |
|
1299 prefs.setIntPref(ps.name, value); |
|
1300 } |
|
1301 gDumpLog("SET PREFERENCE pref(" + ps.name + "," + value + ")\n"); |
|
1302 } |
|
1303 }); |
|
1304 } catch (e) { |
|
1305 if (e == "bad pref") { |
|
1306 var test = gURLs[0]; |
|
1307 if (test.expected == EXPECTED_FAIL) { |
|
1308 gDumpLog("REFTEST TEST-KNOWN-FAIL | " + test.url1.spec + |
|
1309 " | (SKIPPED; " + badPref + " not known or wrong type)\n"); |
|
1310 ++gTestResults.Skip; |
|
1311 } else { |
|
1312 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + test.url1.spec + |
|
1313 " | " + badPref + " not known or wrong type\n"); |
|
1314 ++gTestResults.UnexpectedFail; |
|
1315 } |
|
1316 } else { |
|
1317 throw e; |
|
1318 } |
|
1319 } |
|
1320 if (badPref != undefined) { |
|
1321 // skip the test that had a bad preference |
|
1322 gURLs.shift(); |
|
1323 |
|
1324 StartCurrentTest(); |
|
1325 return; |
|
1326 } |
|
1327 } |
|
1328 |
|
1329 if (prefSettings.length == 0 && |
|
1330 gURICanvases[gCurrentURL] && |
|
1331 (gURLs[0].type == TYPE_REFTEST_EQUAL || |
|
1332 gURLs[0].type == TYPE_REFTEST_NOTEQUAL) && |
|
1333 gURLs[0].maxAsserts == 0) { |
|
1334 // Pretend the document loaded --- RecordResult will notice |
|
1335 // there's already a canvas for this URL |
|
1336 gContainingWindow.setTimeout(RecordResult, 0); |
|
1337 } else { |
|
1338 var currentTest = gTotalTests - gURLs.length; |
|
1339 gDumpLog("REFTEST TEST-LOAD | " + gCurrentURL + " | " + currentTest + " / " + gTotalTests + |
|
1340 " (" + Math.floor(100 * (currentTest / gTotalTests)) + "%)\n"); |
|
1341 LogInfo("START " + gCurrentURL); |
|
1342 var type = gURLs[0].type |
|
1343 if (TYPE_SCRIPT == type) { |
|
1344 SendLoadScriptTest(gCurrentURL, gLoadTimeout); |
|
1345 } else { |
|
1346 SendLoadTest(type, gCurrentURL, gLoadTimeout); |
|
1347 } |
|
1348 } |
|
1349 } |
|
1350 |
|
1351 function DoneTests() |
|
1352 { |
|
1353 gDumpLog("REFTEST FINISHED: Slowest test took " + gSlowestTestTime + |
|
1354 "ms (" + gSlowestTestURL + ")\n"); |
|
1355 |
|
1356 gDumpLog("REFTEST INFO | Result summary:\n"); |
|
1357 var count = gTestResults.Pass + gTestResults.LoadOnly; |
|
1358 gDumpLog("REFTEST INFO | Successful: " + count + " (" + |
|
1359 gTestResults.Pass + " pass, " + |
|
1360 gTestResults.LoadOnly + " load only)\n"); |
|
1361 count = gTestResults.Exception + gTestResults.FailedLoad + |
|
1362 gTestResults.UnexpectedFail + gTestResults.UnexpectedPass + |
|
1363 gTestResults.AssertionUnexpected + |
|
1364 gTestResults.AssertionUnexpectedFixed; |
|
1365 gDumpLog("REFTEST INFO | Unexpected: " + count + " (" + |
|
1366 gTestResults.UnexpectedFail + " unexpected fail, " + |
|
1367 gTestResults.UnexpectedPass + " unexpected pass, " + |
|
1368 gTestResults.AssertionUnexpected + " unexpected asserts, " + |
|
1369 gTestResults.AssertionUnexpectedFixed + " unexpected fixed asserts, " + |
|
1370 gTestResults.FailedLoad + " failed load, " + |
|
1371 gTestResults.Exception + " exception)\n"); |
|
1372 count = gTestResults.KnownFail + gTestResults.AssertionKnown + |
|
1373 gTestResults.Random + gTestResults.Skip + gTestResults.Slow; |
|
1374 gDumpLog("REFTEST INFO | Known problems: " + count + " (" + |
|
1375 gTestResults.KnownFail + " known fail, " + |
|
1376 gTestResults.AssertionKnown + " known asserts, " + |
|
1377 gTestResults.Random + " random, " + |
|
1378 gTestResults.Skip + " skipped, " + |
|
1379 gTestResults.Slow + " slow)\n"); |
|
1380 |
|
1381 gDumpLog("REFTEST INFO | Total canvas count = " + gRecycledCanvases.length + "\n"); |
|
1382 |
|
1383 gDumpLog("REFTEST TEST-START | Shutdown\n"); |
|
1384 function onStopped() { |
|
1385 let appStartup = CC["@mozilla.org/toolkit/app-startup;1"].getService(CI.nsIAppStartup); |
|
1386 appStartup.quit(CI.nsIAppStartup.eForceQuit); |
|
1387 } |
|
1388 if (gServer) { |
|
1389 gServer.stop(onStopped); |
|
1390 } |
|
1391 else { |
|
1392 onStopped(); |
|
1393 } |
|
1394 } |
|
1395 |
|
1396 function UpdateCanvasCache(url, canvas) |
|
1397 { |
|
1398 var spec = url.spec; |
|
1399 |
|
1400 --gURIUseCounts[spec]; |
|
1401 |
|
1402 if (gNoCanvasCache || gURIUseCounts[spec] == 0) { |
|
1403 ReleaseCanvas(canvas); |
|
1404 delete gURICanvases[spec]; |
|
1405 } else if (gURIUseCounts[spec] > 0) { |
|
1406 gURICanvases[spec] = canvas; |
|
1407 } else { |
|
1408 throw "Use counts were computed incorrectly"; |
|
1409 } |
|
1410 } |
|
1411 |
|
1412 // Recompute drawWindow flags for every drawWindow operation. |
|
1413 // We have to do this every time since our window can be |
|
1414 // asynchronously resized (e.g. by the window manager, to make |
|
1415 // it fit on screen) at unpredictable times. |
|
1416 // Fortunately this is pretty cheap. |
|
1417 function DoDrawWindow(ctx, x, y, w, h) |
|
1418 { |
|
1419 var flags = ctx.DRAWWINDOW_DRAW_CARET | ctx.DRAWWINDOW_DRAW_VIEW; |
|
1420 var testRect = gBrowser.getBoundingClientRect(); |
|
1421 if (gIgnoreWindowSize || |
|
1422 (0 <= testRect.left && |
|
1423 0 <= testRect.top && |
|
1424 gContainingWindow.innerWidth >= testRect.right && |
|
1425 gContainingWindow.innerHeight >= testRect.bottom)) { |
|
1426 // We can use the window's retained layer manager |
|
1427 // because the window is big enough to display the entire |
|
1428 // browser element |
|
1429 flags |= ctx.DRAWWINDOW_USE_WIDGET_LAYERS; |
|
1430 } else if (gBrowserIsRemote) { |
|
1431 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + " | can't drawWindow remote content\n"); |
|
1432 ++gTestResults.Exception; |
|
1433 } |
|
1434 |
|
1435 if (gDrawWindowFlags != flags) { |
|
1436 // Every time the flags change, dump the new state. |
|
1437 gDrawWindowFlags = flags; |
|
1438 var flagsStr = "DRAWWINDOW_DRAW_CARET | DRAWWINDOW_DRAW_VIEW"; |
|
1439 if (flags & ctx.DRAWWINDOW_USE_WIDGET_LAYERS) { |
|
1440 flagsStr += " | DRAWWINDOW_USE_WIDGET_LAYERS"; |
|
1441 } else { |
|
1442 // Output a special warning because we need to be able to detect |
|
1443 // this whenever it happens. |
|
1444 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | WARNING: USE_WIDGET_LAYERS disabled\n"); |
|
1445 } |
|
1446 gDumpLog("REFTEST INFO | drawWindow flags = " + flagsStr + |
|
1447 "; window size = " + gContainingWindow.innerWidth + "," + gContainingWindow.innerHeight + |
|
1448 "; test browser size = " + testRect.width + "," + testRect.height + |
|
1449 "\n"); |
|
1450 } |
|
1451 |
|
1452 LogInfo("DoDrawWindow " + x + "," + y + "," + w + "," + h); |
|
1453 ctx.drawWindow(gContainingWindow, x, y, w, h, "rgb(255,255,255)", |
|
1454 gDrawWindowFlags); |
|
1455 } |
|
1456 |
|
1457 function InitCurrentCanvasWithSnapshot() |
|
1458 { |
|
1459 LogInfo("Initializing canvas snapshot"); |
|
1460 |
|
1461 if (gURLs[0].type == TYPE_LOAD || gURLs[0].type == TYPE_SCRIPT) { |
|
1462 // We don't want to snapshot this kind of test |
|
1463 return false; |
|
1464 } |
|
1465 |
|
1466 if (!gCurrentCanvas) { |
|
1467 gCurrentCanvas = AllocateCanvas(); |
|
1468 } |
|
1469 |
|
1470 var ctx = gCurrentCanvas.getContext("2d"); |
|
1471 DoDrawWindow(ctx, 0, 0, gCurrentCanvas.width, gCurrentCanvas.height); |
|
1472 return true; |
|
1473 } |
|
1474 |
|
1475 function UpdateCurrentCanvasForInvalidation(rects) |
|
1476 { |
|
1477 LogInfo("Updating canvas for invalidation"); |
|
1478 |
|
1479 if (!gCurrentCanvas) { |
|
1480 return; |
|
1481 } |
|
1482 |
|
1483 var ctx = gCurrentCanvas.getContext("2d"); |
|
1484 for (var i = 0; i < rects.length; ++i) { |
|
1485 var r = rects[i]; |
|
1486 // Set left/top/right/bottom to pixel boundaries |
|
1487 var left = Math.floor(r.left); |
|
1488 var top = Math.floor(r.top); |
|
1489 var right = Math.ceil(r.right); |
|
1490 var bottom = Math.ceil(r.bottom); |
|
1491 |
|
1492 ctx.save(); |
|
1493 ctx.translate(left, top); |
|
1494 DoDrawWindow(ctx, left, top, right - left, bottom - top); |
|
1495 ctx.restore(); |
|
1496 } |
|
1497 } |
|
1498 |
|
1499 function UpdateWholeCurrentCanvasForInvalidation() |
|
1500 { |
|
1501 LogInfo("Updating entire canvas for invalidation"); |
|
1502 |
|
1503 if (!gCurrentCanvas) { |
|
1504 return; |
|
1505 } |
|
1506 |
|
1507 var ctx = gCurrentCanvas.getContext("2d"); |
|
1508 DoDrawWindow(ctx, 0, 0, gCurrentCanvas.width, gCurrentCanvas.height); |
|
1509 } |
|
1510 |
|
1511 function RecordResult(testRunTime, errorMsg, scriptResults) |
|
1512 { |
|
1513 LogInfo("RecordResult fired"); |
|
1514 |
|
1515 // Keep track of which test was slowest, and how long it took. |
|
1516 if (testRunTime > gSlowestTestTime) { |
|
1517 gSlowestTestTime = testRunTime; |
|
1518 gSlowestTestURL = gCurrentURL; |
|
1519 } |
|
1520 |
|
1521 // Not 'const ...' because of 'EXPECTED_*' value dependency. |
|
1522 var outputs = {}; |
|
1523 const randomMsg = "(EXPECTED RANDOM)"; |
|
1524 outputs[EXPECTED_PASS] = { |
|
1525 true: {s: "TEST-PASS" , n: "Pass"}, |
|
1526 false: {s: "TEST-UNEXPECTED-FAIL" , n: "UnexpectedFail"} |
|
1527 }; |
|
1528 outputs[EXPECTED_FAIL] = { |
|
1529 true: {s: "TEST-UNEXPECTED-PASS" , n: "UnexpectedPass"}, |
|
1530 false: {s: "TEST-KNOWN-FAIL" , n: "KnownFail"} |
|
1531 }; |
|
1532 outputs[EXPECTED_RANDOM] = { |
|
1533 true: {s: "TEST-PASS" + randomMsg , n: "Random"}, |
|
1534 false: {s: "TEST-KNOWN-FAIL" + randomMsg, n: "Random"} |
|
1535 }; |
|
1536 outputs[EXPECTED_FUZZY] = outputs[EXPECTED_PASS]; |
|
1537 |
|
1538 var output; |
|
1539 |
|
1540 if (gURLs[0].type == TYPE_LOAD) { |
|
1541 ++gTestResults.LoadOnly; |
|
1542 gDumpLog("REFTEST TEST-PASS | " + gURLs[0].prettyPath + " | (LOAD ONLY)\n"); |
|
1543 gCurrentCanvas = null; |
|
1544 FinishTestItem(); |
|
1545 return; |
|
1546 } |
|
1547 if (gURLs[0].type == TYPE_SCRIPT) { |
|
1548 var expected = gURLs[0].expected; |
|
1549 |
|
1550 if (errorMsg) { |
|
1551 // Force an unexpected failure to alert the test author to fix the test. |
|
1552 expected = EXPECTED_PASS; |
|
1553 } else if (scriptResults.length == 0) { |
|
1554 // This failure may be due to a JavaScript Engine bug causing |
|
1555 // early termination of the test. If we do not allow silent |
|
1556 // failure, report an error. |
|
1557 if (!gURLs[0].allowSilentFail) |
|
1558 errorMsg = "No test results reported. (SCRIPT)\n"; |
|
1559 else |
|
1560 gDumpLog("REFTEST INFO | An expected silent failure occurred \n"); |
|
1561 } |
|
1562 |
|
1563 if (errorMsg) { |
|
1564 output = outputs[expected][false]; |
|
1565 ++gTestResults[output.n]; |
|
1566 var result = "REFTEST " + output.s + " | " + |
|
1567 gURLs[0].prettyPath + " | " + // the URL being tested |
|
1568 errorMsg; |
|
1569 |
|
1570 gDumpLog(result); |
|
1571 FinishTestItem(); |
|
1572 return; |
|
1573 } |
|
1574 |
|
1575 var anyFailed = scriptResults.some(function(result) { return !result.passed; }); |
|
1576 var outputPair; |
|
1577 if (anyFailed && expected == EXPECTED_FAIL) { |
|
1578 // If we're marked as expected to fail, and some (but not all) tests |
|
1579 // passed, treat those tests as though they were marked random |
|
1580 // (since we can't tell whether they were really intended to be |
|
1581 // marked failing or not). |
|
1582 outputPair = { true: outputs[EXPECTED_RANDOM][true], |
|
1583 false: outputs[expected][false] }; |
|
1584 } else { |
|
1585 outputPair = outputs[expected]; |
|
1586 } |
|
1587 var index = 0; |
|
1588 scriptResults.forEach(function(result) { |
|
1589 var output = outputPair[result.passed]; |
|
1590 |
|
1591 ++gTestResults[output.n]; |
|
1592 result = "REFTEST " + output.s + " | " + |
|
1593 gURLs[0].prettyPath + " | " + // the URL being tested |
|
1594 result.description + " item " + (++index) + "\n"; |
|
1595 gDumpLog(result); |
|
1596 }); |
|
1597 |
|
1598 if (anyFailed && expected == EXPECTED_PASS) { |
|
1599 FlushTestLog(); |
|
1600 } |
|
1601 |
|
1602 FinishTestItem(); |
|
1603 return; |
|
1604 } |
|
1605 |
|
1606 if (gURLs[0]["prefSettings" + gState].length == 0 && |
|
1607 gURICanvases[gCurrentURL]) { |
|
1608 gCurrentCanvas = gURICanvases[gCurrentURL]; |
|
1609 } |
|
1610 if (gCurrentCanvas == null) { |
|
1611 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + " | program error managing snapshots\n"); |
|
1612 ++gTestResults.Exception; |
|
1613 } |
|
1614 if (gState == 1) { |
|
1615 gCanvas1 = gCurrentCanvas; |
|
1616 } else { |
|
1617 gCanvas2 = gCurrentCanvas; |
|
1618 } |
|
1619 gCurrentCanvas = null; |
|
1620 |
|
1621 ResetRenderingState(); |
|
1622 |
|
1623 switch (gState) { |
|
1624 case 1: |
|
1625 // First document has been loaded. |
|
1626 // Proceed to load the second document. |
|
1627 |
|
1628 CleanUpCrashDumpFiles(); |
|
1629 StartCurrentURI(2); |
|
1630 break; |
|
1631 case 2: |
|
1632 // Both documents have been loaded. Compare the renderings and see |
|
1633 // if the comparison result matches the expected result specified |
|
1634 // in the manifest. |
|
1635 |
|
1636 // number of different pixels |
|
1637 var differences; |
|
1638 // whether the two renderings match: |
|
1639 var equal; |
|
1640 var maxDifference = {}; |
|
1641 |
|
1642 differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, maxDifference); |
|
1643 equal = (differences == 0); |
|
1644 |
|
1645 // what is expected on this platform (PASS, FAIL, or RANDOM) |
|
1646 var expected = gURLs[0].expected; |
|
1647 |
|
1648 if (maxDifference.value > 0 && maxDifference.value <= gURLs[0].fuzzyMaxDelta && |
|
1649 differences <= gURLs[0].fuzzyMaxPixels) { |
|
1650 if (equal) { |
|
1651 throw "Inconsistent result from compareCanvases."; |
|
1652 } |
|
1653 equal = expected == EXPECTED_FUZZY; |
|
1654 gDumpLog("REFTEST fuzzy match\n"); |
|
1655 } |
|
1656 |
|
1657 // whether the comparison result matches what is in the manifest |
|
1658 var test_passed = (equal == (gURLs[0].type == TYPE_REFTEST_EQUAL)) && !gFailedNoPaint; |
|
1659 |
|
1660 output = outputs[expected][test_passed]; |
|
1661 |
|
1662 ++gTestResults[output.n]; |
|
1663 |
|
1664 // It's possible that we failed both reftest-no-paint and the normal comparison, but we don't |
|
1665 // have a way to annotate these separately, so just print an error for the no-paint failure. |
|
1666 if (gFailedNoPaint) { |
|
1667 if (expected == EXPECTED_FAIL) { |
|
1668 gDumpLog("REFTEST TEST-KNOWN-FAIL | " + gURLs[0].prettyPath + " | failed reftest-no-paint\n"); |
|
1669 } else { |
|
1670 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gURLs[0].prettyPath + " | failed reftest-no-paint\n"); |
|
1671 } |
|
1672 } else { |
|
1673 var result = "REFTEST " + output.s + " | " + |
|
1674 gURLs[0].prettyPath + " | "; // the URL being tested |
|
1675 switch (gURLs[0].type) { |
|
1676 case TYPE_REFTEST_NOTEQUAL: |
|
1677 result += "image comparison (!=)"; |
|
1678 break; |
|
1679 case TYPE_REFTEST_EQUAL: |
|
1680 result += "image comparison (==)"; |
|
1681 break; |
|
1682 } |
|
1683 |
|
1684 if (!test_passed && expected == EXPECTED_PASS || |
|
1685 !test_passed && expected == EXPECTED_FUZZY || |
|
1686 test_passed && expected == EXPECTED_FAIL) { |
|
1687 if (!equal) { |
|
1688 result += ", max difference: " + maxDifference.value + ", number of differing pixels: " + differences + "\n"; |
|
1689 result += "REFTEST IMAGE 1 (TEST): " + gCanvas1.toDataURL() + "\n"; |
|
1690 result += "REFTEST IMAGE 2 (REFERENCE): " + gCanvas2.toDataURL() + "\n"; |
|
1691 } else { |
|
1692 result += "\n"; |
|
1693 result += "REFTEST IMAGE: " + gCanvas1.toDataURL() + "\n"; |
|
1694 } |
|
1695 } else { |
|
1696 result += "\n"; |
|
1697 } |
|
1698 |
|
1699 gDumpLog(result); |
|
1700 } |
|
1701 |
|
1702 if (!test_passed && expected == EXPECTED_PASS) { |
|
1703 FlushTestLog(); |
|
1704 } |
|
1705 |
|
1706 if (gURLs[0].prefSettings1.length == 0) { |
|
1707 UpdateCanvasCache(gURLs[0].url1, gCanvas1); |
|
1708 } |
|
1709 if (gURLs[0].prefSettings2.length == 0) { |
|
1710 UpdateCanvasCache(gURLs[0].url2, gCanvas2); |
|
1711 } |
|
1712 |
|
1713 CleanUpCrashDumpFiles(); |
|
1714 FinishTestItem(); |
|
1715 break; |
|
1716 default: |
|
1717 throw "Unexpected state."; |
|
1718 } |
|
1719 } |
|
1720 |
|
1721 function LoadFailed(why) |
|
1722 { |
|
1723 ++gTestResults.FailedLoad; |
|
1724 // Once bug 896840 is fixed, this can go away, but for now it will give log |
|
1725 // output that is TBPL starable for bug 789751 and bug 720452. |
|
1726 if (!why) { |
|
1727 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | load failed with unknown reason\n"); |
|
1728 } |
|
1729 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + |
|
1730 gURLs[0]["url" + gState].spec + " | load failed: " + why + "\n"); |
|
1731 FlushTestLog(); |
|
1732 FinishTestItem(); |
|
1733 } |
|
1734 |
|
1735 function RemoveExpectedCrashDumpFiles() |
|
1736 { |
|
1737 if (gExpectingProcessCrash) { |
|
1738 for each (let crashFilename in gExpectedCrashDumpFiles) { |
|
1739 let file = gCrashDumpDir.clone(); |
|
1740 file.append(crashFilename); |
|
1741 if (file.exists()) { |
|
1742 file.remove(false); |
|
1743 } |
|
1744 } |
|
1745 } |
|
1746 gExpectedCrashDumpFiles.length = 0; |
|
1747 } |
|
1748 |
|
1749 function FindUnexpectedCrashDumpFiles() |
|
1750 { |
|
1751 if (!gCrashDumpDir.exists()) { |
|
1752 return; |
|
1753 } |
|
1754 |
|
1755 let entries = gCrashDumpDir.directoryEntries; |
|
1756 if (!entries) { |
|
1757 return; |
|
1758 } |
|
1759 |
|
1760 let foundCrashDumpFile = false; |
|
1761 while (entries.hasMoreElements()) { |
|
1762 let file = entries.getNext().QueryInterface(CI.nsIFile); |
|
1763 let path = String(file.path); |
|
1764 if (path.match(/\.(dmp|extra)$/) && !gUnexpectedCrashDumpFiles[path]) { |
|
1765 if (!foundCrashDumpFile) { |
|
1766 ++gTestResults.UnexpectedFail; |
|
1767 foundCrashDumpFile = true; |
|
1768 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + |
|
1769 " | This test left crash dumps behind, but we weren't expecting it to!\n"); |
|
1770 } |
|
1771 gDumpLog("REFTEST INFO | Found unexpected crash dump file " + path + |
|
1772 ".\n"); |
|
1773 gUnexpectedCrashDumpFiles[path] = true; |
|
1774 } |
|
1775 } |
|
1776 } |
|
1777 |
|
1778 function CleanUpCrashDumpFiles() |
|
1779 { |
|
1780 RemoveExpectedCrashDumpFiles(); |
|
1781 FindUnexpectedCrashDumpFiles(); |
|
1782 gExpectingProcessCrash = false; |
|
1783 } |
|
1784 |
|
1785 function FinishTestItem() |
|
1786 { |
|
1787 // Replace document with BLANK_URL_FOR_CLEARING in case there are |
|
1788 // assertions when unloading. |
|
1789 gDumpLog("REFTEST INFO | Loading a blank page\n"); |
|
1790 // After clearing, content will notify us of the assertion count |
|
1791 // and tests will continue. |
|
1792 SetAsyncScroll(false); |
|
1793 SendClear(); |
|
1794 gFailedNoPaint = false; |
|
1795 } |
|
1796 |
|
1797 function DoAssertionCheck(numAsserts) |
|
1798 { |
|
1799 if (gDebug.isDebugBuild) { |
|
1800 if (gBrowserIsRemote) { |
|
1801 // Count chrome-process asserts too when content is out of |
|
1802 // process. |
|
1803 var newAssertionCount = gDebug.assertionCount; |
|
1804 var numLocalAsserts = newAssertionCount - gAssertionCount; |
|
1805 gAssertionCount = newAssertionCount; |
|
1806 |
|
1807 numAsserts += numLocalAsserts; |
|
1808 } |
|
1809 |
|
1810 var minAsserts = gURLs[0].minAsserts; |
|
1811 var maxAsserts = gURLs[0].maxAsserts; |
|
1812 |
|
1813 var expectedAssertions = "expected " + minAsserts; |
|
1814 if (minAsserts != maxAsserts) { |
|
1815 expectedAssertions += " to " + maxAsserts; |
|
1816 } |
|
1817 expectedAssertions += " assertions"; |
|
1818 |
|
1819 if (numAsserts < minAsserts) { |
|
1820 ++gTestResults.AssertionUnexpectedFixed; |
|
1821 gDumpLog("REFTEST TEST-UNEXPECTED-PASS | " + gURLs[0].prettyPath + |
|
1822 " | assertion count " + numAsserts + " is less than " + |
|
1823 expectedAssertions + "\n"); |
|
1824 } else if (numAsserts > maxAsserts) { |
|
1825 ++gTestResults.AssertionUnexpected; |
|
1826 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gURLs[0].prettyPath + |
|
1827 " | assertion count " + numAsserts + " is more than " + |
|
1828 expectedAssertions + "\n"); |
|
1829 } else if (numAsserts != 0) { |
|
1830 ++gTestResults.AssertionKnown; |
|
1831 gDumpLog("REFTEST TEST-KNOWN-FAIL | " + gURLs[0].prettyPath + |
|
1832 " | assertion count " + numAsserts + " matches " + |
|
1833 expectedAssertions + "\n"); |
|
1834 } |
|
1835 } |
|
1836 |
|
1837 gDumpLog("REFTEST TEST-END | " + gURLs[0].prettyPath + "\n"); |
|
1838 |
|
1839 // And start the next test. |
|
1840 gURLs.shift(); |
|
1841 StartCurrentTest(); |
|
1842 } |
|
1843 |
|
1844 function ResetRenderingState() |
|
1845 { |
|
1846 SendResetRenderingState(); |
|
1847 // We would want to clear any viewconfig here, if we add support for it |
|
1848 } |
|
1849 |
|
1850 function RestoreChangedPreferences() |
|
1851 { |
|
1852 if (gPrefsToRestore.length > 0) { |
|
1853 var prefs = Components.classes["@mozilla.org/preferences-service;1"]. |
|
1854 getService(Components.interfaces.nsIPrefBranch); |
|
1855 gPrefsToRestore.reverse(); |
|
1856 gPrefsToRestore.forEach(function(ps) { |
|
1857 var value = ps.value; |
|
1858 if (ps.type == PREF_BOOLEAN) { |
|
1859 prefs.setBoolPref(ps.name, value); |
|
1860 } else if (ps.type == PREF_STRING) { |
|
1861 prefs.setCharPref(ps.name, value); |
|
1862 value = '"' + value + '"'; |
|
1863 } else if (ps.type == PREF_INTEGER) { |
|
1864 prefs.setIntPref(ps.name, value); |
|
1865 } |
|
1866 gDumpLog("RESTORE PREFERENCE pref(" + ps.name + "," + value + ")\n"); |
|
1867 }); |
|
1868 gPrefsToRestore = []; |
|
1869 } |
|
1870 } |
|
1871 |
|
1872 function RegisterMessageListenersAndLoadContentScript() |
|
1873 { |
|
1874 gBrowserMessageManager.addMessageListener( |
|
1875 "reftest:AssertionCount", |
|
1876 function (m) { RecvAssertionCount(m.json.count); } |
|
1877 ); |
|
1878 gBrowserMessageManager.addMessageListener( |
|
1879 "reftest:ContentReady", |
|
1880 function (m) { return RecvContentReady() } |
|
1881 ); |
|
1882 gBrowserMessageManager.addMessageListener( |
|
1883 "reftest:Exception", |
|
1884 function (m) { RecvException(m.json.what) } |
|
1885 ); |
|
1886 gBrowserMessageManager.addMessageListener( |
|
1887 "reftest:FailedLoad", |
|
1888 function (m) { RecvFailedLoad(m.json.why); } |
|
1889 ); |
|
1890 gBrowserMessageManager.addMessageListener( |
|
1891 "reftest:FailedNoPaint", |
|
1892 function (m) { RecvFailedNoPaint(); } |
|
1893 ); |
|
1894 gBrowserMessageManager.addMessageListener( |
|
1895 "reftest:InitCanvasWithSnapshot", |
|
1896 function (m) { return RecvInitCanvasWithSnapshot(); } |
|
1897 ); |
|
1898 gBrowserMessageManager.addMessageListener( |
|
1899 "reftest:Log", |
|
1900 function (m) { RecvLog(m.json.type, m.json.msg); } |
|
1901 ); |
|
1902 gBrowserMessageManager.addMessageListener( |
|
1903 "reftest:ScriptResults", |
|
1904 function (m) { RecvScriptResults(m.json.runtimeMs, m.json.error, m.json.results); } |
|
1905 ); |
|
1906 gBrowserMessageManager.addMessageListener( |
|
1907 "reftest:TestDone", |
|
1908 function (m) { RecvTestDone(m.json.runtimeMs); } |
|
1909 ); |
|
1910 gBrowserMessageManager.addMessageListener( |
|
1911 "reftest:UpdateCanvasForInvalidation", |
|
1912 function (m) { RecvUpdateCanvasForInvalidation(m.json.rects); } |
|
1913 ); |
|
1914 gBrowserMessageManager.addMessageListener( |
|
1915 "reftest:UpdateWholeCanvasForInvalidation", |
|
1916 function (m) { RecvUpdateWholeCanvasForInvalidation(); } |
|
1917 ); |
|
1918 gBrowserMessageManager.addMessageListener( |
|
1919 "reftest:ExpectProcessCrash", |
|
1920 function (m) { RecvExpectProcessCrash(); } |
|
1921 ); |
|
1922 gBrowserMessageManager.addMessageListener( |
|
1923 "reftest:EnableAsyncScroll", |
|
1924 function (m) { SetAsyncScroll(true); } |
|
1925 ); |
|
1926 |
|
1927 gBrowserMessageManager.loadFrameScript("chrome://reftest/content/reftest-content.js", true, true); |
|
1928 } |
|
1929 |
|
1930 function SetAsyncScroll(enabled) |
|
1931 { |
|
1932 gBrowser.QueryInterface(CI.nsIFrameLoaderOwner).frameLoader.renderMode = |
|
1933 enabled ? CI.nsIFrameLoader.RENDER_MODE_ASYNC_SCROLL : |
|
1934 CI.nsIFrameLoader.RENDER_MODE_DEFAULT; |
|
1935 } |
|
1936 |
|
1937 function RecvAssertionCount(count) |
|
1938 { |
|
1939 DoAssertionCheck(count); |
|
1940 } |
|
1941 |
|
1942 function RecvContentReady() |
|
1943 { |
|
1944 InitAndStartRefTests(); |
|
1945 return { remote: gBrowserIsRemote }; |
|
1946 } |
|
1947 |
|
1948 function RecvException(what) |
|
1949 { |
|
1950 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + " | " + what + "\n"); |
|
1951 ++gTestResults.Exception; |
|
1952 } |
|
1953 |
|
1954 function RecvFailedLoad(why) |
|
1955 { |
|
1956 LoadFailed(why); |
|
1957 } |
|
1958 |
|
1959 function RecvFailedNoPaint() |
|
1960 { |
|
1961 gFailedNoPaint = true; |
|
1962 } |
|
1963 |
|
1964 function RecvInitCanvasWithSnapshot() |
|
1965 { |
|
1966 var painted = InitCurrentCanvasWithSnapshot(); |
|
1967 return { painted: painted }; |
|
1968 } |
|
1969 |
|
1970 function RecvLog(type, msg) |
|
1971 { |
|
1972 msg = "[CONTENT] "+ msg; |
|
1973 if (type == "info") { |
|
1974 LogInfo(msg); |
|
1975 } else if (type == "warning") { |
|
1976 LogWarning(msg); |
|
1977 } else { |
|
1978 gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + " | unknown log type " + type + "\n"); |
|
1979 ++gTestResults.Exception; |
|
1980 } |
|
1981 } |
|
1982 |
|
1983 function RecvScriptResults(runtimeMs, error, results) |
|
1984 { |
|
1985 RecordResult(runtimeMs, error, results); |
|
1986 } |
|
1987 |
|
1988 function RecvTestDone(runtimeMs) |
|
1989 { |
|
1990 RecordResult(runtimeMs, '', [ ]); |
|
1991 } |
|
1992 |
|
1993 function RecvUpdateCanvasForInvalidation(rects) |
|
1994 { |
|
1995 UpdateCurrentCanvasForInvalidation(rects); |
|
1996 } |
|
1997 |
|
1998 function RecvUpdateWholeCanvasForInvalidation() |
|
1999 { |
|
2000 UpdateWholeCurrentCanvasForInvalidation(); |
|
2001 } |
|
2002 |
|
2003 function OnProcessCrashed(subject, topic, data) |
|
2004 { |
|
2005 var id; |
|
2006 subject = subject.QueryInterface(CI.nsIPropertyBag2); |
|
2007 if (topic == "plugin-crashed") { |
|
2008 id = subject.getPropertyAsAString("pluginDumpID"); |
|
2009 } else if (topic == "ipc:content-shutdown") { |
|
2010 id = subject.getPropertyAsAString("dumpID"); |
|
2011 } |
|
2012 if (id) { |
|
2013 gExpectedCrashDumpFiles.push(id + ".dmp"); |
|
2014 gExpectedCrashDumpFiles.push(id + ".extra"); |
|
2015 } |
|
2016 } |
|
2017 |
|
2018 function RegisterProcessCrashObservers() |
|
2019 { |
|
2020 var os = CC[NS_OBSERVER_SERVICE_CONTRACTID] |
|
2021 .getService(CI.nsIObserverService); |
|
2022 os.addObserver(OnProcessCrashed, "plugin-crashed", false); |
|
2023 os.addObserver(OnProcessCrashed, "ipc:content-shutdown", false); |
|
2024 } |
|
2025 |
|
2026 function RecvExpectProcessCrash() |
|
2027 { |
|
2028 gExpectingProcessCrash = true; |
|
2029 } |
|
2030 |
|
2031 function SendClear() |
|
2032 { |
|
2033 gBrowserMessageManager.sendAsyncMessage("reftest:Clear"); |
|
2034 } |
|
2035 |
|
2036 function SendLoadScriptTest(uri, timeout) |
|
2037 { |
|
2038 gBrowserMessageManager.sendAsyncMessage("reftest:LoadScriptTest", |
|
2039 { uri: uri, timeout: timeout }); |
|
2040 } |
|
2041 |
|
2042 function SendLoadTest(type, uri, timeout) |
|
2043 { |
|
2044 gBrowserMessageManager.sendAsyncMessage("reftest:LoadTest", |
|
2045 { type: type, uri: uri, timeout: timeout } |
|
2046 ); |
|
2047 } |
|
2048 |
|
2049 function SendResetRenderingState() |
|
2050 { |
|
2051 gBrowserMessageManager.sendAsyncMessage("reftest:ResetRenderingState"); |
|
2052 } |