| 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 |