OLD | NEW |
1 /*global self*/ | 1 /*global self*/ |
2 /*jshint latedef: nofunc*/ | 2 /*jshint latedef: nofunc*/ |
3 /* | 3 /* |
4 Distributed under both the W3C Test Suite License [1] and the W3C | 4 Distributed under both the W3C Test Suite License [1] and the W3C |
5 3-clause BSD License [2]. To contribute to a W3C Test Suite, see the | 5 3-clause BSD License [2]. To contribute to a W3C Test Suite, see the |
6 policies and contribution forms [3]. | 6 policies and contribution forms [3]. |
7 | 7 |
8 [1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license | 8 [1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license |
9 [2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license | 9 [2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license |
10 [3] http://www.w3.org/2004/10/27-testcases | 10 [3] http://www.w3.org/2004/10/27-testcases |
11 */ | 11 */ |
12 | 12 |
13 /* | 13 /* Documentation is in docs/api.md */ |
14 * == Introduction == | |
15 * | |
16 * This file provides a framework for writing testcases. It is intended to | |
17 * provide a convenient API for making common assertions, and to work both | |
18 * for testing synchronous and asynchronous DOM features in a way that | |
19 * promotes clear, robust, tests. | |
20 * | |
21 * == Basic Usage == | |
22 * | |
23 * To use this file, import the script and the testharnessreport script into | |
24 * the test document: | |
25 * | |
26 * <script src="/resources/testharness.js"></script> | |
27 * <script src="/resources/testharnessreport.js"></script> | |
28 * | |
29 * Within each file one may define one or more tests. Each test is atomic | |
30 * in the sense that a single test has a single result (pass/fail/timeout). | |
31 * Within each test one may have a number of asserts. The test fails at the | |
32 * first failing assert, and the remainder of the test is (typically) not run. | |
33 * | |
34 * If the file containing the tests is a HTML file, a table containing the test | |
35 * results will be added to the document after all tests have run. By default th
is | |
36 * will be added to a div element with id=log if it exists, or a new div element | |
37 * appended to document.body if it does not. | |
38 * | |
39 * NOTE: By default tests must be created before the load event fires. For ways | |
40 * to create tests after the load event, see "Determining when all tests | |
41 * are complete", below | |
42 * | |
43 * == Synchronous Tests == | |
44 * | |
45 * To create a synchronous test use the test() function: | |
46 * | |
47 * test(test_function, name, properties) | |
48 * | |
49 * test_function is a function that contains the code to test. For example a | |
50 * trivial passing test would be: | |
51 * | |
52 * test(function() {assert_true(true)}, "assert_true with true") | |
53 * | |
54 * The function passed in is run in the test() call. | |
55 * | |
56 * properties is an object that overrides default test properties. The | |
57 * recognised properties are: | |
58 * timeout - the test timeout in ms | |
59 * | |
60 * e.g. | |
61 * test(test_function, "Sample test", {timeout:1000}) | |
62 * | |
63 * would run test_function with a timeout of 1s. | |
64 * | |
65 * Additionally, test-specific metadata can be passed in the properties. These | |
66 * are used when the individual test has different metadata from that stored | |
67 * in the <head>. | |
68 * The recognized metadata properties are: | |
69 * | |
70 * help - The url or an array of urls of the part(s) of the specification(s) | |
71 * being tested | |
72 * | |
73 * assert - A human readable description of what the test is attempting | |
74 * to prove | |
75 * | |
76 * author - Name and contact information for the author of the test in the | |
77 * format: "Name <email_addr>" or "Name http://contact/url" | |
78 * | |
79 * == Asynchronous Tests == | |
80 * | |
81 * Testing asynchronous features is somewhat more complex since the result of | |
82 * a test may depend on one or more events or other callbacks. The API provided | |
83 * for testing these features is indended to be rather low-level but hopefully | |
84 * applicable to many situations. | |
85 * | |
86 * To create a test, one starts by getting a Test object using async_test: | |
87 * | |
88 * async_test(name, properties) | |
89 * | |
90 * e.g. | |
91 * var t = async_test("Simple async test") | |
92 * | |
93 * Assertions can be added to the test by calling the step method of the test | |
94 * object with a function containing the test assertions: | |
95 * | |
96 * t.step(function() {assert_true(true)}); | |
97 * | |
98 * When all the steps are complete, the done() method must be called: | |
99 * | |
100 * t.done(); | |
101 * | |
102 * As a convenience, async_test can also takes a function as first argument. | |
103 * This function is called with the test object as both its `this` object and | |
104 * first argument. The above example can be rewritten as: | |
105 * | |
106 * async_test(function(t) { | |
107 * object.some_event = function() { | |
108 * t.step(function (){assert_true(true); t.done();}); | |
109 * }; | |
110 * }, "Simple async test"); | |
111 * | |
112 * which avoids cluttering the global scope with references to async | |
113 * tests instances. | |
114 * | |
115 * The properties argument is identical to that for test(). | |
116 * | |
117 * In many cases it is convenient to run a step in response to an event or a | |
118 * callback. A convenient method of doing this is through the step_func method | |
119 * which returns a function that, when called runs a test step. For example | |
120 * | |
121 * object.some_event = t.step_func(function(e) {assert_true(e.a)}); | |
122 * | |
123 * For asynchronous callbacks that should never execute, unreached_func can | |
124 * be used. For example: | |
125 * | |
126 * object.some_event = t.unreached_func("some_event should not fire"); | |
127 * | |
128 * == Single Page Tests == | |
129 * | |
130 * Sometimes, particularly when dealing with asynchronous behaviour, | |
131 * having exactly one test per page is desirable, and the overhead of | |
132 * wrapping everything in functions for isolation becomes | |
133 * burdensome. For these cases testharness.js support "single page | |
134 * tests". | |
135 * | |
136 * In order for a test to be interpreted as a "single page" test, the | |
137 * it must simply not call test() or async_test() anywhere on the page, and | |
138 * must call the done() function to indicate that the test is complete. All | |
139 * the assert_* functions are avaliable as normal, but are called without | |
140 * the normal step function wrapper. For example: | |
141 * | |
142 * <!doctype html> | |
143 * <title>Example single-page test</title> | |
144 * <script src="/resources/testharness.js"></script> | |
145 * <script src="/resources/testharnessreport.js"></script> | |
146 * <body> | |
147 * <script> | |
148 * assert_equals(document.body, document.getElementsByTagName("body")[0]) | |
149 * done() | |
150 * </script> | |
151 * | |
152 * The test title for sinple page tests is always taken from document.title. | |
153 * | |
154 * == Making assertions == | |
155 * | |
156 * Functions for making assertions start assert_ | |
157 * The best way to get a list is to look in this file for functions names | |
158 * matching that pattern. The general signature is | |
159 * | |
160 * assert_something(actual, expected, description) | |
161 * | |
162 * although not all assertions precisely match this pattern e.g. assert_true | |
163 * only takes actual and description as arguments. | |
164 * | |
165 * The description parameter is used to present more useful error messages when | |
166 * a test fails | |
167 * | |
168 * NOTE: All asserts must be located in a test() or a step of an async_test(). | |
169 * asserts outside these places won't be detected correctly by the harness | |
170 * and may cause a file to stop testing. | |
171 * | |
172 * == Cleanup == | |
173 * | |
174 * Occasionally tests may create state that will persist beyond the test itself. | |
175 * In order to ensure that tests are independent, such state should be cleaned | |
176 * up once the test has a result. This can be achieved by adding cleanup | |
177 * callbacks to the test. Such callbacks are registered using the add_cleanup | |
178 * function on the test object. All registered callbacks will be run as soon as | |
179 * the test result is known. For example | |
180 * | |
181 * test(function() { | |
182 * window.some_global = "example"; | |
183 * this.add_cleanup(function() {delete window.some_global}); | |
184 * assert_true(false); | |
185 * }); | |
186 * | |
187 * == Harness Timeout == | |
188 * | |
189 * The overall harness admits two timeout values "normal" (the | |
190 * default) and "long", used for tests which have an unusually long | |
191 * runtime. After the timeout is reached, the harness will stop | |
192 * waiting for further async tests to complete. By default the | |
193 * timeouts are set to 10s and 60s, respectively, but may be changed | |
194 * when the test is run on hardware with different performance | |
195 * characteristics to a common desktop computer. In order to opt-in | |
196 * to the longer test timeout, the test must specify a meta element: | |
197 * | |
198 * <meta name="timeout" content="long"> | |
199 * | |
200 * Occasionally tests may have a race between the harness timing out and | |
201 * a particular test failing; typically when the test waits for some event | |
202 * that never occurs. In this case it is possible to use test.force_timeout() | |
203 * in place of assert_unreached(), to immediately fail the test but with a | |
204 * status of "timeout". This should only be used as a last resort when it is | |
205 * not possible to make the test reliable in some other way. | |
206 * | |
207 * == Setup == | |
208 * | |
209 * Sometimes tests require non-trivial setup that may fail. For this purpose | |
210 * there is a setup() function, that may be called with one or two arguments. | |
211 * The two argument version is: | |
212 * | |
213 * setup(func, properties) | |
214 * | |
215 * The one argument versions may omit either argument. | |
216 * func is a function to be run synchronously. setup() becomes a no-op once | |
217 * any tests have returned results. Properties are global properties of the test | |
218 * harness. Currently recognised properties are: | |
219 * | |
220 * | |
221 * explicit_done - Wait for an explicit call to done() before declaring all | |
222 * tests complete (see below; implicitly true for single page | |
223 * tests) | |
224 * | |
225 * output_document - The document to which results should be logged. By default | |
226 * this is the current document but could be an ancestor | |
227 * document in some cases e.g. a SVG test loaded in an HTML | |
228 * wrapper | |
229 * | |
230 * explicit_timeout - disable file timeout; only stop waiting for results | |
231 * when the timeout() function is called (typically for | |
232 * use when integrating with some existing test framework | |
233 * that has its own timeout mechanism). | |
234 * | |
235 * allow_uncaught_exception - don't treat an uncaught exception as an error; | |
236 * needed when e.g. testing the window.onerror | |
237 * handler. | |
238 * | |
239 * timeout_multiplier - Multiplier to apply to per-test timeouts. | |
240 * | |
241 * == Determining when all tests are complete == | |
242 * | |
243 * By default the test harness will assume there are no more results to come | |
244 * when: | |
245 * 1) There are no Test objects that have been created but not completed | |
246 * 2) The load event on the document has fired | |
247 * | |
248 * This behaviour can be overridden by setting the explicit_done property to | |
249 * true in a call to setup(). If explicit_done is true, the test harness will | |
250 * not assume it is done until the global done() function is called. Once done() | |
251 * is called, the two conditions above apply like normal. | |
252 * | |
253 * == Generating tests == | |
254 * | |
255 * There are scenarios in which is is desirable to create a large number of | |
256 * (synchronous) tests that are internally similar but vary in the parameters | |
257 * used. To make this easier, the generate_tests function allows a single | |
258 * function to be called with each set of parameters in a list: | |
259 * | |
260 * generate_tests(test_function, parameter_lists, properties) | |
261 * | |
262 * For example: | |
263 * | |
264 * generate_tests(assert_equals, [ | |
265 * ["Sum one and one", 1+1, 2], | |
266 * ["Sum one and zero", 1+0, 1] | |
267 * ]) | |
268 * | |
269 * Is equivalent to: | |
270 * | |
271 * test(function() {assert_equals(1+1, 2)}, "Sum one and one") | |
272 * test(function() {assert_equals(1+0, 1)}, "Sum one and zero") | |
273 * | |
274 * Note that the first item in each parameter list corresponds to the name of | |
275 * the test. | |
276 * | |
277 * The properties argument is identical to that for test(). This may be a | |
278 * single object (used for all generated tests) or an array. | |
279 * | |
280 * == Callback API == | |
281 * | |
282 * The framework provides callbacks corresponding to 3 events: | |
283 * | |
284 * start - happens when the first Test is created | |
285 * result - happens when a test result is recieved | |
286 * complete - happens when all results are recieved | |
287 * | |
288 * The page defining the tests may add callbacks for these events by calling | |
289 * the following methods: | |
290 * | |
291 * add_start_callback(callback) - callback called with no arguments | |
292 * add_result_callback(callback) - callback called with a test argument | |
293 * add_completion_callback(callback) - callback called with an array of tests | |
294 * and an status object | |
295 * | |
296 * tests have the following properties: | |
297 * status: A status code. This can be compared to the PASS, FAIL, TIMEOUT and | |
298 * NOTRUN properties on the test object | |
299 * message: A message indicating the reason for failure. In the future this | |
300 * will always be a string | |
301 * | |
302 * The status object gives the overall status of the harness. It has the | |
303 * following properties: | |
304 * status: Can be compared to the OK, ERROR and TIMEOUT properties | |
305 * message: An error message set when the status is ERROR | |
306 * | |
307 * == External API == | |
308 * | |
309 * In order to collect the results of multiple pages containing tests, the test | |
310 * harness will, when loaded in a nested browsing context, attempt to call | |
311 * certain functions in each ancestor and opener browsing context: | |
312 * | |
313 * start - start_callback | |
314 * result - result_callback | |
315 * complete - completion_callback | |
316 * | |
317 * These are given the same arguments as the corresponding internal callbacks | |
318 * described above. | |
319 * | |
320 * == External API through cross-document messaging == | |
321 * | |
322 * Where supported, the test harness will also send messages using | |
323 * cross-document messaging to each ancestor and opener browsing context. Since | |
324 * it uses the wildcard keyword (*), cross-origin communication is enabled and | |
325 * script on different origins can collect the results. | |
326 * | |
327 * This API follows similar conventions as those described above only slightly | |
328 * modified to accommodate message event API. Each message is sent by the harnes
s | |
329 * is passed a single vanilla object, available as the `data` property of the | |
330 * event object. These objects are structures as follows: | |
331 * | |
332 * start - { type: "start" } | |
333 * result - { type: "result", test: Test } | |
334 * complete - { type: "complete", tests: [Test, ...], status: TestsStatus } | |
335 * | |
336 * == List of assertions == | |
337 * | |
338 * assert_true(actual, description) | |
339 * asserts that /actual/ is strictly true | |
340 * | |
341 * assert_false(actual, description) | |
342 * asserts that /actual/ is strictly false | |
343 * | |
344 * assert_equals(actual, expected, description) | |
345 * asserts that /actual/ is the same value as /expected/ | |
346 * | |
347 * assert_not_equals(actual, expected, description) | |
348 * asserts that /actual/ is a different value to /expected/. Yes, this means | |
349 * that "expected" is a misnomer | |
350 * | |
351 * assert_in_array(actual, expected, description) | |
352 * asserts that /expected/ is an Array, and /actual/ is equal to one of the | |
353 * members -- expected.indexOf(actual) != -1 | |
354 * | |
355 * assert_array_equals(actual, expected, description) | |
356 * asserts that /actual/ and /expected/ have the same length and the value of | |
357 * each indexed property in /actual/ is the strictly equal to the correspondin
g | |
358 * property value in /expected/ | |
359 * | |
360 * assert_approx_equals(actual, expected, epsilon, description) | |
361 * asserts that /actual/ is a number within +/- /epsilon/ of /expected/ | |
362 * | |
363 * assert_less_than(actual, expected, description) | |
364 * asserts that /actual/ is a number less than /expected/ | |
365 * | |
366 * assert_greater_than(actual, expected, description) | |
367 * asserts that /actual/ is a number greater than /expected/ | |
368 * | |
369 * assert_less_than_equal(actual, expected, description) | |
370 * asserts that /actual/ is a number less than or equal to /expected/ | |
371 * | |
372 * assert_greater_than_equal(actual, expected, description) | |
373 * asserts that /actual/ is a number greater than or equal to /expected/ | |
374 * | |
375 * assert_regexp_match(actual, expected, description) | |
376 * asserts that /actual/ matches the regexp /expected/ | |
377 * | |
378 * assert_class_string(object, class_name, description) | |
379 * asserts that the class string of /object/ as returned in | |
380 * Object.prototype.toString is equal to /class_name/. | |
381 * | |
382 * assert_own_property(object, property_name, description) | |
383 * assert that object has own property property_name | |
384 * | |
385 * assert_inherits(object, property_name, description) | |
386 * assert that object does not have an own property named property_name | |
387 * but that property_name is present in the prototype chain for object | |
388 * | |
389 * assert_idl_attribute(object, attribute_name, description) | |
390 * assert that an object that is an instance of some interface has the | |
391 * attribute attribute_name following the conditions specified by WebIDL | |
392 * | |
393 * assert_readonly(object, property_name, description) | |
394 * assert that property property_name on object is readonly | |
395 * | |
396 * assert_throws(code, func, description) | |
397 * code - the expected exception: | |
398 * o string: the thrown exception must be a DOMException with the given | |
399 * name, e.g., "TimeoutError" (for compatibility with existing | |
400 * tests, a constant is also supported, e.g., "TIMEOUT_ERR") | |
401 * o object: the thrown exception must have a property called "name" that | |
402 * matches code.name | |
403 * o null: allow any exception (in general, one of the options above | |
404 * should be used) | |
405 * func - a function that should throw | |
406 * | |
407 * assert_unreached(description) | |
408 * asserts if called. Used to ensure that some codepath is *not* taken e.g. | |
409 * an event does not fire. | |
410 * | |
411 * assert_any(assert_func, actual, expected_array, extra_arg_1, ... extra_arg_N) | |
412 * asserts that one assert_func(actual, expected_array_N, extra_arg1, ..., ext
ra_arg_N) | |
413 * is true for some expected_array_N in expected_array. This only works for as
sert_func | |
414 * with signature assert_func(actual, expected, args_1, ..., args_N). Note tha
t tests | |
415 * with multiple allowed pass conditions are bad practice unless the spec spec
ifically | |
416 * allows multiple behaviours. Test authors should not use this method simply
to hide | |
417 * UA bugs. | |
418 * | |
419 * assert_exists(object, property_name, description) | |
420 * *** deprecated *** | |
421 * asserts that object has an own property property_name | |
422 * | |
423 * assert_not_exists(object, property_name, description) | |
424 * *** deprecated *** | |
425 * assert that object does not have own property property_name | |
426 * | |
427 * == Metadata == | |
428 * | |
429 * It is possible to add optional metadata to tests; this can be done in one of
two ways; | |
430 * either by adding <meta> elements to the head of the document containing the t
ests, | |
431 * or by adding the metadata to individual [async_]test calls, as properties. | |
432 */ | |
433 | 14 |
434 (function () | 15 (function () |
435 { | 16 { |
436 var debug = false; | 17 var debug = false; |
437 // default timeout is 10 seconds, test can override if needed | 18 // default timeout is 10 seconds, test can override if needed |
438 var settings = { | 19 var settings = { |
439 output:true, | 20 output:true, |
440 harness_timeout:{ | 21 harness_timeout:{ |
441 "normal":10000, | 22 "normal":10000, |
442 "long":60000 | 23 "long":60000 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
483 this.output_handler = null; | 64 this.output_handler = null; |
484 this.all_loaded = false; | 65 this.all_loaded = false; |
485 var this_obj = this; | 66 var this_obj = this; |
486 on_event(window, 'load', function() { | 67 on_event(window, 'load', function() { |
487 this_obj.all_loaded = true; | 68 this_obj.all_loaded = true; |
488 }); | 69 }); |
489 } | 70 } |
490 | 71 |
491 WindowTestEnvironment.prototype._dispatch = function(selector, callback_args
, message_arg) { | 72 WindowTestEnvironment.prototype._dispatch = function(selector, callback_args
, message_arg) { |
492 this._forEach_windows( | 73 this._forEach_windows( |
493 function(w, is_same_origin) { | 74 function(w, same_origin) { |
494 if (is_same_origin && selector in w) { | 75 if (same_origin) { |
495 try { | 76 try { |
496 w[selector].apply(undefined, callback_args); | 77 var has_selector = selector in w; |
497 } catch (e) { | 78 } catch(e) { |
498 if (debug) { | 79 // If document.domain was set at some point same_ori
gin can be |
499 throw e; | 80 // wrong and the above will fail. |
| 81 has_selector = false; |
| 82 } |
| 83 if (has_selector) { |
| 84 try { |
| 85 w[selector].apply(undefined, callback_args); |
| 86 } catch (e) { |
| 87 if (debug) { |
| 88 throw e; |
| 89 } |
500 } | 90 } |
501 } | 91 } |
502 } | 92 } |
503 if (supports_post_message(w) && w !== self) { | 93 if (supports_post_message(w) && w !== self) { |
504 w.postMessage(message_arg, "*"); | 94 w.postMessage(message_arg, "*"); |
505 } | 95 } |
506 }); | 96 }); |
507 }; | 97 }; |
508 | 98 |
509 WindowTestEnvironment.prototype._forEach_windows = function(callback) { | 99 WindowTestEnvironment.prototype._forEach_windows = function(callback) { |
(...skipping 366 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
876 .catch(test.step_func( | 466 .catch(test.step_func( |
877 function(value) { | 467 function(value) { |
878 if (value instanceof AssertionError) { | 468 if (value instanceof AssertionError) { |
879 throw value; | 469 throw value; |
880 } | 470 } |
881 assert(false, "promise_test", null, | 471 assert(false, "promise_test", null, |
882 "Unhandled rejection with value: ${value}", {value:va
lue}); | 472 "Unhandled rejection with value: ${value}", {value:va
lue}); |
883 })); | 473 })); |
884 } | 474 } |
885 | 475 |
| 476 function promise_rejects(test, expected, promise) { |
| 477 return promise.then(test.unreached_func("Should have rejected.")).catch(
function(e) { |
| 478 assert_throws(expected, function() { throw e }); |
| 479 }); |
| 480 } |
| 481 |
| 482 /** |
| 483 * This constructor helper allows DOM events to be handled using Promises, |
| 484 * which can make it a lot easier to test a very specific series of events, |
| 485 * including ensuring that unexpected events are not fired at any point. |
| 486 */ |
| 487 function EventWatcher(test, watchedNode, eventTypes) |
| 488 { |
| 489 if (typeof eventTypes == 'string') { |
| 490 eventTypes = [eventTypes]; |
| 491 } |
| 492 |
| 493 var waitingFor = null; |
| 494 |
| 495 var eventHandler = test.step_func(function(evt) { |
| 496 assert_true(!!waitingFor, |
| 497 'Not expecting event, but got ' + evt.type + ' event'); |
| 498 assert_equals(evt.type, waitingFor.types[0], |
| 499 'Expected ' + waitingFor.types[0] + ' event, but got '
+ |
| 500 evt.type + ' event instead'); |
| 501 if (waitingFor.types.length > 1) { |
| 502 // Pop first event from array |
| 503 waitingFor.types.shift(); |
| 504 return; |
| 505 } |
| 506 // We need to null out waitingFor before calling the resolve functio
n |
| 507 // since the Promise's resolve handlers may call wait_for() which wi
ll |
| 508 // need to set waitingFor. |
| 509 var resolveFunc = waitingFor.resolve; |
| 510 waitingFor = null; |
| 511 resolveFunc(evt); |
| 512 }); |
| 513 |
| 514 for (var i = 0; i < eventTypes.length; i++) { |
| 515 watchedNode.addEventListener(eventTypes[i], eventHandler); |
| 516 } |
| 517 |
| 518 /** |
| 519 * Returns a Promise that will resolve after the specified event or |
| 520 * series of events has occured. |
| 521 */ |
| 522 this.wait_for = function(types) { |
| 523 if (waitingFor) { |
| 524 return Promise.reject('Already waiting for an event or events'); |
| 525 } |
| 526 if (typeof types == 'string') { |
| 527 types = [types]; |
| 528 } |
| 529 return new Promise(function(resolve, reject) { |
| 530 waitingFor = { |
| 531 types: types, |
| 532 resolve: resolve, |
| 533 reject: reject |
| 534 }; |
| 535 }); |
| 536 }; |
| 537 |
| 538 function stop_watching() { |
| 539 for (var i = 0; i < eventTypes.length; i++) { |
| 540 watchedNode.removeEventListener(eventTypes[i], eventHandler); |
| 541 } |
| 542 }; |
| 543 |
| 544 test.add_cleanup(stop_watching); |
| 545 |
| 546 return this; |
| 547 } |
| 548 expose(EventWatcher, 'EventWatcher'); |
| 549 |
886 function setup(func_or_properties, maybe_properties) | 550 function setup(func_or_properties, maybe_properties) |
887 { | 551 { |
888 var func = null; | 552 var func = null; |
889 var properties = {}; | 553 var properties = {}; |
890 if (arguments.length === 2) { | 554 if (arguments.length === 2) { |
891 func = func_or_properties; | 555 func = func_or_properties; |
892 properties = maybe_properties; | 556 properties = maybe_properties; |
893 } else if (func_or_properties instanceof Function) { | 557 } else if (func_or_properties instanceof Function) { |
894 func = func_or_properties; | 558 func = func_or_properties; |
895 } else { | 559 } else { |
(...skipping 27 matching lines...) Expand all Loading... |
923 } | 587 } |
924 | 588 |
925 function on_event(object, event, callback) | 589 function on_event(object, event, callback) |
926 { | 590 { |
927 object.addEventListener(event, callback, false); | 591 object.addEventListener(event, callback, false); |
928 } | 592 } |
929 | 593 |
930 expose(test, 'test'); | 594 expose(test, 'test'); |
931 expose(async_test, 'async_test'); | 595 expose(async_test, 'async_test'); |
932 expose(promise_test, 'promise_test'); | 596 expose(promise_test, 'promise_test'); |
| 597 expose(promise_rejects, 'promise_rejects'); |
933 expose(generate_tests, 'generate_tests'); | 598 expose(generate_tests, 'generate_tests'); |
934 expose(setup, 'setup'); | 599 expose(setup, 'setup'); |
935 expose(done, 'done'); | 600 expose(done, 'done'); |
936 expose(on_event, 'on_event'); | 601 expose(on_event, 'on_event'); |
937 | 602 |
938 /* | 603 /* |
939 * Return a string truncated to the given length, with ... added at the end | 604 * Return a string truncated to the given length, with ... added at the end |
940 * if it was longer. | 605 * if it was longer. |
941 */ | 606 */ |
942 function truncate(s, len) | 607 function truncate(s, len) |
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1186 function assert_array_equals(actual, expected, description) | 851 function assert_array_equals(actual, expected, description) |
1187 { | 852 { |
1188 assert(actual.length === expected.length, | 853 assert(actual.length === expected.length, |
1189 "assert_array_equals", description, | 854 "assert_array_equals", description, |
1190 "lengths differ, expected ${expected} got ${actual}", | 855 "lengths differ, expected ${expected} got ${actual}", |
1191 {expected:expected.length, actual:actual.length}); | 856 {expected:expected.length, actual:actual.length}); |
1192 | 857 |
1193 for (var i = 0; i < actual.length; i++) { | 858 for (var i = 0; i < actual.length; i++) { |
1194 assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i), | 859 assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i), |
1195 "assert_array_equals", description, | 860 "assert_array_equals", description, |
1196 "property ${i}, property expected to be $expected but was $ac
tual", | 861 "property ${i}, property expected to be ${expected} but was $
{actual}", |
1197 {i:i, expected:expected.hasOwnProperty(i) ? "present" : "miss
ing", | 862 {i:i, expected:expected.hasOwnProperty(i) ? "present" : "miss
ing", |
1198 actual:actual.hasOwnProperty(i) ? "present" : "missing"}); | 863 actual:actual.hasOwnProperty(i) ? "present" : "missing"}); |
1199 assert(same_value(expected[i], actual[i]), | 864 assert(same_value(expected[i], actual[i]), |
1200 "assert_array_equals", description, | 865 "assert_array_equals", description, |
1201 "property ${i}, expected ${expected} but got ${actual}", | 866 "property ${i}, expected ${expected} but got ${actual}", |
1202 {i:i, expected:expected[i], actual:actual[i]}); | 867 {i:i, expected:expected[i], actual:actual[i]}); |
1203 } | 868 } |
1204 } | 869 } |
1205 expose(assert_array_equals, "assert_array_equals"); | 870 expose(assert_array_equals, "assert_array_equals"); |
1206 | 871 |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1248 "expected a number but got a ${type_actual}", | 913 "expected a number but got a ${type_actual}", |
1249 {type_actual:typeof actual}); | 914 {type_actual:typeof actual}); |
1250 | 915 |
1251 assert(actual > expected, | 916 assert(actual > expected, |
1252 "assert_greater_than", description, | 917 "assert_greater_than", description, |
1253 "expected a number greater than ${expected} but got ${actual}", | 918 "expected a number greater than ${expected} but got ${actual}", |
1254 {expected:expected, actual:actual}); | 919 {expected:expected, actual:actual}); |
1255 } | 920 } |
1256 expose(assert_greater_than, "assert_greater_than"); | 921 expose(assert_greater_than, "assert_greater_than"); |
1257 | 922 |
| 923 function assert_between_exclusive(actual, lower, upper, description) |
| 924 { |
| 925 /* |
| 926 * Test if a primitive number is between two others |
| 927 */ |
| 928 assert(typeof actual === "number", |
| 929 "assert_between_exclusive", description, |
| 930 "expected a number but got a ${type_actual}", |
| 931 {type_actual:typeof actual}); |
| 932 |
| 933 assert(actual > lower && actual < upper, |
| 934 "assert_between_exclusive", description, |
| 935 "expected a number greater than ${lower} " + |
| 936 "and less than ${upper} but got ${actual}", |
| 937 {lower:lower, upper:upper, actual:actual}); |
| 938 } |
| 939 expose(assert_between_exclusive, "assert_between_exclusive"); |
| 940 |
1258 function assert_less_than_equal(actual, expected, description) | 941 function assert_less_than_equal(actual, expected, description) |
1259 { | 942 { |
1260 /* | 943 /* |
1261 * Test if a primitive number is less than or equal to another | 944 * Test if a primitive number is less than or equal to another |
1262 */ | 945 */ |
1263 assert(typeof actual === "number", | 946 assert(typeof actual === "number", |
1264 "assert_less_than_equal", description, | 947 "assert_less_than_equal", description, |
1265 "expected a number but got a ${type_actual}", | 948 "expected a number but got a ${type_actual}", |
1266 {type_actual:typeof actual}); | 949 {type_actual:typeof actual}); |
1267 | 950 |
1268 assert(actual <= expected, | 951 assert(actual <= expected, |
1269 "assert_less_than", description, | 952 "assert_less_than_equal", description, |
1270 "expected a number less than or equal to ${expected} but got ${ac
tual}", | 953 "expected a number less than or equal to ${expected} but got ${ac
tual}", |
1271 {expected:expected, actual:actual}); | 954 {expected:expected, actual:actual}); |
1272 } | 955 } |
1273 expose(assert_less_than_equal, "assert_less_than_equal"); | 956 expose(assert_less_than_equal, "assert_less_than_equal"); |
1274 | 957 |
1275 function assert_greater_than_equal(actual, expected, description) | 958 function assert_greater_than_equal(actual, expected, description) |
1276 { | 959 { |
1277 /* | 960 /* |
1278 * Test if a primitive number is greater than or equal to another | 961 * Test if a primitive number is greater than or equal to another |
1279 */ | 962 */ |
1280 assert(typeof actual === "number", | 963 assert(typeof actual === "number", |
1281 "assert_greater_than_equal", description, | 964 "assert_greater_than_equal", description, |
1282 "expected a number but got a ${type_actual}", | 965 "expected a number but got a ${type_actual}", |
1283 {type_actual:typeof actual}); | 966 {type_actual:typeof actual}); |
1284 | 967 |
1285 assert(actual >= expected, | 968 assert(actual >= expected, |
1286 "assert_greater_than_equal", description, | 969 "assert_greater_than_equal", description, |
1287 "expected a number greater than or equal to ${expected} but got $
{actual}", | 970 "expected a number greater than or equal to ${expected} but got $
{actual}", |
1288 {expected:expected, actual:actual}); | 971 {expected:expected, actual:actual}); |
1289 } | 972 } |
1290 expose(assert_greater_than_equal, "assert_greater_than_equal"); | 973 expose(assert_greater_than_equal, "assert_greater_than_equal"); |
1291 | 974 |
| 975 function assert_between_inclusive(actual, lower, upper, description) |
| 976 { |
| 977 /* |
| 978 * Test if a primitive number is between to two others or equal to eithe
r of them |
| 979 */ |
| 980 assert(typeof actual === "number", |
| 981 "assert_between_inclusive", description, |
| 982 "expected a number but got a ${type_actual}", |
| 983 {type_actual:typeof actual}); |
| 984 |
| 985 assert(actual >= lower && actual <= upper, |
| 986 "assert_between_inclusive", description, |
| 987 "expected a number greater than or equal to ${lower} " + |
| 988 "and less than or equal to ${upper} but got ${actual}", |
| 989 {lower:lower, upper:upper, actual:actual}); |
| 990 } |
| 991 expose(assert_between_inclusive, "assert_between_inclusive"); |
| 992 |
1292 function assert_regexp_match(actual, expected, description) { | 993 function assert_regexp_match(actual, expected, description) { |
1293 /* | 994 /* |
1294 * Test if a string (actual) matches a regexp (expected) | 995 * Test if a string (actual) matches a regexp (expected) |
1295 */ | 996 */ |
1296 assert(expected.test(actual), | 997 assert(expected.test(actual), |
1297 "assert_regexp_match", description, | 998 "assert_regexp_match", description, |
1298 "expected ${expected} but got ${actual}", | 999 "expected ${expected} but got ${actual}", |
1299 {expected:expected, actual:actual}); | 1000 {expected:expected, actual:actual}); |
1300 } | 1001 } |
1301 expose(assert_regexp_match, "assert_regexp_match"); | 1002 expose(assert_regexp_match, "assert_regexp_match"); |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1433 TypeMismatchError: 17, | 1134 TypeMismatchError: 17, |
1434 SecurityError: 18, | 1135 SecurityError: 18, |
1435 NetworkError: 19, | 1136 NetworkError: 19, |
1436 AbortError: 20, | 1137 AbortError: 20, |
1437 URLMismatchError: 21, | 1138 URLMismatchError: 21, |
1438 QuotaExceededError: 22, | 1139 QuotaExceededError: 22, |
1439 TimeoutError: 23, | 1140 TimeoutError: 23, |
1440 InvalidNodeTypeError: 24, | 1141 InvalidNodeTypeError: 24, |
1441 DataCloneError: 25, | 1142 DataCloneError: 25, |
1442 | 1143 |
| 1144 EncodingError: 0, |
| 1145 NotReadableError: 0, |
1443 UnknownError: 0, | 1146 UnknownError: 0, |
1444 ConstraintError: 0, | 1147 ConstraintError: 0, |
1445 DataError: 0, | 1148 DataError: 0, |
1446 TransactionInactiveError: 0, | 1149 TransactionInactiveError: 0, |
1447 ReadOnlyError: 0, | 1150 ReadOnlyError: 0, |
1448 VersionError: 0 | 1151 VersionError: 0, |
| 1152 OperationError: 0, |
1449 }; | 1153 }; |
1450 | 1154 |
1451 if (!(name in name_code_map)) { | 1155 if (!(name in name_code_map)) { |
1452 throw new AssertionError('Test bug: unrecognized DOMException co
de "' + code + '" passed to assert_throws()'); | 1156 throw new AssertionError('Test bug: unrecognized DOMException co
de "' + code + '" passed to assert_throws()'); |
1453 } | 1157 } |
1454 | 1158 |
1455 var required_props = { code: name_code_map[name] }; | 1159 var required_props = { code: name_code_map[name] }; |
1456 | 1160 |
1457 if (required_props.code === 0 || | 1161 if (required_props.code === 0 || |
1458 ("name" in e && e.name !== e.name.toUpperCase() && e.name !== "DO
MException")) { | 1162 (typeof e == "object" && |
| 1163 "name" in e && |
| 1164 e.name !== e.name.toUpperCase() && |
| 1165 e.name !== "DOMException")) { |
1459 // New style exception: also test the name property. | 1166 // New style exception: also test the name property. |
1460 required_props.name = name; | 1167 required_props.name = name; |
1461 } | 1168 } |
1462 | 1169 |
1463 //We'd like to test that e instanceof the appropriate interface, | 1170 //We'd like to test that e instanceof the appropriate interface, |
1464 //but we can't, because we don't know what window it was created | 1171 //but we can't, because we don't know what window it was created |
1465 //in. It might be an instanceof the appropriate interface on some | 1172 //in. It might be an instanceof the appropriate interface on some |
1466 //unknown other window. TODO: Work around this somehow? | 1173 //unknown other window. TODO: Work around this somehow? |
1467 | 1174 |
1468 assert(typeof e == "object", | 1175 assert(typeof e == "object", |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1522 | 1229 |
1523 this.properties = properties; | 1230 this.properties = properties; |
1524 var timeout = properties.timeout ? properties.timeout : settings.test_ti
meout; | 1231 var timeout = properties.timeout ? properties.timeout : settings.test_ti
meout; |
1525 if (timeout !== null) { | 1232 if (timeout !== null) { |
1526 this.timeout_length = timeout * tests.timeout_multiplier; | 1233 this.timeout_length = timeout * tests.timeout_multiplier; |
1527 } else { | 1234 } else { |
1528 this.timeout_length = null; | 1235 this.timeout_length = null; |
1529 } | 1236 } |
1530 | 1237 |
1531 this.message = null; | 1238 this.message = null; |
| 1239 this.stack = null; |
1532 | 1240 |
1533 this.steps = []; | 1241 this.steps = []; |
1534 | 1242 |
1535 this.cleanup_callbacks = []; | 1243 this.cleanup_callbacks = []; |
1536 | 1244 |
1537 tests.push(this); | 1245 tests.push(this); |
1538 } | 1246 } |
1539 | 1247 |
1540 Test.statuses = { | 1248 Test.statuses = { |
1541 PASS:0, | 1249 PASS:0, |
(...skipping 16 matching lines...) Expand all Loading... |
1558 if (!this._structured_clone) { | 1266 if (!this._structured_clone) { |
1559 var msg = this.message; | 1267 var msg = this.message; |
1560 msg = msg ? String(msg) : msg; | 1268 msg = msg ? String(msg) : msg; |
1561 this._structured_clone = merge({ | 1269 this._structured_clone = merge({ |
1562 name:String(this.name), | 1270 name:String(this.name), |
1563 properties:merge({}, this.properties), | 1271 properties:merge({}, this.properties), |
1564 }, Test.statuses); | 1272 }, Test.statuses); |
1565 } | 1273 } |
1566 this._structured_clone.status = this.status; | 1274 this._structured_clone.status = this.status; |
1567 this._structured_clone.message = this.message; | 1275 this._structured_clone.message = this.message; |
| 1276 this._structured_clone.stack = this.stack; |
1568 this._structured_clone.index = this.index; | 1277 this._structured_clone.index = this.index; |
1569 return this._structured_clone; | 1278 return this._structured_clone; |
1570 }; | 1279 }; |
1571 | 1280 |
1572 Test.prototype.step = function(func, this_obj) | 1281 Test.prototype.step = function(func, this_obj) |
1573 { | 1282 { |
1574 if (this.phase > this.phases.STARTED) { | 1283 if (this.phase > this.phases.STARTED) { |
1575 return; | 1284 return; |
1576 } | 1285 } |
1577 this.phase = this.phases.STARTED; | 1286 this.phase = this.phases.STARTED; |
(...skipping 12 matching lines...) Expand all Loading... |
1590 if (arguments.length === 1) { | 1299 if (arguments.length === 1) { |
1591 this_obj = this; | 1300 this_obj = this; |
1592 } | 1301 } |
1593 | 1302 |
1594 try { | 1303 try { |
1595 return func.apply(this_obj, Array.prototype.slice.call(arguments, 2)
); | 1304 return func.apply(this_obj, Array.prototype.slice.call(arguments, 2)
); |
1596 } catch (e) { | 1305 } catch (e) { |
1597 if (this.phase >= this.phases.HAS_RESULT) { | 1306 if (this.phase >= this.phases.HAS_RESULT) { |
1598 return; | 1307 return; |
1599 } | 1308 } |
1600 var message = (typeof e === "object" && e !== null) ? e.message : e; | 1309 var message = String((typeof e === "object" && e !== null) ? e.messa
ge : e); |
1601 if (typeof e.stack != "undefined" && typeof e.message == "string") { | 1310 var stack = e.stack ? e.stack : null; |
1602 //Try to make it more informative for some exceptions, at least | 1311 |
1603 //in Gecko and WebKit. This results in a stack dump instead of | 1312 this.set_status(this.FAIL, message, stack); |
1604 //just errors like "Cannot read property 'parentNode' of null" | |
1605 //or "root is null". Makes it a lot longer, of course. | |
1606 message += "(stack: " + e.stack + ")"; | |
1607 } | |
1608 this.set_status(this.FAIL, message); | |
1609 this.phase = this.phases.HAS_RESULT; | 1313 this.phase = this.phases.HAS_RESULT; |
1610 this.done(); | 1314 this.done(); |
1611 } | 1315 } |
1612 }; | 1316 }; |
1613 | 1317 |
1614 Test.prototype.step_func = function(func, this_obj) | 1318 Test.prototype.step_func = function(func, this_obj) |
1615 { | 1319 { |
1616 var test_this = this; | 1320 var test_this = this; |
1617 | 1321 |
1618 if (arguments.length === 1) { | 1322 if (arguments.length === 1) { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1664 { | 1368 { |
1665 if (this.timeout_length !== null) { | 1369 if (this.timeout_length !== null) { |
1666 var this_obj = this; | 1370 var this_obj = this; |
1667 this.timeout_id = setTimeout(function() | 1371 this.timeout_id = setTimeout(function() |
1668 { | 1372 { |
1669 this_obj.timeout(); | 1373 this_obj.timeout(); |
1670 }, this.timeout_length); | 1374 }, this.timeout_length); |
1671 } | 1375 } |
1672 }; | 1376 }; |
1673 | 1377 |
1674 Test.prototype.set_status = function(status, message) | 1378 Test.prototype.set_status = function(status, message, stack) |
1675 { | 1379 { |
1676 this.status = status; | 1380 this.status = status; |
1677 this.message = message; | 1381 this.message = message; |
| 1382 this.stack = stack ? stack : null; |
1678 }; | 1383 }; |
1679 | 1384 |
1680 Test.prototype.timeout = function() | 1385 Test.prototype.timeout = function() |
1681 { | 1386 { |
1682 this.timeout_id = null; | 1387 this.timeout_id = null; |
1683 this.set_status(this.TIMEOUT, "Test timed out"); | 1388 this.set_status(this.TIMEOUT, "Test timed out"); |
1684 this.phase = this.phases.HAS_RESULT; | 1389 this.phase = this.phases.HAS_RESULT; |
1685 this.done(); | 1390 this.done(); |
1686 }; | 1391 }; |
1687 | 1392 |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1740 }); | 1445 }); |
1741 clone.phases = merge({}, this.phases); | 1446 clone.phases = merge({}, this.phases); |
1742 return clone; | 1447 return clone; |
1743 }; | 1448 }; |
1744 | 1449 |
1745 RemoteTest.prototype.cleanup = function() {}; | 1450 RemoteTest.prototype.cleanup = function() {}; |
1746 RemoteTest.prototype.phases = Test.prototype.phases; | 1451 RemoteTest.prototype.phases = Test.prototype.phases; |
1747 RemoteTest.prototype.update_state_from = function(clone) { | 1452 RemoteTest.prototype.update_state_from = function(clone) { |
1748 this.status = clone.status; | 1453 this.status = clone.status; |
1749 this.message = clone.message; | 1454 this.message = clone.message; |
| 1455 this.stack = clone.stack; |
1750 if (this.phase === this.phases.INITIAL) { | 1456 if (this.phase === this.phases.INITIAL) { |
1751 this.phase = this.phases.STARTED; | 1457 this.phase = this.phases.STARTED; |
1752 } | 1458 } |
1753 }; | 1459 }; |
1754 RemoteTest.prototype.done = function() { | 1460 RemoteTest.prototype.done = function() { |
1755 this.phase = this.phases.COMPLETE; | 1461 this.phase = this.phases.COMPLETE; |
1756 } | 1462 } |
1757 | 1463 |
1758 /* | 1464 /* |
1759 * A RemoteWorker listens for test events from a worker. These events are | 1465 * A RemoteWorker listens for test events from a worker. These events are |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1799 } | 1505 } |
1800 | 1506 |
1801 RemoteWorker.prototype.worker_error = function(error) { | 1507 RemoteWorker.prototype.worker_error = function(error) { |
1802 var message = error.message || String(error); | 1508 var message = error.message || String(error); |
1803 var filename = (error.filename ? " " + error.filename: ""); | 1509 var filename = (error.filename ? " " + error.filename: ""); |
1804 // FIXME: Display worker error states separately from main document | 1510 // FIXME: Display worker error states separately from main document |
1805 // error state. | 1511 // error state. |
1806 this.worker_done({ | 1512 this.worker_done({ |
1807 status: { | 1513 status: { |
1808 status: tests.status.ERROR, | 1514 status: tests.status.ERROR, |
1809 message: "Error in worker" + filename + ": " + message | 1515 message: "Error in worker" + filename + ": " + message, |
| 1516 stack: error.stack |
1810 } | 1517 } |
1811 }); | 1518 }); |
1812 error.preventDefault(); | 1519 error.preventDefault(); |
1813 }; | 1520 }; |
1814 | 1521 |
1815 RemoteWorker.prototype.test_state = function(data) { | 1522 RemoteWorker.prototype.test_state = function(data) { |
1816 var remote_test = this.tests[data.test.index]; | 1523 var remote_test = this.tests[data.test.index]; |
1817 if (!remote_test) { | 1524 if (!remote_test) { |
1818 remote_test = new RemoteTest(data.test); | 1525 remote_test = new RemoteTest(data.test); |
1819 this.tests[data.test.index] = remote_test; | 1526 this.tests[data.test.index] = remote_test; |
1820 } | 1527 } |
1821 remote_test.update_state_from(data.test); | 1528 remote_test.update_state_from(data.test); |
1822 tests.notify_test_state(remote_test); | 1529 tests.notify_test_state(remote_test); |
1823 }; | 1530 }; |
1824 | 1531 |
1825 RemoteWorker.prototype.test_done = function(data) { | 1532 RemoteWorker.prototype.test_done = function(data) { |
1826 var remote_test = this.tests[data.test.index]; | 1533 var remote_test = this.tests[data.test.index]; |
1827 remote_test.update_state_from(data.test); | 1534 remote_test.update_state_from(data.test); |
1828 remote_test.done(); | 1535 remote_test.done(); |
1829 tests.result(remote_test); | 1536 tests.result(remote_test); |
1830 }; | 1537 }; |
1831 | 1538 |
1832 RemoteWorker.prototype.worker_done = function(data) { | 1539 RemoteWorker.prototype.worker_done = function(data) { |
1833 if (tests.status.status === null && | 1540 if (tests.status.status === null && |
1834 data.status.status !== data.status.OK) { | 1541 data.status.status !== data.status.OK) { |
1835 tests.status.status = data.status.status; | 1542 tests.status.status = data.status.status; |
1836 tests.status.message = data.status.message; | 1543 tests.status.message = data.status.message; |
| 1544 tests.status.stack = data.status.stack; |
1837 } | 1545 } |
1838 this.running = false; | 1546 this.running = false; |
1839 this.worker = null; | 1547 this.worker = null; |
1840 if (tests.all_done()) { | 1548 if (tests.all_done()) { |
1841 tests.complete(); | 1549 tests.complete(); |
1842 } | 1550 } |
1843 }; | 1551 }; |
1844 | 1552 |
1845 RemoteWorker.prototype.message_handlers = { | 1553 RemoteWorker.prototype.message_handlers = { |
1846 test_state: RemoteWorker.prototype.test_state, | 1554 test_state: RemoteWorker.prototype.test_state, |
1847 result: RemoteWorker.prototype.test_done, | 1555 result: RemoteWorker.prototype.test_done, |
1848 complete: RemoteWorker.prototype.worker_done | 1556 complete: RemoteWorker.prototype.worker_done |
1849 }; | 1557 }; |
1850 | 1558 |
1851 /* | 1559 /* |
1852 * Harness | 1560 * Harness |
1853 */ | 1561 */ |
1854 | 1562 |
1855 function TestsStatus() | 1563 function TestsStatus() |
1856 { | 1564 { |
1857 this.status = null; | 1565 this.status = null; |
1858 this.message = null; | 1566 this.message = null; |
| 1567 this.stack = null; |
1859 } | 1568 } |
1860 | 1569 |
1861 TestsStatus.statuses = { | 1570 TestsStatus.statuses = { |
1862 OK:0, | 1571 OK:0, |
1863 ERROR:1, | 1572 ERROR:1, |
1864 TIMEOUT:2 | 1573 TIMEOUT:2 |
1865 }; | 1574 }; |
1866 | 1575 |
1867 TestsStatus.prototype = merge({}, TestsStatus.statuses); | 1576 TestsStatus.prototype = merge({}, TestsStatus.statuses); |
1868 | 1577 |
1869 TestsStatus.prototype.structured_clone = function() | 1578 TestsStatus.prototype.structured_clone = function() |
1870 { | 1579 { |
1871 if (!this._structured_clone) { | 1580 if (!this._structured_clone) { |
1872 var msg = this.message; | 1581 var msg = this.message; |
1873 msg = msg ? String(msg) : msg; | 1582 msg = msg ? String(msg) : msg; |
1874 this._structured_clone = merge({ | 1583 this._structured_clone = merge({ |
1875 status:this.status, | 1584 status:this.status, |
1876 message:msg | 1585 message:msg, |
| 1586 stack:this.stack |
1877 }, TestsStatus.statuses); | 1587 }, TestsStatus.statuses); |
1878 } | 1588 } |
1879 return this._structured_clone; | 1589 return this._structured_clone; |
1880 }; | 1590 }; |
1881 | 1591 |
1882 function Tests() | 1592 function Tests() |
1883 { | 1593 { |
1884 this.tests = []; | 1594 this.tests = []; |
1885 this.num_pending = 0; | 1595 this.num_pending = 0; |
1886 | 1596 |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1956 } | 1666 } |
1957 } | 1667 } |
1958 } | 1668 } |
1959 | 1669 |
1960 if (func) { | 1670 if (func) { |
1961 try { | 1671 try { |
1962 func(); | 1672 func(); |
1963 } catch (e) { | 1673 } catch (e) { |
1964 this.status.status = this.status.ERROR; | 1674 this.status.status = this.status.ERROR; |
1965 this.status.message = String(e); | 1675 this.status.message = String(e); |
| 1676 this.status.stack = e.stack ? e.stack : null; |
1966 } | 1677 } |
1967 } | 1678 } |
1968 this.set_timeout(); | 1679 this.set_timeout(); |
1969 }; | 1680 }; |
1970 | 1681 |
1971 Tests.prototype.set_file_is_test = function() { | 1682 Tests.prototype.set_file_is_test = function() { |
1972 if (this.tests.length > 0) { | 1683 if (this.tests.length > 0) { |
1973 throw new Error("Tried to set file as test after creating a test"); | 1684 throw new Error("Tried to set file as test after creating a test"); |
1974 } | 1685 } |
1975 this.wait_for_finish = true; | 1686 this.wait_for_finish = true; |
(...skipping 337 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2313 ["p", {}, | 2024 ["p", {}, |
2314 "Harness status: ", | 2025 "Harness status: ", |
2315 ["span", {"class":status_class(s
tatus)}, | 2026 ["span", {"class":status_class(s
tatus)}, |
2316 status | 2027 status |
2317 ], | 2028 ], |
2318 ] | 2029 ] |
2319 ]]; | 2030 ]]; |
2320 | 2031 |
2321 if (harness_status.status === harness_status
.ERROR) { | 2032 if (harness_status.status === harness_status
.ERROR) { |
2322 rv[0].push(["pre", {}, harness_status.me
ssage]); | 2033 rv[0].push(["pre", {}, harness_status.me
ssage]); |
| 2034 if (harness_status.stack) { |
| 2035 rv[0].push(["pre", {}, harness_statu
s.stack]); |
| 2036 } |
2323 } | 2037 } |
2324 return rv; | 2038 return rv; |
2325 }, | 2039 }, |
2326 ["p", {}, "Found ${num_tests} tests"], | 2040 ["p", {}, "Found ${num_tests} tests"], |
2327 function() { | 2041 function() { |
2328 var rv = [["div", {}]]; | 2042 var rv = [["div", {}]]; |
2329 var i = 0; | 2043 var i = 0; |
2330 while (status_text.hasOwnProperty(i)) { | 2044 while (status_text.hasOwnProperty(i)) { |
2331 if (status_number.hasOwnProperty(status_
text[i])) { | 2045 if (status_number.hasOwnProperty(status_
text[i])) { |
2332 var status = status_text[i]; | 2046 var status = status_text[i]; |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2410 for (var i = 0; i < tests.length; i++) { | 2124 for (var i = 0; i < tests.length; i++) { |
2411 html += '<tr class="' + | 2125 html += '<tr class="' + |
2412 escape_html(status_class(status_text[tests[i].status])) + | 2126 escape_html(status_class(status_text[tests[i].status])) + |
2413 '"><td>' + | 2127 '"><td>' + |
2414 escape_html(status_text[tests[i].status]) + | 2128 escape_html(status_text[tests[i].status]) + |
2415 "</td><td>" + | 2129 "</td><td>" + |
2416 escape_html(tests[i].name) + | 2130 escape_html(tests[i].name) + |
2417 "</td><td>" + | 2131 "</td><td>" + |
2418 (assertions ? escape_html(get_assertion(tests[i])) + "</td><td>"
: "") + | 2132 (assertions ? escape_html(get_assertion(tests[i])) + "</td><td>"
: "") + |
2419 escape_html(tests[i].message ? tests[i].message : " ") + | 2133 escape_html(tests[i].message ? tests[i].message : " ") + |
| 2134 (tests[i].stack ? "<pre>" + |
| 2135 escape_html(tests[i].stack) + |
| 2136 "</pre>": "") + |
2420 "</td></tr>"; | 2137 "</td></tr>"; |
2421 } | 2138 } |
2422 html += "</tbody></table>"; | 2139 html += "</tbody></table>"; |
2423 try { | 2140 try { |
2424 log.lastChild.innerHTML = html; | 2141 log.lastChild.innerHTML = html; |
2425 } catch (e) { | 2142 } catch (e) { |
2426 log.appendChild(document.createElementNS(xhtml_ns, "p")) | 2143 log.appendChild(document.createElementNS(xhtml_ns, "p")) |
2427 .textContent = "Setting innerHTML for the log threw an exception.
"; | 2144 .textContent = "Setting innerHTML for the log threw an exception.
"; |
2428 log.appendChild(document.createElementNS(xhtml_ns, "pre")) | 2145 log.appendChild(document.createElementNS(xhtml_ns, "pre")) |
2429 .textContent = html; | 2146 .textContent = html; |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2605 if (expected_true !== true) { | 2322 if (expected_true !== true) { |
2606 var msg = make_message(function_name, description, | 2323 var msg = make_message(function_name, description, |
2607 error, substitutions); | 2324 error, substitutions); |
2608 throw new AssertionError(msg); | 2325 throw new AssertionError(msg); |
2609 } | 2326 } |
2610 } | 2327 } |
2611 | 2328 |
2612 function AssertionError(message) | 2329 function AssertionError(message) |
2613 { | 2330 { |
2614 this.message = message; | 2331 this.message = message; |
| 2332 this.stack = this.get_stack(); |
2615 } | 2333 } |
2616 | 2334 |
2617 AssertionError.prototype.toString = function() { | 2335 AssertionError.prototype = Object.create(Error.prototype); |
2618 return this.message; | 2336 |
2619 }; | 2337 AssertionError.prototype.get_stack = function() { |
| 2338 var lines = new Error().stack.split("\n"); |
| 2339 var rv = []; |
| 2340 var re = /\/resources\/testharness\.js/; |
| 2341 var i = 0; |
| 2342 // Fire remove any preamble that doesn't match the regexp |
| 2343 while (!re.test(lines[i])) { |
| 2344 i++ |
| 2345 } |
| 2346 // Then remove top frames in testharness.js itself |
| 2347 while (re.test(lines[i])) { |
| 2348 i++ |
| 2349 } |
| 2350 return lines.slice(i).join("\n"); |
| 2351 } |
2620 | 2352 |
2621 function make_message(function_name, description, error, substitutions) | 2353 function make_message(function_name, description, error, substitutions) |
2622 { | 2354 { |
2623 for (var p in substitutions) { | 2355 for (var p in substitutions) { |
2624 if (substitutions.hasOwnProperty(p)) { | 2356 if (substitutions.hasOwnProperty(p)) { |
2625 substitutions[p] = format_value(substitutions[p]); | 2357 substitutions[p] = format_value(substitutions[p]); |
2626 } | 2358 } |
2627 } | 2359 } |
2628 var node_form = substitute(["{text}", "${function_name}: ${description}"
+ error], | 2360 var node_form = substitute(["{text}", "${function_name}: ${description}"
+ error], |
2629 merge({function_name:function_name, | 2361 merge({function_name:function_name, |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2748 */ | 2480 */ |
2749 | 2481 |
2750 var tests = new Tests(); | 2482 var tests = new Tests(); |
2751 | 2483 |
2752 addEventListener("error", function(e) { | 2484 addEventListener("error", function(e) { |
2753 if (tests.file_is_test) { | 2485 if (tests.file_is_test) { |
2754 var test = tests.tests[0]; | 2486 var test = tests.tests[0]; |
2755 if (test.phase >= test.phases.HAS_RESULT) { | 2487 if (test.phase >= test.phases.HAS_RESULT) { |
2756 return; | 2488 return; |
2757 } | 2489 } |
2758 var message = e.message; | 2490 test.set_status(test.FAIL, e.message, e.stack); |
2759 test.set_status(test.FAIL, message); | |
2760 test.phase = test.phases.HAS_RESULT; | 2491 test.phase = test.phases.HAS_RESULT; |
2761 test.done(); | 2492 test.done(); |
2762 done(); | 2493 done(); |
2763 } else if (!tests.allow_uncaught_exception) { | 2494 } else if (!tests.allow_uncaught_exception) { |
2764 tests.status.status = tests.status.ERROR; | 2495 tests.status.status = tests.status.ERROR; |
2765 tests.status.message = e.message; | 2496 tests.status.message = e.message; |
| 2497 tests.status.stack = e.stack; |
2766 } | 2498 } |
2767 }); | 2499 }); |
2768 | 2500 |
2769 test_environment.on_tests_ready(); | 2501 test_environment.on_tests_ready(); |
2770 | 2502 |
2771 })(); | 2503 })(); |
2772 // vim: set expandtab shiftwidth=4 tabstop=4: | 2504 // vim: set expandtab shiftwidth=4 tabstop=4: |
OLD | NEW |