Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: LayoutTests/http/tests/w3c/resources/testharness.js

Issue 1061543006: Pull in the latest upstream testharness.js changes. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Rebase/tweaks for affected tests Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 <!doctype html>
27 <title></title>
28 <script src="/resources/testharness.js"></script>
29 <script src="/resources/testharnessreport.js"></script>
30
31 * Within each file one may define one or more tests. Each test is atomic
32 * in the sense that a single test has a single result (pass/fail/timeout).
33 * Within each test one may have a number of asserts. The test fails at the
34 * first failing assert, and the remainder of the test is (typically) not run.
35 *
36 * If the file containing the tests is a HTML file, a table containing the test
37 * results will be added to the document after all tests have run. By default th is
38 * will be added to a div element with id=log if it exists, or a new div element
39 * appended to document.body if it does not.
40 *
41 * NOTE: By default tests must be created before the load event fires. For ways
42 * to create tests after the load event, see "Determining when all tests
43 * are complete", below
44 *
45 * == Synchronous Tests ==
46 *
47 * To create a synchronous test use the test() function:
48 *
49 * test(test_function, name, properties)
50 *
51 * test_function is a function that contains the code to test. For example a
52 * trivial passing test would be:
53 *
54 * test(function() {assert_true(true)}, "assert_true with true")
55 *
56 * The function passed in is run in the test() call.
57 *
58 * properties is an object that overrides default test properties. The
59 * recognised properties are:
60 * timeout - the test timeout in ms
61 *
62 * e.g.
63 * test(test_function, "Sample test", {timeout:1000})
64 *
65 * would run test_function with a timeout of 1s.
66 *
67 * Additionally, test-specific metadata can be passed in the properties. These
68 * are used when the individual test has different metadata from that stored
69 * in the <head>.
70 * The recognized metadata properties are:
71 *
72 * help - The url or an array of urls of the part(s) of the specification(s)
73 * being tested
74 *
75 * assert - A human readable description of what the test is attempting
76 * to prove
77 *
78 * author - Name and contact information for the author of the test in the
79 * format: "Name <email_addr>" or "Name http://contact/url"
80 *
81 * == Asynchronous Tests ==
82 *
83 * Testing asynchronous features is somewhat more complex since the result of
84 * a test may depend on one or more events or other callbacks. The API provided
85 * for testing these features is indended to be rather low-level but hopefully
86 * applicable to many situations.
87 *
88 * To create a test, one starts by getting a Test object using async_test:
89 *
90 * async_test(name, properties)
91 *
92 * e.g.
93 * var t = async_test("Simple async test")
94 *
95 * Assertions can be added to the test by calling the step method of the test
96 * object with a function containing the test assertions:
97 *
98 * t.step(function() {assert_true(true)});
99 *
100 * When all the steps are complete, the done() method must be called:
101 *
102 * t.done();
103 *
104 * As a convenience, async_test can also takes a function as first argument.
105 * This function is called with the test object as both its `this` object and
106 * first argument. The above example can be rewritten as:
107 *
108 * async_test(function(t) {
109 * object.some_event = function() {
110 * t.step(function (){assert_true(true); t.done();});
111 * };
112 * }, "Simple async test");
113 *
114 * which avoids cluttering the global scope with references to async
115 * tests instances.
116 *
117 * The properties argument is identical to that for test().
118 *
119 * In many cases it is convenient to run a step in response to an event or a
120 * callback. A convenient method of doing this is through the step_func method
121 * which returns a function that, when called runs a test step. For example
122 *
123 * object.some_event = t.step_func(function(e) {assert_true(e.a)});
124 *
125 * For asynchronous callbacks that should never execute, unreached_func can
126 * be used. For example:
127 *
128 * object.some_event = t.unreached_func("some_event should not fire");
129 *
130 * == Single Page Tests ==
131 *
132 * Sometimes, particularly when dealing with asynchronous behaviour,
133 * having exactly one test per page is desirable, and the overhead of
134 * wrapping everything in functions for isolation becomes
135 * burdensome. For these cases testharness.js support "single page
136 * tests".
137 *
138 * In order for a test to be interpreted as a "single page" test, the
139 * it must simply not call test() or async_test() anywhere on the page, and
140 * must call the done() function to indicate that the test is complete. All
141 * the assert_* functions are avaliable as normal, but are called without
142 * the normal step function wrapper. For example:
143 *
144 * <!doctype html>
145 * <title>Example single-page test</title>
146 * <script src="/resources/testharness.js"></script>
147 * <script src="/resources/testharnessreport.js"></script>
148 * <body>
149 * <script>
150 * assert_equals(document.body, document.getElementsByTagName("body")[0])
151 * done()
152 * </script>
153 *
154 * The test title for sinple page tests is always taken from document.title.
155 *
156 * == Making assertions ==
157 *
158 * Functions for making assertions start assert_
159 * The best way to get a list is to look in this file for functions names
160 * matching that pattern. The general signature is
161 *
162 * assert_something(actual, expected, description)
163 *
164 * although not all assertions precisely match this pattern e.g. assert_true
165 * only takes actual and description as arguments.
166 *
167 * The description parameter is used to present more useful error messages when
168 * a test fails
169 *
170 * NOTE: All asserts must be located in a test() or a step of an async_test().
171 * asserts outside these places won't be detected correctly by the harness
172 * and may cause a file to stop testing.
173 *
174 * == Cleanup ==
175 *
176 * Occasionally tests may create state that will persist beyond the test itself.
177 * In order to ensure that tests are independent, such state should be cleaned
178 * up once the test has a result. This can be achieved by adding cleanup
179 * callbacks to the test. Such callbacks are registered using the add_cleanup
180 * function on the test object. All registered callbacks will be run as soon as
181 * the test result is known. For example
182 * test(function() {
183 * window.some_global = "example";
184 * this.add_cleanup(function() {delete window.some_global});
185 * assert_true(false);
186 * });
187 *
188 * == Harness Timeout ==
189 *
190 * The overall harness admits two timeout values "normal" (the
191 * default) and "long", used for tests which have an unusually long
192 * runtime. After the timeout is reached, the harness will stop
193 * waiting for further async tests to complete. By default the
194 * timeouts are set to 10s and 60s, respectively, but may be changed
195 * when the test is run on hardware with different performance
196 * characteristics to a common desktop computer. In order to opt-in
197 * to the longer test timeout, the test must specify a meta element:
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 * NOTE: this functionality may be removed
256 *
257 * There are scenarios in which is is desirable to create a large number of
258 * (synchronous) tests that are internally similar but vary in the parameters
259 * used. To make this easier, the generate_tests function allows a single
260 * function to be called with each set of parameters in a list:
261 *
262 * generate_tests(test_function, parameter_lists, properties)
263 *
264 * For example:
265 *
266 * generate_tests(assert_equals, [
267 * ["Sum one and one", 1+1, 2],
268 * ["Sum one and zero", 1+0, 1]
269 * ])
270 *
271 * Is equivalent to:
272 *
273 * test(function() {assert_equals(1+1, 2)}, "Sum one and one")
274 * test(function() {assert_equals(1+0, 1)}, "Sum one and zero")
275 *
276 * Note that the first item in each parameter list corresponds to the name of
277 * the test.
278 *
279 * The properties argument is identical to that for test(). This may be a
280 * single object (used for all generated tests) or an array.
281 *
282 * == Callback API ==
283 *
284 * The framework provides callbacks corresponding to 3 events:
285 *
286 * start - happens when the first Test is created
287 * result - happens when a test result is recieved
288 * complete - happens when all results are recieved
289 *
290 * The page defining the tests may add callbacks for these events by calling
291 * the following methods:
292 *
293 * add_start_callback(callback) - callback called with no arguments
294 * add_result_callback(callback) - callback called with a test argument
295 * add_completion_callback(callback) - callback called with an array of tests
296 * and an status object
297 *
298 * tests have the following properties:
299 * status: A status code. This can be compared to the PASS, FAIL, TIMEOUT and
300 * NOTRUN properties on the test object
301 * message: A message indicating the reason for failure. In the future this
302 * will always be a string
303 *
304 * The status object gives the overall status of the harness. It has the
305 * following properties:
306 * status: Can be compared to the OK, ERROR and TIMEOUT properties
307 * message: An error message set when the status is ERROR
308 *
309 * == External API ==
310 *
311 * In order to collect the results of multiple pages containing tests, the test
312 * harness will, when loaded in a nested browsing context, attempt to call
313 * certain functions in each ancestor and opener browsing context:
314 *
315 * start - start_callback
316 * result - result_callback
317 * complete - completion_callback
318 *
319 * These are given the same arguments as the corresponding internal callbacks
320 * described above.
321 *
322 * == External API through cross-document messaging ==
323 *
324 * Where supported, the test harness will also send messages using
325 * cross-document messaging to each ancestor and opener browsing context. Since
326 * it uses the wildcard keyword (*), cross-origin communication is enabled and
327 * script on different origins can collect the results.
328 *
329 * This API follows similar conventions as those described above only slightly
330 * modified to accommodate message event API. Each message is sent by the harnes s
331 * is passed a single vanilla object, available as the `data` property of the
332 * event object. These objects are structures as follows:
333 *
334 * start - { type: "start" }
335 * result - { type: "result", test: Test }
336 * complete - { type: "complete", tests: [Test, ...], status: TestsStatus }
337 *
338 * == List of assertions ==
339 *
340 * assert_true(actual, description)
341 * asserts that /actual/ is strictly true
342 *
343 * assert_false(actual, description)
344 * asserts that /actual/ is strictly false
345 *
346 * assert_equals(actual, expected, description)
347 * asserts that /actual/ is the same value as /expected/
348 *
349 * assert_not_equals(actual, expected, description)
350 * asserts that /actual/ is a different value to /expected/. Yes, this means
351 * that "expected" is a misnomer
352 *
353 * assert_in_array(actual, expected, description)
354 * asserts that /expected/ is an Array, and /actual/ is equal to one of the
355 * members -- expected.indexOf(actual) != -1
356 *
357 * assert_array_equals(actual, expected, description)
358 * asserts that /actual/ and /expected/ have the same length and the value of
359 * each indexed property in /actual/ is the strictly equal to the correspondin g
360 * property value in /expected/
361 *
362 * assert_approx_equals(actual, expected, epsilon, description)
363 * asserts that /actual/ is a number within +/- /epsilon/ of /expected/
364 *
365 * assert_less_than(actual, expected, description)
366 * asserts that /actual/ is a number less than /expected/
367 *
368 * assert_greater_than(actual, expected, description)
369 * asserts that /actual/ is a number greater than /expected/
370 *
371 * assert_less_than_equal(actual, expected, description)
372 * asserts that /actual/ is a number less than or equal to /expected/
373 *
374 * assert_greater_than_equal(actual, expected, description)
375 * asserts that /actual/ is a number greater than or equal to /expected/
376 *
377 * assert_regexp_match(actual, expected, description)
378 * asserts that /actual/ matches the regexp /expected/
379 *
380 * assert_class_string(object, class_name, description)
381 * asserts that the class string of /object/ as returned in
382 * Object.prototype.toString is equal to /class_name/.
383 *
384 * assert_own_property(object, property_name, description)
385 * assert that object has own property property_name
386 *
387 * assert_inherits(object, property_name, description)
388 * assert that object does not have an own property named property_name
389 * but that property_name is present in the prototype chain for object
390 *
391 * assert_idl_attribute(object, attribute_name, description)
392 * assert that an object that is an instance of some interface has the
393 * attribute attribute_name following the conditions specified by WebIDL
394 *
395 * assert_readonly(object, property_name, description)
396 * assert that property property_name on object is readonly
397 *
398 * assert_throws(code, func, description)
399 * code - the expected exception:
400 * o string: the thrown exception must be a DOMException with the given
401 * name, e.g., "TimeoutError" (for compatibility with existing
402 * tests, a constant is also supported, e.g., "TIMEOUT_ERR")
403 * o object: the thrown exception must have a property called "name" that
404 * matches code.name
405 * o null: allow any exception (in general, one of the options above
406 * should be used)
407 * func - a function that should throw
408 *
409 * assert_unreached(description)
410 * asserts if called. Used to ensure that some codepath is *not* taken e.g.
411 * an event does not fire.
412 *
413 * assert_any(assert_func, actual, expected_array, extra_arg_1, ... extra_arg_N)
414 * asserts that one assert_func(actual, expected_array_N, extra_arg1, ..., ext ra_arg_N)
415 * is true for some expected_array_N in expected_array. This only works for as sert_func
416 * with signature assert_func(actual, expected, args_1, ..., args_N). Note tha t tests
417 * with multiple allowed pass conditions are bad practice unless the spec spec ifically
418 * allows multiple behaviours. Test authors should not use this method simply to hide
419 * UA bugs.
420 *
421 * assert_exists(object, property_name, description)
422 * *** deprecated ***
423 * asserts that object has an own property property_name
424 *
425 * assert_not_exists(object, property_name, description)
426 * *** deprecated ***
427 * assert that object does not have own property property_name
428 */
429 14
430 (function () 15 (function ()
431 { 16 {
432 var debug = false; 17 var debug = false;
433 // default timeout is 5 minutes, to let LayoutTests timeout first 18 // default timeout is 10 seconds, test can override if needed
434 var settings = { 19 var settings = {
435 output:true, 20 output:true,
436 harness_timeout:{ 21 harness_timeout:{
437 "normal":300000, 22 "normal":10000,
438 "long":300000 23 "long":60000
439 }, 24 },
440 test_timeout:null 25 test_timeout:null
441 }; 26 };
442 27
443 var xhtml_ns = "http://www.w3.org/1999/xhtml"; 28 var xhtml_ns = "http://www.w3.org/1999/xhtml";
444 29
445 // script_prefix is used by Output.prototype.show_results() to figure out 30 /*
446 // where to get testharness.css from. It's enclosed in an extra closure to 31 * TestEnvironment is an abstraction for the environment in which the test
447 // not pollute the library's namespace with variables like "src". 32 * harness is used. Each implementation of a test environment has to provide
448 var script_prefix = null; 33 * the following interface:
449 (function () 34 *
450 { 35 * interface TestEnvironment {
451 var scripts = document.getElementsByTagName("script"); 36 * // Invoked after the global 'tests' object has been created and it's
452 for (var i = 0; i < scripts.length; i++) { 37 * // safe to call add_*_callback() to register event handlers.
453 var src; 38 * void on_tests_ready();
454 if (scripts[i].src) { 39 *
455 src = scripts[i].src; 40 * // Invoked after setup() has been called to notify the test environment
456 } else if (scripts[i].href) { 41 * // of changes to the test harness properties.
457 //SVG case 42 * void on_new_harness_properties(object properties);
458 src = scripts[i].href.baseVal; 43 *
44 * // Should return a new unique default test name.
45 * DOMString next_default_test_name();
46 *
47 * // Should return the test harness timeout duration in milliseconds.
48 * float test_timeout();
49 *
50 * // Should return the global scope object.
51 * object global_scope();
52 * };
53 */
54
55 /*
56 * A test environment with a DOM. The global object is 'window'. By default
57 * test results are displayed in a table. Any parent windows receive
58 * callbacks or messages via postMessage() when test events occur. See
59 * apisample11.html and apisample12.html.
60 */
61 function WindowTestEnvironment() {
62 this.name_counter = 0;
63 this.window_cache = null;
64 this.output_handler = null;
65 this.all_loaded = false;
66 var this_obj = this;
67 on_event(window, 'load', function() {
68 this_obj.all_loaded = true;
69 });
70 }
71
72 WindowTestEnvironment.prototype._dispatch = function(selector, callback_args , message_arg) {
73 this._forEach_windows(
74 function(w, same_origin) {
75 if (same_origin) {
76 try {
77 var has_selector = selector in w;
78 } catch(e) {
79 // If document.domain was set at some point same_ori gin can be
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 }
90 }
91 }
92 }
93 if (supports_post_message(w) && w !== self) {
94 w.postMessage(message_arg, "*");
95 }
96 });
97 };
98
99 WindowTestEnvironment.prototype._forEach_windows = function(callback) {
100 // Iterate of the the windows [self ... top, opener]. The callback is pa ssed
101 // two objects, the first one is the windows object itself, the second o ne
102 // is a boolean indicating whether or not its on the same origin as the
103 // current window.
104 var cache = this.window_cache;
105 if (!cache) {
106 cache = [[self, true]];
107 var w = self;
108 var i = 0;
109 var so;
110 var origins = location.ancestorOrigins;
111 while (w != w.parent) {
112 w = w.parent;
113 // In WebKit, calls to parent windows' properties that aren't on the same
114 // origin cause an error message to be displayed in the error co nsole but
115 // don't throw an exception. This is a deviation from the curren t HTML5
116 // spec. See: https://bugs.webkit.org/show_bug.cgi?id=43504
117 // The problem with WebKit's behavior is that it pollutes the er ror console
118 // with error messages that can't be caught.
119 //
120 // This issue can be mitigated by relying on the (for now) propr ietary
121 // `location.ancestorOrigins` property which returns an ordered list of
122 // the origins of enclosing windows. See:
123 // http://trac.webkit.org/changeset/113945.
124 if (origins) {
125 so = (location.origin == origins[i]);
126 } else {
127 so = is_same_origin(w);
128 }
129 cache.push([w, so]);
130 i++;
459 } 131 }
460 132 w = window.opener;
461 if (src && src.slice(src.length - "testharness.js".length) === "test harness.js") { 133 if (w) {
462 script_prefix = src.slice(0, src.length - "testharness.js".lengt h); 134 // window.opener isn't included in the `location.ancestorOrigins ` prop.
463 break; 135 // We'll just have to deal with a simple check and an error msg on WebKit
136 // browsers in this case.
137 cache.push([w, is_same_origin(w)]);
464 } 138 }
465 } 139 this.window_cache = cache;
466 })(); 140 }
467 141
468 /* 142 forEach(cache,
469 * API functions 143 function(a) {
470 */ 144 callback.apply(null, a);
471 145 });
472 var name_counter = 0; 146 };
473 function next_default_name() 147
474 { 148 WindowTestEnvironment.prototype.on_tests_ready = function() {
149 var output = new Output();
150 this.output_handler = output;
151
152 var this_obj = this;
153 add_start_callback(function (properties) {
154 this_obj.output_handler.init(properties);
155 this_obj._dispatch("start_callback", [properties],
156 { type: "start", properties: properties });
157 });
158 add_test_state_callback(function(test) {
159 this_obj.output_handler.show_status();
160 this_obj._dispatch("test_state_callback", [test],
161 { type: "test_state", test: test.structured_clone () });
162 });
163 add_result_callback(function (test) {
164 this_obj.output_handler.show_status();
165 this_obj._dispatch("result_callback", [test],
166 { type: "result", test: test.structured_clone() } );
167 });
168 add_completion_callback(function (tests, harness_status) {
169 this_obj.output_handler.show_results(tests, harness_status);
170 var cloned_tests = map(tests, function(test) { return test.structure d_clone(); });
171 this_obj._dispatch("completion_callback", [tests, harness_status],
172 { type: "complete", tests: cloned_tests,
173 status: harness_status.structured_clone() });
174 });
175 };
176
177 WindowTestEnvironment.prototype.next_default_test_name = function() {
475 //Don't use document.title to work around an Opera bug in XHTML document s 178 //Don't use document.title to work around an Opera bug in XHTML document s
476 var title = document.getElementsByTagName("title")[0]; 179 var title = document.getElementsByTagName("title")[0];
477 var prefix = (title && title.firstChild && title.firstChild.data) || "Un titled"; 180 var prefix = (title && title.firstChild && title.firstChild.data) || "Un titled";
478 var suffix = name_counter > 0 ? " " + name_counter : ""; 181 var suffix = this.name_counter > 0 ? " " + this.name_counter : "";
479 name_counter++; 182 this.name_counter++;
480 return prefix + suffix; 183 return prefix + suffix;
481 } 184 };
185
186 WindowTestEnvironment.prototype.on_new_harness_properties = function(propert ies) {
187 this.output_handler.setup(properties);
188 };
189
190 WindowTestEnvironment.prototype.add_on_loaded_callback = function(callback) {
191 on_event(window, 'load', callback);
192 };
193
194 WindowTestEnvironment.prototype.test_timeout = function() {
195 var metas = document.getElementsByTagName("meta");
196 for (var i = 0; i < metas.length; i++) {
197 if (metas[i].name == "timeout") {
198 if (metas[i].content == "long") {
199 return settings.harness_timeout.long;
200 }
201 break;
202 }
203 }
204 return settings.harness_timeout.normal;
205 };
206
207 WindowTestEnvironment.prototype.global_scope = function() {
208 return window;
209 };
210
211 /*
212 * Base TestEnvironment implementation for a generic web worker.
213 *
214 * Workers accumulate test results. One or more clients can connect and
215 * retrieve results from a worker at any time.
216 *
217 * WorkerTestEnvironment supports communicating with a client via a
218 * MessagePort. The mechanism for determining the appropriate MessagePort
219 * for communicating with a client depends on the type of worker and is
220 * implemented by the various specializations of WorkerTestEnvironment
221 * below.
222 *
223 * A client document using testharness can use fetch_tests_from_worker() to
224 * retrieve results from a worker. See apisample16.html.
225 */
226 function WorkerTestEnvironment() {
227 this.name_counter = 0;
228 this.all_loaded = true;
229 this.message_list = [];
230 this.message_ports = [];
231 }
232
233 WorkerTestEnvironment.prototype._dispatch = function(message) {
234 this.message_list.push(message);
235 for (var i = 0; i < this.message_ports.length; ++i)
236 {
237 this.message_ports[i].postMessage(message);
238 }
239 };
240
241 // The only requirement is that port has a postMessage() method. It doesn't
242 // have to be an instance of a MessagePort, and often isn't.
243 WorkerTestEnvironment.prototype._add_message_port = function(port) {
244 this.message_ports.push(port);
245 for (var i = 0; i < this.message_list.length; ++i)
246 {
247 port.postMessage(this.message_list[i]);
248 }
249 };
250
251 WorkerTestEnvironment.prototype.next_default_test_name = function() {
252 var suffix = this.name_counter > 0 ? " " + this.name_counter : "";
253 this.name_counter++;
254 return "Untitled" + suffix;
255 };
256
257 WorkerTestEnvironment.prototype.on_new_harness_properties = function() {};
258
259 WorkerTestEnvironment.prototype.on_tests_ready = function() {
260 var this_obj = this;
261 add_start_callback(
262 function(properties) {
263 this_obj._dispatch({
264 type: "start",
265 properties: properties,
266 });
267 });
268 add_test_state_callback(
269 function(test) {
270 this_obj._dispatch({
271 type: "test_state",
272 test: test.structured_clone()
273 });
274 });
275 add_result_callback(
276 function(test) {
277 this_obj._dispatch({
278 type: "result",
279 test: test.structured_clone()
280 });
281 });
282 add_completion_callback(
283 function(tests, harness_status) {
284 this_obj._dispatch({
285 type: "complete",
286 tests: map(tests,
287 function(test) {
288 return test.structured_clone();
289 }),
290 status: harness_status.structured_clone()
291 });
292 });
293 };
294
295 WorkerTestEnvironment.prototype.add_on_loaded_callback = function() {};
296
297 WorkerTestEnvironment.prototype.test_timeout = function() {
298 // Tests running in a worker don't have a default timeout. I.e. all
299 // worker tests behave as if settings.explicit_timeout is true.
300 return null;
301 };
302
303 WorkerTestEnvironment.prototype.global_scope = function() {
304 return self;
305 };
306
307 /*
308 * Dedicated web workers.
309 * https://html.spec.whatwg.org/multipage/workers.html#dedicatedworkerglobal scope
310 *
311 * This class is used as the test_environment when testharness is running
312 * inside a dedicated worker.
313 */
314 function DedicatedWorkerTestEnvironment() {
315 WorkerTestEnvironment.call(this);
316 // self is an instance of DedicatedWorkerGlobalScope which exposes
317 // a postMessage() method for communicating via the message channel
318 // established when the worker is created.
319 this._add_message_port(self);
320 }
321 DedicatedWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironme nt.prototype);
322
323 DedicatedWorkerTestEnvironment.prototype.on_tests_ready = function() {
324 WorkerTestEnvironment.prototype.on_tests_ready.call(this);
325 // In the absence of an onload notification, we a require dedicated
326 // workers to explicitly signal when the tests are done.
327 tests.wait_for_finish = true;
328 };
329
330 /*
331 * Shared web workers.
332 * https://html.spec.whatwg.org/multipage/workers.html#sharedworkerglobalsco pe
333 *
334 * This class is used as the test_environment when testharness is running
335 * inside a shared web worker.
336 */
337 function SharedWorkerTestEnvironment() {
338 WorkerTestEnvironment.call(this);
339 var this_obj = this;
340 // Shared workers receive message ports via the 'onconnect' event for
341 // each connection.
342 self.addEventListener("connect",
343 function(message_event) {
344 this_obj._add_message_port(message_event.source);
345 });
346 }
347 SharedWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment. prototype);
348
349 SharedWorkerTestEnvironment.prototype.on_tests_ready = function() {
350 WorkerTestEnvironment.prototype.on_tests_ready.call(this);
351 // In the absence of an onload notification, we a require shared
352 // workers to explicitly signal when the tests are done.
353 tests.wait_for_finish = true;
354 };
355
356 /*
357 * Service workers.
358 * http://www.w3.org/TR/service-workers/
359 *
360 * This class is used as the test_environment when testharness is running
361 * inside a service worker.
362 */
363 function ServiceWorkerTestEnvironment() {
364 WorkerTestEnvironment.call(this);
365 this.all_loaded = false;
366 this.on_loaded_callback = null;
367 var this_obj = this;
368 self.addEventListener("message",
369 function(event) {
370 if (event.data.type && event.data.type === "connect") {
371 this_obj._add_message_port(event.ports[0]);
372 event.ports[0].start();
373 }
374 });
375
376 // The oninstall event is received after the service worker script and
377 // all imported scripts have been fetched and executed. It's the
378 // equivalent of an onload event for a document. All tests should have
379 // been added by the time this event is received, thus it's not
380 // necessary to wait until the onactivate event.
381 on_event(self, "install",
382 function(event) {
383 this_obj.all_loaded = true;
384 if (this_obj.on_loaded_callback) {
385 this_obj.on_loaded_callback();
386 }
387 });
388 }
389 ServiceWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment .prototype);
390
391 ServiceWorkerTestEnvironment.prototype.add_on_loaded_callback = function(cal lback) {
392 if (this.all_loaded) {
393 callback();
394 } else {
395 this.on_loaded_callback = callback;
396 }
397 };
398
399 function create_test_environment() {
400 if ('document' in self) {
401 return new WindowTestEnvironment();
402 }
403 if ('DedicatedWorkerGlobalScope' in self &&
404 self instanceof DedicatedWorkerGlobalScope) {
405 return new DedicatedWorkerTestEnvironment();
406 }
407 if ('SharedWorkerGlobalScope' in self &&
408 self instanceof SharedWorkerGlobalScope) {
409 return new SharedWorkerTestEnvironment();
410 }
411 if ('ServiceWorkerGlobalScope' in self &&
412 self instanceof ServiceWorkerGlobalScope) {
413 return new ServiceWorkerTestEnvironment();
414 }
415 throw new Error("Unsupported test environment");
416 }
417
418 var test_environment = create_test_environment();
419
420 function is_shared_worker(worker) {
421 return 'SharedWorker' in self && worker instanceof SharedWorker;
422 }
423
424 function is_service_worker(worker) {
425 return 'ServiceWorker' in self && worker instanceof ServiceWorker;
426 }
427
428 /*
429 * API functions
430 */
482 431
483 function test(func, name, properties) 432 function test(func, name, properties)
484 { 433 {
485 var test_name = name ? name : next_default_name(); 434 var test_name = name ? name : test_environment.next_default_test_name();
486 properties = properties ? properties : {}; 435 properties = properties ? properties : {};
487 var test_obj = new Test(test_name, properties); 436 var test_obj = new Test(test_name, properties);
488 test_obj.step(func, test_obj, test_obj); 437 test_obj.step(func, test_obj, test_obj);
489 if (test_obj.phase === test_obj.phases.STARTED) { 438 if (test_obj.phase === test_obj.phases.STARTED) {
490 test_obj.done(); 439 test_obj.done();
491 } 440 }
492 } 441 }
493 442
494 function async_test(func, name, properties) 443 function async_test(func, name, properties)
495 { 444 {
496 if (typeof func !== "function") { 445 if (typeof func !== "function") {
497 properties = name; 446 properties = name;
498 name = func; 447 name = func;
499 func = null; 448 func = null;
500 } 449 }
501 var test_name = name ? name : next_default_name(); 450 var test_name = name ? name : test_environment.next_default_test_name();
502 properties = properties ? properties : {}; 451 properties = properties ? properties : {};
503 var test_obj = new Test(test_name, properties); 452 var test_obj = new Test(test_name, properties);
504 if (func) { 453 if (func) {
505 test_obj.step(func, test_obj, test_obj); 454 test_obj.step(func, test_obj, test_obj);
506 } 455 }
507 return test_obj; 456 return test_obj;
508 } 457 }
509 458
459 function promise_test(func, name, properties) {
460 var test = async_test(name, properties);
461 Promise.resolve(test.step(func, test, test))
462 .then(
463 function() {
464 test.done();
465 })
466 .catch(test.step_func(
467 function(value) {
468 if (value instanceof AssertionError) {
469 throw value;
470 }
471 assert(false, "promise_test", null,
472 "Unhandled rejection with value: ${value}", {value:va lue});
473 }));
474 }
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
510 function setup(func_or_properties, maybe_properties) 550 function setup(func_or_properties, maybe_properties)
511 { 551 {
512 var func = null; 552 var func = null;
513 var properties = {}; 553 var properties = {};
514 if (arguments.length === 2) { 554 if (arguments.length === 2) {
515 func = func_or_properties; 555 func = func_or_properties;
516 properties = maybe_properties; 556 properties = maybe_properties;
517 } else if (func_or_properties instanceof Function) { 557 } else if (func_or_properties instanceof Function) {
518 func = func_or_properties; 558 func = func_or_properties;
519 } else { 559 } else {
520 properties = func_or_properties; 560 properties = func_or_properties;
521 } 561 }
522 tests.setup(func, properties); 562 tests.setup(func, properties);
523 output.setup(properties); 563 test_environment.on_new_harness_properties(properties);
524 } 564 }
525 565
526 function done() { 566 function done() {
527 if (tests.tests.length === 0) { 567 if (tests.tests.length === 0) {
528 tests.set_file_is_test(); 568 tests.set_file_is_test();
529 } 569 }
530 if (tests.file_is_test) { 570 if (tests.file_is_test) {
531 tests.tests[0].done(); 571 tests.tests[0].done();
532 } 572 }
533 tests.end_wait(); 573 tests.end_wait();
(...skipping 12 matching lines...) Expand all
546 }); 586 });
547 } 587 }
548 588
549 function on_event(object, event, callback) 589 function on_event(object, event, callback)
550 { 590 {
551 object.addEventListener(event, callback, false); 591 object.addEventListener(event, callback, false);
552 } 592 }
553 593
554 expose(test, 'test'); 594 expose(test, 'test');
555 expose(async_test, 'async_test'); 595 expose(async_test, 'async_test');
596 expose(promise_test, 'promise_test');
597 expose(promise_rejects, 'promise_rejects');
556 expose(generate_tests, 'generate_tests'); 598 expose(generate_tests, 'generate_tests');
557 expose(setup, 'setup'); 599 expose(setup, 'setup');
558 expose(done, 'done'); 600 expose(done, 'done');
559 expose(on_event, 'on_event'); 601 expose(on_event, 'on_event');
560 602
561 /* 603 /*
562 * 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
563 * if it was longer. 605 * if it was longer.
564 */ 606 */
565 function truncate(s, len) 607 function truncate(s, len)
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after
809 function assert_array_equals(actual, expected, description) 851 function assert_array_equals(actual, expected, description)
810 { 852 {
811 assert(actual.length === expected.length, 853 assert(actual.length === expected.length,
812 "assert_array_equals", description, 854 "assert_array_equals", description,
813 "lengths differ, expected ${expected} got ${actual}", 855 "lengths differ, expected ${expected} got ${actual}",
814 {expected:expected.length, actual:actual.length}); 856 {expected:expected.length, actual:actual.length});
815 857
816 for (var i = 0; i < actual.length; i++) { 858 for (var i = 0; i < actual.length; i++) {
817 assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i), 859 assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i),
818 "assert_array_equals", description, 860 "assert_array_equals", description,
819 "property ${i}, property expected to be $expected but was $ac tual", 861 "property ${i}, property expected to be ${expected} but was $ {actual}",
820 {i:i, expected:expected.hasOwnProperty(i) ? "present" : "miss ing", 862 {i:i, expected:expected.hasOwnProperty(i) ? "present" : "miss ing",
821 actual:actual.hasOwnProperty(i) ? "present" : "missing"}); 863 actual:actual.hasOwnProperty(i) ? "present" : "missing"});
822 assert(same_value(expected[i], actual[i]), 864 assert(same_value(expected[i], actual[i]),
823 "assert_array_equals", description, 865 "assert_array_equals", description,
824 "property ${i}, expected ${expected} but got ${actual}", 866 "property ${i}, expected ${expected} but got ${actual}",
825 {i:i, expected:expected[i], actual:actual[i]}); 867 {i:i, expected:expected[i], actual:actual[i]});
826 } 868 }
827 } 869 }
828 expose(assert_array_equals, "assert_array_equals"); 870 expose(assert_array_equals, "assert_array_equals");
829 871
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
871 "expected a number but got a ${type_actual}", 913 "expected a number but got a ${type_actual}",
872 {type_actual:typeof actual}); 914 {type_actual:typeof actual});
873 915
874 assert(actual > expected, 916 assert(actual > expected,
875 "assert_greater_than", description, 917 "assert_greater_than", description,
876 "expected a number greater than ${expected} but got ${actual}", 918 "expected a number greater than ${expected} but got ${actual}",
877 {expected:expected, actual:actual}); 919 {expected:expected, actual:actual});
878 } 920 }
879 expose(assert_greater_than, "assert_greater_than"); 921 expose(assert_greater_than, "assert_greater_than");
880 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
881 function assert_less_than_equal(actual, expected, description) 941 function assert_less_than_equal(actual, expected, description)
882 { 942 {
883 /* 943 /*
884 * 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
885 */ 945 */
886 assert(typeof actual === "number", 946 assert(typeof actual === "number",
887 "assert_less_than_equal", description, 947 "assert_less_than_equal", description,
888 "expected a number but got a ${type_actual}", 948 "expected a number but got a ${type_actual}",
889 {type_actual:typeof actual}); 949 {type_actual:typeof actual});
890 950
891 assert(actual <= expected, 951 assert(actual <= expected,
892 "assert_less_than", description, 952 "assert_less_than_equal", description,
893 "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}",
894 {expected:expected, actual:actual}); 954 {expected:expected, actual:actual});
895 } 955 }
896 expose(assert_less_than_equal, "assert_less_than_equal"); 956 expose(assert_less_than_equal, "assert_less_than_equal");
897 957
898 function assert_greater_than_equal(actual, expected, description) 958 function assert_greater_than_equal(actual, expected, description)
899 { 959 {
900 /* 960 /*
901 * 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
902 */ 962 */
903 assert(typeof actual === "number", 963 assert(typeof actual === "number",
904 "assert_greater_than_equal", description, 964 "assert_greater_than_equal", description,
905 "expected a number but got a ${type_actual}", 965 "expected a number but got a ${type_actual}",
906 {type_actual:typeof actual}); 966 {type_actual:typeof actual});
907 967
908 assert(actual >= expected, 968 assert(actual >= expected,
909 "assert_greater_than_equal", description, 969 "assert_greater_than_equal", description,
910 "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}",
911 {expected:expected, actual:actual}); 971 {expected:expected, actual:actual});
912 } 972 }
913 expose(assert_greater_than_equal, "assert_greater_than_equal"); 973 expose(assert_greater_than_equal, "assert_greater_than_equal");
914 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
915 function assert_regexp_match(actual, expected, description) { 993 function assert_regexp_match(actual, expected, description) {
916 /* 994 /*
917 * Test if a string (actual) matches a regexp (expected) 995 * Test if a string (actual) matches a regexp (expected)
918 */ 996 */
919 assert(expected.test(actual), 997 assert(expected.test(actual),
920 "assert_regexp_match", description, 998 "assert_regexp_match", description,
921 "expected ${expected} but got ${actual}", 999 "expected ${expected} but got ${actual}",
922 {expected:expected, actual:actual}); 1000 {expected:expected, actual:actual});
923 } 1001 }
924 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
1056 TypeMismatchError: 17, 1134 TypeMismatchError: 17,
1057 SecurityError: 18, 1135 SecurityError: 18,
1058 NetworkError: 19, 1136 NetworkError: 19,
1059 AbortError: 20, 1137 AbortError: 20,
1060 URLMismatchError: 21, 1138 URLMismatchError: 21,
1061 QuotaExceededError: 22, 1139 QuotaExceededError: 22,
1062 TimeoutError: 23, 1140 TimeoutError: 23,
1063 InvalidNodeTypeError: 24, 1141 InvalidNodeTypeError: 24,
1064 DataCloneError: 25, 1142 DataCloneError: 25,
1065 1143
1144 EncodingError: 0,
1145 NotReadableError: 0,
1066 UnknownError: 0, 1146 UnknownError: 0,
1067 ConstraintError: 0, 1147 ConstraintError: 0,
1068 DataError: 0, 1148 DataError: 0,
1069 TransactionInactiveError: 0, 1149 TransactionInactiveError: 0,
1070 ReadOnlyError: 0, 1150 ReadOnlyError: 0,
1071 VersionError: 0 1151 VersionError: 0,
1152 OperationError: 0,
1072 }; 1153 };
1073 1154
1074 if (!(name in name_code_map)) { 1155 if (!(name in name_code_map)) {
1075 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()');
1076 } 1157 }
1077 1158
1078 var required_props = { code: name_code_map[name] }; 1159 var required_props = { code: name_code_map[name] };
1079 1160
1080 if (required_props.code === 0 || 1161 if (required_props.code === 0 ||
1081 ("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")) {
1082 // New style exception: also test the name property. 1166 // New style exception: also test the name property.
1083 required_props.name = name; 1167 required_props.name = name;
1084 } 1168 }
1085 1169
1086 //We'd like to test that e instanceof the appropriate interface, 1170 //We'd like to test that e instanceof the appropriate interface,
1087 //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
1088 //in. It might be an instanceof the appropriate interface on some 1172 //in. It might be an instanceof the appropriate interface on some
1089 //unknown other window. TODO: Work around this somehow? 1173 //unknown other window. TODO: Work around this somehow?
1090 1174
1091 assert(typeof e == "object", 1175 assert(typeof e == "object",
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
1130 } 1214 }
1131 expose(assert_any, "assert_any"); 1215 expose(assert_any, "assert_any");
1132 1216
1133 function Test(name, properties) 1217 function Test(name, properties)
1134 { 1218 {
1135 if (tests.file_is_test && tests.tests.length) { 1219 if (tests.file_is_test && tests.tests.length) {
1136 throw new Error("Tried to create a test with file_is_test"); 1220 throw new Error("Tried to create a test with file_is_test");
1137 } 1221 }
1138 this.name = name; 1222 this.name = name;
1139 1223
1140 this.phases = {
1141 INITIAL:0,
1142 STARTED:1,
1143 HAS_RESULT:2,
1144 COMPLETE:3
1145 };
1146 this.phase = this.phases.INITIAL; 1224 this.phase = this.phases.INITIAL;
1147 1225
1148 this.status = this.NOTRUN; 1226 this.status = this.NOTRUN;
1149 this.timeout_id = null; 1227 this.timeout_id = null;
1228 this.index = null;
1150 1229
1151 this.properties = properties; 1230 this.properties = properties;
1152 var timeout = properties.timeout ? properties.timeout : settings.test_ti meout; 1231 var timeout = properties.timeout ? properties.timeout : settings.test_ti meout;
1153 if (timeout != null) { 1232 if (timeout !== null) {
1154 this.timeout_length = timeout * tests.timeout_multiplier; 1233 this.timeout_length = timeout * tests.timeout_multiplier;
1155 } else { 1234 } else {
1156 this.timeout_length = null; 1235 this.timeout_length = null;
1157 } 1236 }
1158 1237
1159 this.message = null; 1238 this.message = null;
1239 this.stack = null;
1160 1240
1161 this.steps = []; 1241 this.steps = [];
1162 1242
1163 this.cleanup_callbacks = []; 1243 this.cleanup_callbacks = [];
1164 1244
1165 tests.push(this); 1245 tests.push(this);
1166 } 1246 }
1167 1247
1168 Test.statuses = { 1248 Test.statuses = {
1169 PASS:0, 1249 PASS:0,
1170 FAIL:1, 1250 FAIL:1,
1171 TIMEOUT:2, 1251 TIMEOUT:2,
1172 NOTRUN:3 1252 NOTRUN:3
1173 }; 1253 };
1174 1254
1175 Test.prototype = merge({}, Test.statuses); 1255 Test.prototype = merge({}, Test.statuses);
1176 1256
1257 Test.prototype.phases = {
1258 INITIAL:0,
1259 STARTED:1,
1260 HAS_RESULT:2,
1261 COMPLETE:3
1262 };
1263
1177 Test.prototype.structured_clone = function() 1264 Test.prototype.structured_clone = function()
1178 { 1265 {
1179 if (!this._structured_clone) { 1266 if (!this._structured_clone) {
1180 var msg = this.message; 1267 var msg = this.message;
1181 msg = msg ? String(msg) : msg; 1268 msg = msg ? String(msg) : msg;
1182 this._structured_clone = merge({ 1269 this._structured_clone = merge({
1183 name:String(this.name), 1270 name:String(this.name),
1184 status:this.status, 1271 properties:merge({}, this.properties),
1185 message:msg
1186 }, Test.statuses); 1272 }, Test.statuses);
1187 } 1273 }
1274 this._structured_clone.status = this.status;
1275 this._structured_clone.message = this.message;
1276 this._structured_clone.stack = this.stack;
1277 this._structured_clone.index = this.index;
1188 return this._structured_clone; 1278 return this._structured_clone;
1189 }; 1279 };
1190 1280
1191 Test.prototype.step = function(func, this_obj) 1281 Test.prototype.step = function(func, this_obj)
1192 { 1282 {
1193 if (this.phase > this.phases.STARTED) { 1283 if (this.phase > this.phases.STARTED) {
1194 return; 1284 return;
1195 } 1285 }
1196 this.phase = this.phases.STARTED; 1286 this.phase = this.phases.STARTED;
1197 //If we don't get a result before the harness times out that will be a t est timout 1287 //If we don't get a result before the harness times out that will be a t est timout
1198 this.set_status(this.TIMEOUT, "Test timed out"); 1288 this.set_status(this.TIMEOUT, "Test timed out");
1199 1289
1200 tests.started = true; 1290 tests.started = true;
1291 tests.notify_test_state(this);
1201 1292
1202 if (this.timeout_id === null) { 1293 if (this.timeout_id === null) {
1203 this.set_timeout(); 1294 this.set_timeout();
1204 } 1295 }
1205 1296
1206 this.steps.push(func); 1297 this.steps.push(func);
1207 1298
1208 if (arguments.length === 1) { 1299 if (arguments.length === 1) {
1209 this_obj = this; 1300 this_obj = this;
1210 } 1301 }
1211 1302
1212 try { 1303 try {
1213 return func.apply(this_obj, Array.prototype.slice.call(arguments, 2) ); 1304 return func.apply(this_obj, Array.prototype.slice.call(arguments, 2) );
1214 } catch (e) { 1305 } catch (e) {
1215 if (this.phase >= this.phases.HAS_RESULT) { 1306 if (this.phase >= this.phases.HAS_RESULT) {
1216 return; 1307 return;
1217 } 1308 }
1218 var message = (typeof e === "object" && e !== null) ? e.message : e; 1309 var message = String((typeof e === "object" && e !== null) ? e.messa ge : e);
1219 if (typeof e.stack != "undefined" && typeof e.message == "string") { 1310 var stack = e.stack ? e.stack : null;
1220 //Try to make it more informative for some exceptions, at least 1311
1221 //in Gecko and WebKit. This results in a stack dump instead of 1312 this.set_status(this.FAIL, message, stack);
1222 //just errors like "Cannot read property 'parentNode' of null"
1223 //or "root is null". Makes it a lot longer, of course.
1224 message += "(stack: " + e.stack + ")";
1225 }
1226 this.set_status(this.FAIL, message);
1227 this.phase = this.phases.HAS_RESULT; 1313 this.phase = this.phases.HAS_RESULT;
1228 this.done(); 1314 this.done();
1229 } 1315 }
1230 }; 1316 };
1231 1317
1232 Test.prototype.step_func = function(func, this_obj) 1318 Test.prototype.step_func = function(func, this_obj)
1233 { 1319 {
1234 var test_this = this; 1320 var test_this = this;
1235 1321
1236 if (arguments.length === 1) { 1322 if (arguments.length === 1) {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
1269 }); 1355 });
1270 }; 1356 };
1271 1357
1272 Test.prototype.add_cleanup = function(callback) { 1358 Test.prototype.add_cleanup = function(callback) {
1273 this.cleanup_callbacks.push(callback); 1359 this.cleanup_callbacks.push(callback);
1274 }; 1360 };
1275 1361
1276 Test.prototype.force_timeout = function() { 1362 Test.prototype.force_timeout = function() {
1277 this.set_status(this.TIMEOUT); 1363 this.set_status(this.TIMEOUT);
1278 this.phase = this.phases.HAS_RESULT; 1364 this.phase = this.phases.HAS_RESULT;
1279 } 1365 };
1280 1366
1281 Test.prototype.set_timeout = function() 1367 Test.prototype.set_timeout = function()
1282 { 1368 {
1283 if (this.timeout_length !== null) { 1369 if (this.timeout_length !== null) {
1284 var this_obj = this; 1370 var this_obj = this;
1285 this.timeout_id = setTimeout(function() 1371 this.timeout_id = setTimeout(function()
1286 { 1372 {
1287 this_obj.timeout(); 1373 this_obj.timeout();
1288 }, this.timeout_length); 1374 }, this.timeout_length);
1289 } 1375 }
1290 }; 1376 };
1291 1377
1292 Test.prototype.set_status = function(status, message) 1378 Test.prototype.set_status = function(status, message, stack)
1293 { 1379 {
1294 this.status = status; 1380 this.status = status;
1295 this.message = message; 1381 this.message = message;
1382 this.stack = stack ? stack : null;
1296 }; 1383 };
1297 1384
1298 Test.prototype.timeout = function() 1385 Test.prototype.timeout = function()
1299 { 1386 {
1300 this.timeout_id = null; 1387 this.timeout_id = null;
1301 this.set_status(this.TIMEOUT, "Test timed out"); 1388 this.set_status(this.TIMEOUT, "Test timed out");
1302 this.phase = this.phases.HAS_RESULT; 1389 this.phase = this.phases.HAS_RESULT;
1303 this.done(); 1390 this.done();
1304 }; 1391 };
1305 1392
1306 Test.prototype.done = function() 1393 Test.prototype.done = function()
1307 { 1394 {
1308 if (this.phase == this.phases.COMPLETE) { 1395 if (this.phase == this.phases.COMPLETE) {
1309 return; 1396 return;
1310 } 1397 }
1311 1398
1312 if (this.phase <= this.phases.STARTED) { 1399 if (this.phase <= this.phases.STARTED) {
1313 this.set_status(this.PASS, null); 1400 this.set_status(this.PASS, null);
1314 } 1401 }
1315 1402
1316 if (this.status == this.NOTRUN) {
1317 alert(this.phase);
1318 }
1319
1320 this.phase = this.phases.COMPLETE; 1403 this.phase = this.phases.COMPLETE;
1321 1404
1322 clearTimeout(this.timeout_id); 1405 clearTimeout(this.timeout_id);
1323 tests.result(this); 1406 tests.result(this);
1324 this.cleanup(); 1407 this.cleanup();
1325 }; 1408 };
1326 1409
1327 Test.prototype.cleanup = function() { 1410 Test.prototype.cleanup = function() {
1328 forEach(this.cleanup_callbacks, 1411 forEach(this.cleanup_callbacks,
1329 function(cleanup_callback) { 1412 function(cleanup_callback) {
1330 cleanup_callback(); 1413 cleanup_callback();
1331 }); 1414 });
1332 }; 1415 };
1333 1416
1334 /* 1417 /*
1418 * A RemoteTest object mirrors a Test object on a remote worker. The
1419 * associated RemoteWorker updates the RemoteTest object in response to
1420 * received events. In turn, the RemoteTest object replicates these events
1421 * on the local document. This allows listeners (test result reporting
1422 * etc..) to transparently handle local and remote events.
1423 */
1424 function RemoteTest(clone) {
1425 var this_obj = this;
1426 Object.keys(clone).forEach(
1427 function(key) {
1428 this_obj[key] = clone[key];
1429 });
1430 this.index = null;
1431 this.phase = this.phases.INITIAL;
1432 this.update_state_from(clone);
1433 tests.push(this);
1434 }
1435
1436 RemoteTest.prototype.structured_clone = function() {
1437 var clone = {};
1438 Object.keys(this).forEach(
1439 function(key) {
1440 if (typeof(this[key]) === "object") {
1441 clone[key] = merge({}, this[key]);
1442 } else {
1443 clone[key] = this[key];
1444 }
1445 });
1446 clone.phases = merge({}, this.phases);
1447 return clone;
1448 };
1449
1450 RemoteTest.prototype.cleanup = function() {};
1451 RemoteTest.prototype.phases = Test.prototype.phases;
1452 RemoteTest.prototype.update_state_from = function(clone) {
1453 this.status = clone.status;
1454 this.message = clone.message;
1455 this.stack = clone.stack;
1456 if (this.phase === this.phases.INITIAL) {
1457 this.phase = this.phases.STARTED;
1458 }
1459 };
1460 RemoteTest.prototype.done = function() {
1461 this.phase = this.phases.COMPLETE;
1462 }
1463
1464 /*
1465 * A RemoteWorker listens for test events from a worker. These events are
1466 * then used to construct and maintain RemoteTest objects that mirror the
1467 * tests running on the remote worker.
1468 */
1469 function RemoteWorker(worker) {
1470 this.running = true;
1471 this.tests = new Array();
1472
1473 var this_obj = this;
1474 worker.onerror = function(error) { this_obj.worker_error(error); };
1475
1476 var message_port;
1477
1478 if (is_service_worker(worker)) {
1479 // The ServiceWorker's implicit MessagePort is currently not
1480 // reliably accessible from the ServiceWorkerGlobalScope due to
1481 // Blink setting MessageEvent.source to null for messages sent via
1482 // ServiceWorker.postMessage(). Until that's resolved, create an
1483 // explicit MessageChannel and pass one end to the worker.
1484 var message_channel = new MessageChannel();
1485 message_port = message_channel.port1;
1486 message_port.start();
1487 worker.postMessage({type: "connect"}, [message_channel.port2]);
1488 } else if (is_shared_worker(worker)) {
1489 message_port = worker.port;
1490 } else {
1491 message_port = worker;
1492 }
1493
1494 // Keeping a reference to the worker until worker_done() is seen
1495 // prevents the Worker object and its MessageChannel from going away
1496 // before all the messages are dispatched.
1497 this.worker = worker;
1498
1499 message_port.onmessage =
1500 function(message) {
1501 if (this_obj.running && (message.data.type in this_obj.message_h andlers)) {
1502 this_obj.message_handlers[message.data.type].call(this_obj, message.data);
1503 }
1504 };
1505 }
1506
1507 RemoteWorker.prototype.worker_error = function(error) {
1508 var message = error.message || String(error);
1509 var filename = (error.filename ? " " + error.filename: "");
1510 // FIXME: Display worker error states separately from main document
1511 // error state.
1512 this.worker_done({
1513 status: {
1514 status: tests.status.ERROR,
1515 message: "Error in worker" + filename + ": " + message,
1516 stack: error.stack
1517 }
1518 });
1519 error.preventDefault();
1520 };
1521
1522 RemoteWorker.prototype.test_state = function(data) {
1523 var remote_test = this.tests[data.test.index];
1524 if (!remote_test) {
1525 remote_test = new RemoteTest(data.test);
1526 this.tests[data.test.index] = remote_test;
1527 }
1528 remote_test.update_state_from(data.test);
1529 tests.notify_test_state(remote_test);
1530 };
1531
1532 RemoteWorker.prototype.test_done = function(data) {
1533 var remote_test = this.tests[data.test.index];
1534 remote_test.update_state_from(data.test);
1535 remote_test.done();
1536 tests.result(remote_test);
1537 };
1538
1539 RemoteWorker.prototype.worker_done = function(data) {
1540 if (tests.status.status === null &&
1541 data.status.status !== data.status.OK) {
1542 tests.status.status = data.status.status;
1543 tests.status.message = data.status.message;
1544 tests.status.stack = data.status.stack;
1545 }
1546 this.running = false;
1547 this.worker = null;
1548 if (tests.all_done()) {
1549 tests.complete();
1550 }
1551 };
1552
1553 RemoteWorker.prototype.message_handlers = {
1554 test_state: RemoteWorker.prototype.test_state,
1555 result: RemoteWorker.prototype.test_done,
1556 complete: RemoteWorker.prototype.worker_done
1557 };
1558
1559 /*
1335 * Harness 1560 * Harness
1336 */ 1561 */
1337 1562
1338 function TestsStatus() 1563 function TestsStatus()
1339 { 1564 {
1340 this.status = null; 1565 this.status = null;
1341 this.message = null; 1566 this.message = null;
1567 this.stack = null;
1342 } 1568 }
1343 1569
1344 TestsStatus.statuses = { 1570 TestsStatus.statuses = {
1345 OK:0, 1571 OK:0,
1346 ERROR:1, 1572 ERROR:1,
1347 TIMEOUT:2 1573 TIMEOUT:2
1348 }; 1574 };
1349 1575
1350 TestsStatus.prototype = merge({}, TestsStatus.statuses); 1576 TestsStatus.prototype = merge({}, TestsStatus.statuses);
1351 1577
1352 TestsStatus.prototype.structured_clone = function() 1578 TestsStatus.prototype.structured_clone = function()
1353 { 1579 {
1354 if (!this._structured_clone) { 1580 if (!this._structured_clone) {
1355 var msg = this.message; 1581 var msg = this.message;
1356 msg = msg ? String(msg) : msg; 1582 msg = msg ? String(msg) : msg;
1357 this._structured_clone = merge({ 1583 this._structured_clone = merge({
1358 status:this.status, 1584 status:this.status,
1359 message:msg 1585 message:msg,
1586 stack:this.stack
1360 }, TestsStatus.statuses); 1587 }, TestsStatus.statuses);
1361 } 1588 }
1362 return this._structured_clone; 1589 return this._structured_clone;
1363 }; 1590 };
1364 1591
1365 function Tests() 1592 function Tests()
1366 { 1593 {
1367 this.tests = []; 1594 this.tests = [];
1368 this.num_pending = 0; 1595 this.num_pending = 0;
1369 1596
1370 this.phases = { 1597 this.phases = {
1371 INITIAL:0, 1598 INITIAL:0,
1372 SETUP:1, 1599 SETUP:1,
1373 HAVE_TESTS:2, 1600 HAVE_TESTS:2,
1374 HAVE_RESULTS:3, 1601 HAVE_RESULTS:3,
1375 COMPLETE:4 1602 COMPLETE:4
1376 }; 1603 };
1377 this.phase = this.phases.INITIAL; 1604 this.phase = this.phases.INITIAL;
1378 1605
1379 this.properties = {}; 1606 this.properties = {};
1380 1607
1381 //All tests can't be done until the load event fires
1382 this.all_loaded = false;
1383 this.wait_for_finish = false; 1608 this.wait_for_finish = false;
1384 this.processing_callbacks = false; 1609 this.processing_callbacks = false;
1385 1610
1386 this.allow_uncaught_exception = false; 1611 this.allow_uncaught_exception = false;
1387 1612
1388 this.file_is_test = false; 1613 this.file_is_test = false;
1389 1614
1390 this.timeout_multiplier = 1; 1615 this.timeout_multiplier = 1;
1391 this.timeout_length = this.get_timeout(); 1616 this.timeout_length = test_environment.test_timeout();
1392 this.timeout_id = null; 1617 this.timeout_id = null;
1393 1618
1394 this.start_callbacks = []; 1619 this.start_callbacks = [];
1620 this.test_state_callbacks = [];
1395 this.test_done_callbacks = []; 1621 this.test_done_callbacks = [];
1396 this.all_done_callbacks = []; 1622 this.all_done_callbacks = [];
1397 1623
1624 this.pending_workers = [];
1625
1398 this.status = new TestsStatus(); 1626 this.status = new TestsStatus();
1399 1627
1400 var this_obj = this; 1628 var this_obj = this;
1401 1629
1402 on_event(window, "load", 1630 test_environment.add_on_loaded_callback(function() {
1403 function() 1631 if (this_obj.all_done()) {
1404 { 1632 this_obj.complete();
1405 this_obj.all_loaded = true; 1633 }
1406 if (this_obj.all_done()) 1634 });
1407 {
1408 this_obj.complete();
1409 }
1410 });
1411 1635
1412 this.set_timeout(); 1636 this.set_timeout();
1413 } 1637 }
1414 1638
1415 Tests.prototype.setup = function(func, properties) 1639 Tests.prototype.setup = function(func, properties)
1416 { 1640 {
1417 if (this.phase >= this.phases.HAVE_RESULTS) { 1641 if (this.phase >= this.phases.HAVE_RESULTS) {
1418 return; 1642 return;
1419 } 1643 }
1420 1644
(...skipping 21 matching lines...) Expand all
1442 } 1666 }
1443 } 1667 }
1444 } 1668 }
1445 1669
1446 if (func) { 1670 if (func) {
1447 try { 1671 try {
1448 func(); 1672 func();
1449 } catch (e) { 1673 } catch (e) {
1450 this.status.status = this.status.ERROR; 1674 this.status.status = this.status.ERROR;
1451 this.status.message = String(e); 1675 this.status.message = String(e);
1676 this.status.stack = e.stack ? e.stack : null;
1452 } 1677 }
1453 } 1678 }
1454 this.set_timeout(); 1679 this.set_timeout();
1455 }; 1680 };
1456 1681
1457 Tests.prototype.set_file_is_test = function() { 1682 Tests.prototype.set_file_is_test = function() {
1458 if (this.tests.length > 0) { 1683 if (this.tests.length > 0) {
1459 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");
1460 } 1685 }
1461 this.wait_for_finish = true; 1686 this.wait_for_finish = true;
1462 this.file_is_test = true; 1687 this.file_is_test = true;
1463 // Create the test, which will add it to the list of tests 1688 // Create the test, which will add it to the list of tests
1464 async_test(); 1689 async_test();
1465 }; 1690 };
1466 1691
1467 Tests.prototype.get_timeout = function() {
1468 var metas = document.getElementsByTagName("meta");
1469 for (var i = 0; i < metas.length; i++) {
1470 if (metas[i].name == "timeout") {
1471 if (metas[i].content == "long") {
1472 return settings.harness_timeout.long;
1473 }
1474 break;
1475 }
1476 }
1477 return settings.harness_timeout.normal;
1478 };
1479
1480 Tests.prototype.set_timeout = function() { 1692 Tests.prototype.set_timeout = function() {
1481 var this_obj = this; 1693 var this_obj = this;
1482 clearTimeout(this.timeout_id); 1694 clearTimeout(this.timeout_id);
1483 if (this.timeout_length !== null) { 1695 if (this.timeout_length !== null) {
1484 this.timeout_id = setTimeout(function() { 1696 this.timeout_id = setTimeout(function() {
1485 this_obj.timeout(); 1697 this_obj.timeout();
1486 }, this.timeout_length); 1698 }, this.timeout_length);
1487 } 1699 }
1488 }; 1700 };
1489 1701
1490 Tests.prototype.timeout = function() { 1702 Tests.prototype.timeout = function() {
1491 if (this.status.status == this.status.OK) { 1703 if (this.status.status === null) {
1492 this.status.status = this.status.TIMEOUT; 1704 this.status.status = this.status.TIMEOUT;
1493 } 1705 }
1494 this.complete(); 1706 this.complete();
1495 }; 1707 };
1496 1708
1497 Tests.prototype.end_wait = function() 1709 Tests.prototype.end_wait = function()
1498 { 1710 {
1499 this.wait_for_finish = false; 1711 this.wait_for_finish = false;
1500 if (this.all_done()) { 1712 if (this.all_done()) {
1501 this.complete(); 1713 this.complete();
1502 } 1714 }
1503 }; 1715 };
1504 1716
1505 Tests.prototype.push = function(test) 1717 Tests.prototype.push = function(test)
1506 { 1718 {
1507 if (this.phase < this.phases.HAVE_TESTS) { 1719 if (this.phase < this.phases.HAVE_TESTS) {
1508 this.start(); 1720 this.start();
1509 } 1721 }
1510 this.num_pending++; 1722 this.num_pending++;
1511 this.tests.push(test); 1723 test.index = this.tests.push(test);
1724 this.notify_test_state(test);
1725 };
1726
1727 Tests.prototype.notify_test_state = function(test) {
1728 var this_obj = this;
1729 forEach(this.test_state_callbacks,
1730 function(callback) {
1731 callback(test, this_obj);
1732 });
1512 }; 1733 };
1513 1734
1514 Tests.prototype.all_done = function() { 1735 Tests.prototype.all_done = function() {
1515 return (this.tests.length > 0 && this.all_loaded && this.num_pending === 0 && 1736 return (this.tests.length > 0 && test_environment.all_loaded &&
1516 !this.wait_for_finish && !this.processing_callbacks); 1737 this.num_pending === 0 && !this.wait_for_finish &&
1738 !this.processing_callbacks &&
1739 !this.pending_workers.some(function(w) { return w.running; }));
1517 }; 1740 };
1518 1741
1519 Tests.prototype.start = function() { 1742 Tests.prototype.start = function() {
1520 this.phase = this.phases.HAVE_TESTS; 1743 this.phase = this.phases.HAVE_TESTS;
1521 this.notify_start(); 1744 this.notify_start();
1522 }; 1745 };
1523 1746
1524 Tests.prototype.notify_start = function() { 1747 Tests.prototype.notify_start = function() {
1525 var this_obj = this; 1748 var this_obj = this;
1526 forEach (this.start_callbacks, 1749 forEach (this.start_callbacks,
1527 function(callback) 1750 function(callback)
1528 { 1751 {
1529 callback(this_obj.properties); 1752 callback(this_obj.properties);
1530 }); 1753 });
1531 forEach_windows(
1532 function(w, is_same_origin)
1533 {
1534 if (is_same_origin && w.start_callback) {
1535 try {
1536 w.start_callback(this_obj.properties);
1537 } catch (e) {
1538 if (debug) {
1539 throw e;
1540 }
1541 }
1542 }
1543 if (supports_post_message(w) && w !== self) {
1544 w.postMessage({
1545 type: "start",
1546 properties: this_obj.properties
1547 }, "*");
1548 }
1549 });
1550 }; 1754 };
1551 1755
1552 Tests.prototype.result = function(test) 1756 Tests.prototype.result = function(test)
1553 { 1757 {
1554 if (this.phase > this.phases.HAVE_RESULTS) { 1758 if (this.phase > this.phases.HAVE_RESULTS) {
1555 return; 1759 return;
1556 } 1760 }
1557 this.phase = this.phases.HAVE_RESULTS; 1761 this.phase = this.phases.HAVE_RESULTS;
1558 this.num_pending--; 1762 this.num_pending--;
1559 this.notify_result(test); 1763 this.notify_result(test);
1560 }; 1764 };
1561 1765
1562 Tests.prototype.notify_result = function(test) { 1766 Tests.prototype.notify_result = function(test) {
1563 var this_obj = this; 1767 var this_obj = this;
1564 this.processing_callbacks = true; 1768 this.processing_callbacks = true;
1565 forEach(this.test_done_callbacks, 1769 forEach(this.test_done_callbacks,
1566 function(callback) 1770 function(callback)
1567 { 1771 {
1568 callback(test, this_obj); 1772 callback(test, this_obj);
1569 }); 1773 });
1570
1571 forEach_windows(
1572 function(w, is_same_origin)
1573 {
1574 if (is_same_origin && w.result_callback) {
1575 try {
1576 w.result_callback(test);
1577 } catch (e) {
1578 if (debug) {
1579 throw e;
1580 }
1581 }
1582 }
1583 if (supports_post_message(w) && w !== self) {
1584 w.postMessage({
1585 type: "result",
1586 test: test.structured_clone()
1587 }, "*");
1588 }
1589 });
1590 this.processing_callbacks = false; 1774 this.processing_callbacks = false;
1591 if (this_obj.all_done()) { 1775 if (this_obj.all_done()) {
1592 this_obj.complete(); 1776 this_obj.complete();
1593 } 1777 }
1594 }; 1778 };
1595 1779
1596 Tests.prototype.complete = function() { 1780 Tests.prototype.complete = function() {
1597 if (this.phase === this.phases.COMPLETE) { 1781 if (this.phase === this.phases.COMPLETE) {
1598 return; 1782 return;
1599 } 1783 }
1600 this.phase = this.phases.COMPLETE; 1784 this.phase = this.phases.COMPLETE;
1601 var this_obj = this; 1785 var this_obj = this;
1602 this.tests.forEach( 1786 this.tests.forEach(
1603 function(x) 1787 function(x)
1604 { 1788 {
1605 if (x.status === x.NOTRUN) { 1789 if (x.phase < x.phases.COMPLETE) {
1606 this_obj.notify_result(x); 1790 this_obj.notify_result(x);
1607 x.cleanup(); 1791 x.cleanup();
1792 x.phase = x.phases.COMPLETE;
1608 } 1793 }
1609 } 1794 }
1610 ); 1795 );
1611 this.notify_complete(); 1796 this.notify_complete();
1612 }; 1797 };
1613 1798
1614 Tests.prototype.notify_complete = function() 1799 Tests.prototype.notify_complete = function() {
1615 {
1616 clearTimeout(this.timeout_id);
1617 var this_obj = this; 1800 var this_obj = this;
1618 var tests = map(this_obj.tests,
1619 function(test)
1620 {
1621 return test.structured_clone();
1622 });
1623 if (this.status.status === null) { 1801 if (this.status.status === null) {
1624 this.status.status = this.status.OK; 1802 this.status.status = this.status.OK;
1625 } 1803 }
1626 1804
1627 forEach (this.all_done_callbacks, 1805 forEach (this.all_done_callbacks,
1628 function(callback) 1806 function(callback)
1629 { 1807 {
1630 callback(this_obj.tests, this_obj.status); 1808 callback(this_obj.tests, this_obj.status);
1631 }); 1809 });
1632
1633 forEach_windows(
1634 function(w, is_same_origin)
1635 {
1636 if (is_same_origin && w.completion_callback) {
1637 try {
1638 w.completion_callback(this_obj.tests, this_obj.statu s);
1639 } catch (e) {
1640 if (debug) {
1641 throw e;
1642 }
1643 }
1644 }
1645 if (supports_post_message(w) && w !== self) {
1646 w.postMessage({
1647 type: "complete",
1648 tests: tests,
1649 status: this_obj.status.structured_clone()
1650 }, "*");
1651 }
1652 });
1653 }; 1810 };
1654 1811
1655 var tests = new Tests(); 1812 Tests.prototype.fetch_tests_from_worker = function(worker) {
1813 if (this.phase >= this.phases.COMPLETE) {
1814 return;
1815 }
1656 1816
1657 addEventListener("error", function(e) { 1817 this.pending_workers.push(new RemoteWorker(worker));
1658 if (tests.file_is_test) { 1818 };
1659 var test = tests.tests[0]; 1819
1660 if (test.phase >= test.phases.HAS_RESULT) { 1820 function fetch_tests_from_worker(port) {
1661 return; 1821 tests.fetch_tests_from_worker(port);
1662 } 1822 }
1663 var message = e.message; 1823 expose(fetch_tests_from_worker, 'fetch_tests_from_worker');
1664 test.set_status(test.FAIL, message);
1665 test.phase = test.phases.HAS_RESULT;
1666 test.done();
1667 done();
1668 } else if (!tests.allow_uncaught_exception) {
1669 tests.status.status = tests.status.ERROR;
1670 tests.status.message = e.message;
1671 }
1672 });
1673 1824
1674 function timeout() { 1825 function timeout() {
1675 if (tests.timeout_length === null) { 1826 if (tests.timeout_length === null) {
1676 tests.timeout(); 1827 tests.timeout();
1677 } 1828 }
1678 } 1829 }
1679 expose(timeout, 'timeout'); 1830 expose(timeout, 'timeout');
1680 1831
1681 function add_start_callback(callback) { 1832 function add_start_callback(callback) {
1682 tests.start_callbacks.push(callback); 1833 tests.start_callbacks.push(callback);
1683 } 1834 }
1684 1835
1836 function add_test_state_callback(callback) {
1837 tests.test_state_callbacks.push(callback);
1838 }
1839
1685 function add_result_callback(callback) 1840 function add_result_callback(callback)
1686 { 1841 {
1687 tests.test_done_callbacks.push(callback); 1842 tests.test_done_callbacks.push(callback);
1688 } 1843 }
1689 1844
1690 function add_completion_callback(callback) 1845 function add_completion_callback(callback)
1691 { 1846 {
1692 tests.all_done_callbacks.push(callback); 1847 tests.all_done_callbacks.push(callback);
1693 } 1848 }
1694 1849
1695 expose(add_start_callback, 'add_start_callback'); 1850 expose(add_start_callback, 'add_start_callback');
1851 expose(add_test_state_callback, 'add_test_state_callback');
1696 expose(add_result_callback, 'add_result_callback'); 1852 expose(add_result_callback, 'add_result_callback');
1697 expose(add_completion_callback, 'add_completion_callback'); 1853 expose(add_completion_callback, 'add_completion_callback');
1698 1854
1699 /* 1855 /*
1700 * Output listener 1856 * Output listener
1701 */ 1857 */
1702 1858
1703 function Output() { 1859 function Output() {
1704 this.output_document = document; 1860 this.output_document = document;
1705 this.output_node = null; 1861 this.output_node = null;
1706 this.done_count = 0;
1707 this.enabled = settings.output; 1862 this.enabled = settings.output;
1708 this.phase = this.INITIAL; 1863 this.phase = this.INITIAL;
1709 } 1864 }
1710 1865
1711 Output.prototype.INITIAL = 0; 1866 Output.prototype.INITIAL = 0;
1712 Output.prototype.STARTED = 1; 1867 Output.prototype.STARTED = 1;
1713 Output.prototype.HAVE_RESULTS = 2; 1868 Output.prototype.HAVE_RESULTS = 2;
1714 Output.prototype.COMPLETE = 3; 1869 Output.prototype.COMPLETE = 3;
1715 1870
1716 Output.prototype.setup = function(properties) { 1871 Output.prototype.setup = function(properties) {
(...skipping 24 matching lines...) Expand all
1741 if (typeof this.output_document === "function") { 1896 if (typeof this.output_document === "function") {
1742 output_document = this.output_document.apply(undefined); 1897 output_document = this.output_document.apply(undefined);
1743 } else { 1898 } else {
1744 output_document = this.output_document; 1899 output_document = this.output_document;
1745 } 1900 }
1746 if (!output_document) { 1901 if (!output_document) {
1747 return; 1902 return;
1748 } 1903 }
1749 var node = output_document.getElementById("log"); 1904 var node = output_document.getElementById("log");
1750 if (!node) { 1905 if (!node) {
1751 if (!document.body) { 1906 if (!document.body || document.readyState == "loading") {
1752 return; 1907 return;
1753 } 1908 }
1754 node = output_document.createElement("div"); 1909 node = output_document.createElement("div");
1910 node.id = "log";
1755 output_document.body.appendChild(node); 1911 output_document.body.appendChild(node);
1756 } 1912 }
1757 this.output_document = output_document; 1913 this.output_document = output_document;
1758 this.output_node = node; 1914 this.output_node = node;
1759 }; 1915 };
1760 1916
1761 Output.prototype.show_status = function() { 1917 Output.prototype.show_status = function() {
1762 if (this.phase < this.STARTED) { 1918 if (this.phase < this.STARTED) {
1763 this.init(); 1919 this.init();
1764 } 1920 }
1765 if (!this.enabled) { 1921 if (!this.enabled) {
1766 return; 1922 return;
1767 } 1923 }
1768 if (this.phase < this.HAVE_RESULTS) { 1924 if (this.phase < this.HAVE_RESULTS) {
1769 this.resolve_log(); 1925 this.resolve_log();
1770 this.phase = this.HAVE_RESULTS; 1926 this.phase = this.HAVE_RESULTS;
1771 } 1927 }
1772 this.done_count++; 1928 var done_count = tests.tests.length - tests.num_pending;
1773 if (this.output_node) { 1929 if (this.output_node) {
1774 if (this.done_count < 100 || 1930 if (done_count < 100 ||
1775 (this.done_count < 1000 && this.done_count % 100 === 0) || 1931 (done_count < 1000 && done_count % 100 === 0) ||
1776 this.done_count % 1000 === 0) { 1932 done_count % 1000 === 0) {
1777 this.output_node.textContent = "Running, " + 1933 this.output_node.textContent = "Running, " +
1778 this.done_count + " complete, " + 1934 done_count + " complete, " +
1779 tests.num_pending + " remain"; 1935 tests.num_pending + " remain";
1780 } 1936 }
1781 } 1937 }
1782 }; 1938 };
1783 1939
1784 Output.prototype.show_results = function (tests, harness_status) { 1940 Output.prototype.show_results = function (tests, harness_status) {
1785 if (this.phase >= this.COMPLETE) { 1941 if (this.phase >= this.COMPLETE) {
1786 return; 1942 return;
1787 } 1943 }
1788 if (!this.enabled) { 1944 if (!this.enabled) {
1789 return; 1945 return;
1790 } 1946 }
1791 if (!this.output_node) { 1947 if (!this.output_node) {
1792 this.resolve_log(); 1948 this.resolve_log();
1793 } 1949 }
1794 this.phase = this.COMPLETE; 1950 this.phase = this.COMPLETE;
1795 1951
1796 var log = this.output_node; 1952 var log = this.output_node;
1797 if (!log) { 1953 if (!log) {
1798 return; 1954 return;
1799 } 1955 }
1800 var output_document = this.output_document; 1956 var output_document = this.output_document;
1801 1957
1802 while (log.lastChild) { 1958 while (log.lastChild) {
1803 log.removeChild(log.lastChild); 1959 log.removeChild(log.lastChild);
1804 } 1960 }
1805 1961
1806 if (script_prefix != null) { 1962 var script_prefix = null;
1963 var scripts = document.getElementsByTagName("script");
1964 for (var i = 0; i < scripts.length; i++) {
1965 var src;
1966 if (scripts[i].src) {
1967 src = scripts[i].src;
1968 } else if (scripts[i].href) {
1969 //SVG case
1970 src = scripts[i].href.baseVal;
1971 }
1972
1973 var matches = src && src.match(/^(.*\/|)testharness\.js$/);
1974 if (matches) {
1975 script_prefix = matches[1];
1976 break;
1977 }
1978 }
1979
1980 if (script_prefix !== null) {
1807 var stylesheet = output_document.createElementNS(xhtml_ns, "link"); 1981 var stylesheet = output_document.createElementNS(xhtml_ns, "link");
1808 stylesheet.setAttribute("rel", "stylesheet"); 1982 stylesheet.setAttribute("rel", "stylesheet");
1809 stylesheet.setAttribute("href", script_prefix + "testharness.css"); 1983 stylesheet.setAttribute("href", script_prefix + "testharness.css");
1810 var heads = output_document.getElementsByTagName("head"); 1984 var heads = output_document.getElementsByTagName("head");
1811 if (heads.length) { 1985 if (heads.length) {
1812 heads[0].appendChild(stylesheet); 1986 heads[0].appendChild(stylesheet);
1813 } 1987 }
1814 } 1988 }
1815 1989
1816 var status_text_harness = {}; 1990 var status_text_harness = {};
(...skipping 20 matching lines...) Expand all
1837 2011
1838 function status_class(status) 2012 function status_class(status)
1839 { 2013 {
1840 return status.replace(/\s/g, '').toLowerCase(); 2014 return status.replace(/\s/g, '').toLowerCase();
1841 } 2015 }
1842 2016
1843 var summary_template = ["section", {"id":"summary"}, 2017 var summary_template = ["section", {"id":"summary"},
1844 ["h2", {}, "Summary"], 2018 ["h2", {}, "Summary"],
1845 function() 2019 function()
1846 { 2020 {
1847 if (harness_status.status === harness_status .OK) {
1848 return null;
1849 }
1850 2021
1851 var status = status_text_harness[harness_sta tus.status]; 2022 var status = status_text_harness[harness_sta tus.status];
1852 var rv = [["p", {"class":status_class(status )}]]; 2023 var rv = [["section", {},
2024 ["p", {},
2025 "Harness status: ",
2026 ["span", {"class":status_class(s tatus)},
2027 status
2028 ],
2029 ]
2030 ]];
1853 2031
1854 if (harness_status.status === harness_status .ERROR) { 2032 if (harness_status.status === harness_status .ERROR) {
1855 rv[0].push("Harness encountered an error :"); 2033 rv[0].push(["pre", {}, harness_status.me ssage]);
1856 rv.push(["pre", {}, harness_status.messa ge]); 2034 if (harness_status.stack) {
1857 } else if (harness_status.status === harness _status.TIMEOUT) { 2035 rv[0].push(["pre", {}, harness_statu s.stack]);
1858 rv[0].push("Harness timed out."); 2036 }
1859 } else {
1860 rv[0].push("Harness got an unexpected st atus.");
1861 } 2037 }
1862
1863 return rv; 2038 return rv;
1864 }, 2039 },
1865 ["p", {}, "Found ${num_tests} tests"], 2040 ["p", {}, "Found ${num_tests} tests"],
1866 function() { 2041 function() {
1867 var rv = [["div", {}]]; 2042 var rv = [["div", {}]];
1868 var i = 0; 2043 var i = 0;
1869 while (status_text.hasOwnProperty(i)) { 2044 while (status_text.hasOwnProperty(i)) {
1870 if (status_number.hasOwnProperty(status_ text[i])) { 2045 if (status_number.hasOwnProperty(status_ text[i])) {
1871 var status = status_text[i]; 2046 var status = status_text[i];
1872 rv[0].push(["div", {"class":status_c lass(status)}, 2047 rv[0].push(["div", {"class":status_c lass(status)},
1873 ["label", {}, 2048 ["label", {},
1874 ["input", {type:"checkb ox", checked:"checked"}], 2049 ["input", {type:"checkb ox", checked:"checked"}],
1875 status_number[status] + " " + status]]); 2050 status_number[status] + " " + status]]);
1876 } 2051 }
1877 i++; 2052 i++;
1878 } 2053 }
1879 return rv; 2054 return rv;
1880 }]; 2055 },
2056 ];
1881 2057
1882 log.appendChild(render(summary_template, {num_tests:tests.length}, outpu t_document)); 2058 log.appendChild(render(summary_template, {num_tests:tests.length}, outpu t_document));
1883 2059
1884 forEach(output_document.querySelectorAll("section#summary label"), 2060 forEach(output_document.querySelectorAll("section#summary label"),
1885 function(element) 2061 function(element)
1886 { 2062 {
1887 on_event(element, "click", 2063 on_event(element, "click",
1888 function(e) 2064 function(e)
1889 { 2065 {
1890 if (output_document.getElementById("results") = == null) { 2066 if (output_document.getElementById("results") = == null) {
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
1948 for (var i = 0; i < tests.length; i++) { 2124 for (var i = 0; i < tests.length; i++) {
1949 html += '<tr class="' + 2125 html += '<tr class="' +
1950 escape_html(status_class(status_text[tests[i].status])) + 2126 escape_html(status_class(status_text[tests[i].status])) +
1951 '"><td>' + 2127 '"><td>' +
1952 escape_html(status_text[tests[i].status]) + 2128 escape_html(status_text[tests[i].status]) +
1953 "</td><td>" + 2129 "</td><td>" +
1954 escape_html(tests[i].name) + 2130 escape_html(tests[i].name) +
1955 "</td><td>" + 2131 "</td><td>" +
1956 (assertions ? escape_html(get_assertion(tests[i])) + "</td><td>" : "") + 2132 (assertions ? escape_html(get_assertion(tests[i])) + "</td><td>" : "") +
1957 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>": "") +
1958 "</td></tr>"; 2137 "</td></tr>";
1959 } 2138 }
1960 html += "</tbody></table>"; 2139 html += "</tbody></table>";
1961 try { 2140 try {
1962 log.lastChild.innerHTML = html; 2141 log.lastChild.innerHTML = html;
1963 } catch (e) { 2142 } catch (e) {
1964 log.appendChild(document.createElementNS(xhtml_ns, "p")) 2143 log.appendChild(document.createElementNS(xhtml_ns, "p"))
1965 .textContent = "Setting innerHTML for the log threw an exception. "; 2144 .textContent = "Setting innerHTML for the log threw an exception. ";
1966 log.appendChild(document.createElementNS(xhtml_ns, "pre")) 2145 log.appendChild(document.createElementNS(xhtml_ns, "pre"))
1967 .textContent = html; 2146 .textContent = html;
1968 } 2147 }
1969 }; 2148 };
1970 2149
1971 var output = new Output();
1972 add_start_callback(function (properties) {output.init(properties);});
1973 add_result_callback(function () {output.show_status();});
1974 add_completion_callback(function (tests, harness_status) {output.show_result s(tests, harness_status);});
1975
1976 /* 2150 /*
1977 * Template code 2151 * Template code
1978 * 2152 *
1979 * A template is just a javascript structure. An element is represented as: 2153 * A template is just a javascript structure. An element is represented as:
1980 * 2154 *
1981 * [tag_name, {attr_name:attr_value}, child1, child2] 2155 * [tag_name, {attr_name:attr_value}, child1, child2]
1982 * 2156 *
1983 * the children can either be strings (which act like text nodes), other tem plates or 2157 * the children can either be strings (which act like text nodes), other tem plates or
1984 * functions (see below) 2158 * functions (see below)
1985 * 2159 *
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
2114 } else { 2288 } else {
2115 var text_node = output_document.createTextNode(template[i]); 2289 var text_node = output_document.createTextNode(template[i]);
2116 element.appendChild(text_node); 2290 element.appendChild(text_node);
2117 } 2291 }
2118 } 2292 }
2119 } 2293 }
2120 2294
2121 return element; 2295 return element;
2122 } 2296 }
2123 2297
2124
2125
2126 function make_dom(template, substitutions, output_document) 2298 function make_dom(template, substitutions, output_document)
2127 { 2299 {
2128 if (is_single_node(template)) { 2300 if (is_single_node(template)) {
2129 return make_dom_single(template, output_document); 2301 return make_dom_single(template, output_document);
2130 } 2302 }
2131 2303
2132 return map(template, function(x) { 2304 return map(template, function(x) {
2133 return make_dom_single(x, output_document); 2305 return make_dom_single(x, output_document);
2134 }); 2306 });
2135 } 2307 }
(...skipping 14 matching lines...) Expand all
2150 if (expected_true !== true) { 2322 if (expected_true !== true) {
2151 var msg = make_message(function_name, description, 2323 var msg = make_message(function_name, description,
2152 error, substitutions); 2324 error, substitutions);
2153 throw new AssertionError(msg); 2325 throw new AssertionError(msg);
2154 } 2326 }
2155 } 2327 }
2156 2328
2157 function AssertionError(message) 2329 function AssertionError(message)
2158 { 2330 {
2159 this.message = message; 2331 this.message = message;
2332 this.stack = this.get_stack();
2160 } 2333 }
2161 2334
2162 AssertionError.prototype.toString = function() { 2335 AssertionError.prototype = Object.create(Error.prototype);
2163 return this.message; 2336
2164 }; 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 }
2165 2352
2166 function make_message(function_name, description, error, substitutions) 2353 function make_message(function_name, description, error, substitutions)
2167 { 2354 {
2168 for (var p in substitutions) { 2355 for (var p in substitutions) {
2169 if (substitutions.hasOwnProperty(p)) { 2356 if (substitutions.hasOwnProperty(p)) {
2170 substitutions[p] = format_value(substitutions[p]); 2357 substitutions[p] = format_value(substitutions[p]);
2171 } 2358 }
2172 } 2359 }
2173 var node_form = substitute(["{text}", "${function_name}: ${description}" + error], 2360 var node_form = substitute(["{text}", "${function_name}: ${description}" + error],
2174 merge({function_name:function_name, 2361 merge({function_name:function_name,
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
2225 } 2412 }
2226 for (p in b) { 2413 for (p in b) {
2227 rv[p] = b[p]; 2414 rv[p] = b[p];
2228 } 2415 }
2229 return rv; 2416 return rv;
2230 } 2417 }
2231 2418
2232 function expose(object, name) 2419 function expose(object, name)
2233 { 2420 {
2234 var components = name.split("."); 2421 var components = name.split(".");
2235 var target = window; 2422 var target = test_environment.global_scope();
2236 for (var i = 0; i < components.length - 1; i++) { 2423 for (var i = 0; i < components.length - 1; i++) {
2237 if (!(components[i] in target)) { 2424 if (!(components[i] in target)) {
2238 target[components[i]] = {}; 2425 target[components[i]] = {};
2239 } 2426 }
2240 target = target[components[i]]; 2427 target = target[components[i]];
2241 } 2428 }
2242 target[components[components.length - 1]] = object; 2429 target[components[components.length - 1]] = object;
2243 } 2430 }
2244 2431
2245 function forEach_windows(callback) {
2246 // Iterate of the the windows [self ... top, opener]. The callback is pa ssed
2247 // two objects, the first one is the windows object itself, the second o ne
2248 // is a boolean indicating whether or not its on the same origin as the
2249 // current window.
2250 var cache = forEach_windows.result_cache;
2251 if (!cache) {
2252 cache = [[self, true]];
2253 var w = self;
2254 var i = 0;
2255 var so;
2256 var origins = location.ancestorOrigins;
2257 while (w != w.parent) {
2258 w = w.parent;
2259 // In WebKit, calls to parent windows' properties that aren't on the same
2260 // origin cause an error message to be displayed in the error co nsole but
2261 // don't throw an exception. This is a deviation from the curren t HTML5
2262 // spec. See: https://bugs.webkit.org/show_bug.cgi?id=43504
2263 // The problem with WebKit's behavior is that it pollutes the er ror console
2264 // with error messages that can't be caught.
2265 //
2266 // This issue can be mitigated by relying on the (for now) propr ietary
2267 // `location.ancestorOrigins` property which returns an ordered list of
2268 // the origins of enclosing windows. See:
2269 // http://trac.webkit.org/changeset/113945.
2270 if (origins) {
2271 so = (location.origin == origins[i]);
2272 } else {
2273 so = is_same_origin(w);
2274 }
2275 cache.push([w, so]);
2276 i++;
2277 }
2278 w = window.opener;
2279 if (w) {
2280 // window.opener isn't included in the `location.ancestorOrigins ` prop.
2281 // We'll just have to deal with a simple check and an error msg on WebKit
2282 // browsers in this case.
2283 cache.push([w, is_same_origin(w)]);
2284 }
2285 forEach_windows.result_cache = cache;
2286 }
2287
2288 forEach(cache,
2289 function(a)
2290 {
2291 callback.apply(null, a);
2292 });
2293 }
2294
2295 function is_same_origin(w) { 2432 function is_same_origin(w) {
2296 try { 2433 try {
2297 'random_prop' in w; 2434 'random_prop' in w;
2298 return true; 2435 return true;
2299 } catch (e) { 2436 } catch (e) {
2300 return false; 2437 return false;
2301 } 2438 }
2302 } 2439 }
2303 2440
2304 function supports_post_message(w) 2441 function supports_post_message(w)
(...skipping 25 matching lines...) Expand all
2330 else { 2467 else {
2331 supports = false; 2468 supports = false;
2332 } 2469 }
2333 } catch (e) { 2470 } catch (e) {
2334 // This is the case where postMessage isn't supported AND accessing a 2471 // This is the case where postMessage isn't supported AND accessing a
2335 // window property across origins throws (e.g. old Firefox browser). 2472 // window property across origins throws (e.g. old Firefox browser).
2336 supports = false; 2473 supports = false;
2337 } 2474 }
2338 return supports; 2475 return supports;
2339 } 2476 }
2477
2478 /**
2479 * Setup globals
2480 */
2481
2482 var tests = new Tests();
2483
2484 addEventListener("error", function(e) {
2485 if (tests.file_is_test) {
2486 var test = tests.tests[0];
2487 if (test.phase >= test.phases.HAS_RESULT) {
2488 return;
2489 }
2490 test.set_status(test.FAIL, e.message, e.stack);
2491 test.phase = test.phases.HAS_RESULT;
2492 test.done();
2493 done();
2494 } else if (!tests.allow_uncaught_exception) {
2495 tests.status.status = tests.status.ERROR;
2496 tests.status.message = e.message;
2497 tests.status.stack = e.stack;
2498 }
2499 });
2500
2501 test_environment.on_tests_ready();
2502
2340 })(); 2503 })();
2341 // vim: set expandtab shiftwidth=4 tabstop=4: 2504 // vim: set expandtab shiftwidth=4 tabstop=4:
OLDNEW
« no previous file with comments | « LayoutTests/http/tests/w3c/resources/idlharness.js ('k') | LayoutTests/http/tests/w3c/webperf/resources/idlharness.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698