|
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 |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 var W3CTest = { |
|
6 /** |
|
7 * Dictionary mapping a test URL to either the string "all", which means that |
|
8 * all tests in this file are expected to fail, or a dictionary mapping test |
|
9 * names to either the boolean |true|, or the string "debug". The former |
|
10 * means that this test is expected to fail in all builds, and the latter |
|
11 * that it is only expected to fail in debug builds. |
|
12 */ |
|
13 "expectedFailures": {}, |
|
14 |
|
15 /** |
|
16 * If set to true, we will dump the test failures to the console. |
|
17 */ |
|
18 "dumpFailures": false, |
|
19 |
|
20 /** |
|
21 * If dumpFailures is true, this holds a structure like necessary for |
|
22 * expectedFailures, for ease of updating the expectations. |
|
23 */ |
|
24 "failures": {}, |
|
25 |
|
26 /** |
|
27 * List of test results, needed by TestRunner to update the UI. |
|
28 */ |
|
29 "tests": [], |
|
30 |
|
31 /** |
|
32 * Number of unlogged passes, to stop buildbot from truncating the log. |
|
33 * We will print a message every MAX_COLLAPSED_MESSAGES passes. |
|
34 */ |
|
35 "collapsedMessages": 0, |
|
36 "MAX_COLLAPSED_MESSAGES": 100, |
|
37 |
|
38 /** |
|
39 * Reference to the TestRunner object in the parent frame. |
|
40 */ |
|
41 "runner": parent === this ? null : parent.TestRunner || parent.wrappedJSObject.TestRunner, |
|
42 |
|
43 /** |
|
44 * Prefixes for the error logging. Indexed first by int(todo) and second by |
|
45 * int(result). |
|
46 */ |
|
47 "prefixes": [ |
|
48 ["TEST-UNEXPECTED-FAIL", "TEST-PASS"], |
|
49 ["TEST-KNOWN-FAIL", "TEST-UNEXPECTED-PASS"] |
|
50 ], |
|
51 |
|
52 /** |
|
53 * Prefix of the path to parent of the the failures directory. |
|
54 */ |
|
55 "pathprefix": "/tests/dom/imptests/", |
|
56 |
|
57 /** |
|
58 * Returns the URL of the current test, relative to the root W3C tests |
|
59 * directory. Used as a key into the expectedFailures dictionary. |
|
60 */ |
|
61 "getPath": function() { |
|
62 var url = this.getURL(); |
|
63 if (!url.startsWith(this.pathprefix)) { |
|
64 return ""; |
|
65 } |
|
66 return url.substring(this.pathprefix.length); |
|
67 }, |
|
68 |
|
69 /** |
|
70 * Returns the root-relative URL of the current test. |
|
71 */ |
|
72 "getURL": function() { |
|
73 return this.runner ? this.runner.currentTestURL : location.pathname; |
|
74 }, |
|
75 |
|
76 /** |
|
77 * Report the results in the tests array. |
|
78 */ |
|
79 "reportResults": function() { |
|
80 var element = function element(aLocalName) { |
|
81 var xhtmlNS = "http://www.w3.org/1999/xhtml"; |
|
82 return document.createElementNS(xhtmlNS, aLocalName); |
|
83 }; |
|
84 |
|
85 var stylesheet = element("link"); |
|
86 stylesheet.setAttribute("rel", "stylesheet"); |
|
87 stylesheet.setAttribute("href", "/resources/testharness.css"); |
|
88 var heads = document.getElementsByTagName("head"); |
|
89 if (heads.length) { |
|
90 heads[0].appendChild(stylesheet); |
|
91 } |
|
92 |
|
93 var log = document.getElementById("log"); |
|
94 if (!log) { |
|
95 return; |
|
96 } |
|
97 var section = log.appendChild(element("section")); |
|
98 section.id = "summary"; |
|
99 section.appendChild(element("h2")).textContent = "Details"; |
|
100 |
|
101 var table = section.appendChild(element("table")); |
|
102 table.id = "results"; |
|
103 |
|
104 var tr = table.appendChild(element("thead")).appendChild(element("tr")); |
|
105 for (var header of ["Result", "Test Name", "Message"]) { |
|
106 tr.appendChild(element("th")).textContent = header; |
|
107 } |
|
108 var statuses = [ |
|
109 ["Unexpected Fail", "Pass"], |
|
110 ["Known Fail", "Unexpected Pass"] |
|
111 ]; |
|
112 var tbody = table.appendChild(element("tbody")); |
|
113 for (var test of this.tests) { |
|
114 tr = tbody.appendChild(element("tr")); |
|
115 tr.className = (test.result === !test.todo ? "pass" : "fail"); |
|
116 tr.appendChild(element("td")).textContent = |
|
117 statuses[+test.todo][+test.result]; |
|
118 tr.appendChild(element("td")).textContent = test.name; |
|
119 tr.appendChild(element("td")).textContent = test.message; |
|
120 } |
|
121 }, |
|
122 |
|
123 /** |
|
124 * Returns a printable message based on aTest's 'name' and 'message' |
|
125 * properties. |
|
126 */ |
|
127 "formatTestMessage": function(aTest) { |
|
128 return aTest.name + (aTest.message ? ": " + aTest.message : ""); |
|
129 }, |
|
130 |
|
131 /** |
|
132 * Lets the test runner know about a test result. |
|
133 */ |
|
134 "_log": function(test) { |
|
135 var url = this.getURL(); |
|
136 var msg = this.prefixes[+test.todo][+test.result] + " | "; |
|
137 if (url) { |
|
138 msg += url; |
|
139 } |
|
140 msg += " | " + this.formatTestMessage(test); |
|
141 if (this.runner) { |
|
142 this.runner[(test.result === !test.todo) ? "log" : "error"](msg); |
|
143 } else { |
|
144 dump(msg + "\n"); |
|
145 } |
|
146 }, |
|
147 |
|
148 /** |
|
149 * Logs a message about collapsed messages (if any), and resets the counter. |
|
150 */ |
|
151 "_logCollapsedMessages": function() { |
|
152 if (this.collapsedMessages) { |
|
153 this._log({ |
|
154 "name": document.title, |
|
155 "result": true, |
|
156 "todo": false, |
|
157 "message": "Elided " + this.collapsedMessages + " passes or known failures." |
|
158 }); |
|
159 } |
|
160 this.collapsedMessages = 0; |
|
161 }, |
|
162 |
|
163 /** |
|
164 * Maybe logs a result, eliding up to MAX_COLLAPSED_MESSAGES consecutive |
|
165 * passes. |
|
166 */ |
|
167 "_maybeLog": function(test) { |
|
168 var success = (test.result === !test.todo); |
|
169 if (success && ++this.collapsedMessages < this.MAX_COLLAPSED_MESSAGES) { |
|
170 return; |
|
171 } |
|
172 this._logCollapsedMessages(); |
|
173 this._log(test); |
|
174 }, |
|
175 |
|
176 /** |
|
177 * Reports a test result. The argument is an object with the following |
|
178 * properties: |
|
179 * |
|
180 * o message (string): message to be reported |
|
181 * o result (boolean): whether this test failed |
|
182 * o todo (boolean): whether this test is expected to fail |
|
183 */ |
|
184 "report": function(test) { |
|
185 this.tests.push(test); |
|
186 this._maybeLog(test); |
|
187 }, |
|
188 |
|
189 /** |
|
190 * Returns true if this test is expected to fail, and false otherwise. |
|
191 */ |
|
192 "_todo": function(test) { |
|
193 if (this.expectedFailures === "all") { |
|
194 return true; |
|
195 } |
|
196 var value = this.expectedFailures[test.name]; |
|
197 return value === true || (value === "debug" && !!SpecialPowers.isDebugBuild); |
|
198 }, |
|
199 |
|
200 /** |
|
201 * Callback function for testharness.js. Called when one test in a file |
|
202 * finishes. |
|
203 */ |
|
204 "result": function(test) { |
|
205 var url = this.getPath(); |
|
206 this.report({ |
|
207 "name": test.name, |
|
208 "message": test.message || "", |
|
209 "result": test.status === test.PASS, |
|
210 "todo": this._todo(test) |
|
211 }); |
|
212 if (this.dumpFailures && test.status !== test.PASS) { |
|
213 this.failures[test.name] = true; |
|
214 } |
|
215 }, |
|
216 |
|
217 /** |
|
218 * Callback function for testharness.js. Called when the entire test file |
|
219 * finishes. |
|
220 */ |
|
221 "finish": function(tests, status) { |
|
222 var url = this.getPath(); |
|
223 this.report({ |
|
224 "name": "Finished test", |
|
225 "message": "Status: " + status.status, |
|
226 "result": status.status === status.OK, |
|
227 "todo": |
|
228 url in this.expectedFailures && |
|
229 this.expectedFailures[url] === "error" |
|
230 }); |
|
231 |
|
232 this._logCollapsedMessages(); |
|
233 |
|
234 if (this.dumpFailures) { |
|
235 dump("@@@ @@@ Failures\n"); |
|
236 dump(url + "@@@" + JSON.stringify(this.failures) + "\n"); |
|
237 } |
|
238 if (this.runner) { |
|
239 this.runner.testFinished(this.tests.map(function(aTest) { |
|
240 return { |
|
241 "message": this.formatTestMessage(aTest), |
|
242 "result": aTest.result, |
|
243 "todo": aTest.todo |
|
244 }; |
|
245 }, this)); |
|
246 } else { |
|
247 this.reportResults(); |
|
248 } |
|
249 }, |
|
250 |
|
251 /** |
|
252 * Log an unexpected failure. Intended to be used from harness code, not |
|
253 * from tests. |
|
254 */ |
|
255 "logFailure": function(aTestName, aMessage) { |
|
256 this.report({ |
|
257 "name": aTestName, |
|
258 "message": aMessage, |
|
259 "result": false, |
|
260 "todo": false |
|
261 }); |
|
262 }, |
|
263 |
|
264 /** |
|
265 * Timeout the current test. Intended to be used from harness code, not |
|
266 * from tests. |
|
267 */ |
|
268 "timeout": function() { |
|
269 this.logFailure("Timeout", "Test runner timed us out."); |
|
270 timeout(); |
|
271 } |
|
272 }; |
|
273 (function() { |
|
274 try { |
|
275 var path = W3CTest.getPath(); |
|
276 if (path) { |
|
277 // Get expected fails. If there aren't any, there will be a 404, which is |
|
278 // fine. Anything else is unexpected. |
|
279 var url = W3CTest.pathprefix + "failures/" + path + ".json"; |
|
280 var request = new XMLHttpRequest(); |
|
281 request.open("GET", url, false); |
|
282 request.send(); |
|
283 if (request.status === 200) { |
|
284 W3CTest.expectedFailures = JSON.parse(request.responseText); |
|
285 } else if (request.status !== 404) { |
|
286 W3CTest.logFailure("Fetching failures file", "Request status was " + request.status); |
|
287 } |
|
288 } |
|
289 |
|
290 add_result_callback(W3CTest.result.bind(W3CTest)); |
|
291 add_completion_callback(W3CTest.finish.bind(W3CTest)); |
|
292 setup({ |
|
293 "output": false, |
|
294 "explicit_timeout": true |
|
295 }); |
|
296 } catch (e) { |
|
297 W3CTest.logFailure("Harness setup", "Unexpected exception: " + e); |
|
298 } |
|
299 })(); |