OLD | NEW |
1 /* | 1 /* |
2 Distributed under both the W3C Test Suite License [1] and the W3C | 2 Distributed under both the W3C Test Suite License [1] and the W3C |
3 3-clause BSD License [2]. To contribute to a W3C Test Suite, see the | 3 3-clause BSD License [2]. To contribute to a W3C Test Suite, see the |
4 policies and contribution forms [3]. | 4 policies and contribution forms [3]. |
5 | 5 |
6 [1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license | 6 [1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license |
7 [2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license | 7 [2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license |
8 [3] http://www.w3.org/2004/10/27-testcases | 8 [3] http://www.w3.org/2004/10/27-testcases |
9 */ | 9 */ |
10 | 10 |
(...skipping 15 matching lines...) Expand all Loading... |
26 * Within each file one may define one or more tests. Each test is atomic | 26 * Within each file one may define one or more tests. Each test is atomic |
27 * in the sense that a single test has a single result (pass/fail/timeout). | 27 * in the sense that a single test has a single result (pass/fail/timeout). |
28 * Within each test one may have a number of asserts. The test fails at the | 28 * Within each test one may have a number of asserts. The test fails at the |
29 * first failing assert, and the remainder of the test is (typically) not run. | 29 * first failing assert, and the remainder of the test is (typically) not run. |
30 * | 30 * |
31 * If the file containing the tests is a HTML file with an element of id "log" | 31 * If the file containing the tests is a HTML file with an element of id "log" |
32 * this will be populated with a table containing the test results after all | 32 * this will be populated with a table containing the test results after all |
33 * the tests have run. | 33 * the tests have run. |
34 * | 34 * |
35 * NOTE: By default tests must be created before the load event fires. For ways | 35 * NOTE: By default tests must be created before the load event fires. For ways |
36 * to create tests after the load event, see "Determining when all tests a
re | 36 * to create tests after the load event, see "Determining when all tests |
37 * complete", below | 37 * are complete", below |
38 * | 38 * |
39 * == Synchronous Tests == | 39 * == Synchronous Tests == |
40 * | 40 * |
41 * To create a synchronous test use the test() function: | 41 * To create a synchronous test use the test() function: |
42 * | 42 * |
43 * test(test_function, name, properties) | 43 * test(test_function, name, properties) |
44 * | 44 * |
45 * test_function is a function that contains the code to test. For example a | 45 * test_function is a function that contains the code to test. For example a |
46 * trivial passing test would be: | 46 * trivial passing test would be: |
47 * | 47 * |
48 * test(function() {assert_true(true)}, "assert_true with true") | 48 * test(function() {assert_true(true)}, "assert_true with true") |
49 * | 49 * |
50 * The function passed in is run in the test() call. | 50 * The function passed in is run in the test() call. |
51 * | 51 * |
52 * properties is an object that overrides default test properties. The recognise
d properties | 52 * properties is an object that overrides default test properties. The |
53 * are: | 53 * recognised properties are: |
54 * timeout - the test timeout in ms | 54 * timeout - the test timeout in ms |
55 * | 55 * |
56 * e.g. | 56 * e.g. |
57 * test(test_function, "Sample test", {timeout:1000}) | 57 * test(test_function, "Sample test", {timeout:1000}) |
58 * | 58 * |
59 * would run test_function with a timeout of 1s. | 59 * would run test_function with a timeout of 1s. |
60 * | 60 * |
| 61 * Additionally, test-specific metadata can be passed in the properties. These |
| 62 * are used when the individual test has different metadata from that stored |
| 63 * in the <head>. |
| 64 * The recognized metadata properties are: |
| 65 * |
| 66 * help - The url of the part of the specification being tested |
| 67 * |
| 68 * assert - A human readable description of what the test is attempting |
| 69 * to prove |
| 70 * |
| 71 * author - Name and contact information for the author of the test in the |
| 72 * format: "Name <email_addr>" or "Name http://contact/url" |
| 73 * |
61 * == Asynchronous Tests == | 74 * == Asynchronous Tests == |
62 * | 75 * |
63 * Testing asynchronous features is somewhat more complex since the result of | 76 * Testing asynchronous features is somewhat more complex since the result of |
64 * a test may depend on one or more events or other callbacks. The API provided | 77 * a test may depend on one or more events or other callbacks. The API provided |
65 * for testing these features is indended to be rather low-level but hopefully | 78 * for testing these features is indended to be rather low-level but hopefully |
66 * applicable to many situations. | 79 * applicable to many situations. |
67 * | 80 * |
68 * To create a test, one starts by getting a Test object using async_test: | 81 * To create a test, one starts by getting a Test object using async_test: |
69 * | 82 * |
70 * async_test(name, properties) | 83 * async_test(name, properties) |
71 * | 84 * |
72 * e.g. | 85 * e.g. |
73 * var t = async_test("Simple async test") | 86 * var t = async_test("Simple async test") |
74 * | 87 * |
75 * Assertions can be added to the test by calling the step method of the test | 88 * Assertions can be added to the test by calling the step method of the test |
76 * object with a function containing the test assertions: | 89 * object with a function containing the test assertions: |
77 * | 90 * |
78 * t.step(function() {assert_true(true)}); | 91 * t.step(function() {assert_true(true)}); |
79 * | 92 * |
80 * When all the steps are complete, the done() method must be called: | 93 * When all the steps are complete, the done() method must be called: |
81 * | 94 * |
82 * t.done(); | 95 * t.done(); |
83 * | 96 * |
| 97 * As a convenience, async_test can also takes a function as first argument. |
| 98 * This function is called with the test object as both its `this` object and |
| 99 * first argument. The above example can be rewritten as: |
| 100 * |
| 101 * async_test(function(t) { |
| 102 * object.some_event = function() { |
| 103 * t.step(function (){assert_true(true); t.done();}); |
| 104 * }; |
| 105 * }, "Simple async test"); |
| 106 * |
| 107 * which avoids cluttering the global scope with references to async |
| 108 * tests instances. |
| 109 * |
84 * The properties argument is identical to that for test(). | 110 * The properties argument is identical to that for test(). |
85 * | 111 * |
86 * In many cases it is convenient to run a step in response to an event or a | 112 * In many cases it is convenient to run a step in response to an event or a |
87 * callback. A convenient method of doing this is through the step_func method | 113 * callback. A convenient method of doing this is through the step_func method |
88 * which returns a function that, when called runs a test step. For example | 114 * which returns a function that, when called runs a test step. For example |
89 * | 115 * |
90 * object.some_event = t.step_func(function(e) {assert_true(e.a)}); | 116 * object.some_event = t.step_func(function(e) {assert_true(e.a)}); |
91 * | 117 * |
92 * == Making assertions == | 118 * == Making assertions == |
93 * | 119 * |
(...skipping 23 matching lines...) Expand all Loading... |
117 * | 143 * |
118 * The one argument versions may omit either argument. | 144 * The one argument versions may omit either argument. |
119 * func is a function to be run synchronously. setup() becomes a no-op once | 145 * func is a function to be run synchronously. setup() becomes a no-op once |
120 * any tests have returned results. Properties are global properties of the test | 146 * any tests have returned results. Properties are global properties of the test |
121 * harness. Currently recognised properties are: | 147 * harness. Currently recognised properties are: |
122 * | 148 * |
123 * timeout - The time in ms after which the harness should stop waiting for | 149 * timeout - The time in ms after which the harness should stop waiting for |
124 * tests to complete (this is different to the per-test timeout | 150 * tests to complete (this is different to the per-test timeout |
125 * because async tests do not start their timer until .step is called) | 151 * because async tests do not start their timer until .step is called) |
126 * | 152 * |
127 * explicit_done - Wait for an explicit call to done() before declaring all test
s | 153 * explicit_done - Wait for an explicit call to done() before declaring all |
128 * complete (see below) | 154 * tests complete (see below) |
129 * | 155 * |
130 * output_document - The document to which results should be logged. By default
this is | 156 * output_document - The document to which results should be logged. By default |
131 * the current document but could be an ancestor document in s
ome cases | 157 * this is the current document but could be an ancestor |
132 * e.g. a SVG test loaded in an HTML wrapper | 158 * document in some cases e.g. a SVG test loaded in an HTML |
| 159 * wrapper |
| 160 * |
| 161 * explicit_timeout - disable file timeout; only stop waiting for results |
| 162 * when the timeout() function is called (typically for |
| 163 * use when integrating with some existing test framework |
| 164 * that has its own timeout mechanism). |
133 * | 165 * |
134 * == Determining when all tests are complete == | 166 * == Determining when all tests are complete == |
135 * | 167 * |
136 * By default the test harness will assume there are no more results to come | 168 * By default the test harness will assume there are no more results to come |
137 * when: | 169 * when: |
138 * 1) There are no Test objects that have been created but not completed | 170 * 1) There are no Test objects that have been created but not completed |
139 * 2) The load event on the document has fired | 171 * 2) The load event on the document has fired |
140 * | 172 * |
141 * This behaviour can be overridden by setting the explicit_done property to tru
e | 173 * This behaviour can be overridden by setting the explicit_done property to |
142 * in a call to setup(). If explicit_done is true, the test harness will not ass
ume | 174 * true in a call to setup(). If explicit_done is true, the test harness will |
143 * it is done until the global done() function is called. Once done() is called,
the | 175 * not assume it is done until the global done() function is called. Once done() |
144 * two conditions above apply like normal. | 176 * is called, the two conditions above apply like normal. |
145 * | 177 * |
146 * == Generating tests == | 178 * == Generating tests == |
147 * | 179 * |
148 * NOTE: this functionality may be removed | 180 * NOTE: this functionality may be removed |
149 * | 181 * |
150 * There are scenarios in which is is desirable to create a large number of | 182 * There are scenarios in which is is desirable to create a large number of |
151 * (synchronous) tests that are internally similar but vary in the parameters | 183 * (synchronous) tests that are internally similar but vary in the parameters |
152 * used. To make this easier, the generate_tests function allows a single | 184 * used. To make this easier, the generate_tests function allows a single |
153 * function to be called with each set of parameters in a list: | 185 * function to be called with each set of parameters in a list: |
154 * | 186 * |
155 * generate_tests(test_function, parameter_lists) | 187 * generate_tests(test_function, parameter_lists, properties) |
156 * | 188 * |
157 * For example: | 189 * For example: |
158 * | 190 * |
159 * generate_tests(assert_equals, [ | 191 * generate_tests(assert_equals, [ |
160 * ["Sum one and one", 1+1, 2], | 192 * ["Sum one and one", 1+1, 2], |
161 * ["Sum one and zero", 1+0, 1] | 193 * ["Sum one and zero", 1+0, 1] |
162 * ]) | 194 * ]) |
163 * | 195 * |
164 * Is equivalent to: | 196 * Is equivalent to: |
165 * | 197 * |
166 * test(function() {assert_equals(1+1, 2)}, "Sum one and one") | 198 * test(function() {assert_equals(1+1, 2)}, "Sum one and one") |
167 * test(function() {assert_equals(1+0, 1)}, "Sum one and zero") | 199 * test(function() {assert_equals(1+0, 1)}, "Sum one and zero") |
168 * | 200 * |
169 * Note that the first item in each parameter list corresponds to the name of | 201 * Note that the first item in each parameter list corresponds to the name of |
170 * the test. | 202 * the test. |
171 * | 203 * |
| 204 * The properties argument is identical to that for test(). This may be a |
| 205 * single object (used for all generated tests) or an array. |
| 206 * |
172 * == Callback API == | 207 * == Callback API == |
173 * | 208 * |
174 * The framework provides callbacks corresponding to 3 events: | 209 * The framework provides callbacks corresponding to 3 events: |
175 * | 210 * |
176 * start - happens when the first Test is created | 211 * start - happens when the first Test is created |
177 * result - happens when a test result is recieved | 212 * result - happens when a test result is recieved |
178 * complete - happens when all results are recieved | 213 * complete - happens when all results are recieved |
179 * | 214 * |
180 * The page defining the tests may add callbacks for these events by calling | 215 * The page defining the tests may add callbacks for these events by calling |
181 * the following methods: | 216 * the following methods: |
(...skipping 11 matching lines...) Expand all Loading... |
193 * | 228 * |
194 * The status object gives the overall status of the harness. It has the | 229 * The status object gives the overall status of the harness. It has the |
195 * following properties: | 230 * following properties: |
196 * status: Can be compared to the OK, ERROR and TIMEOUT properties | 231 * status: Can be compared to the OK, ERROR and TIMEOUT properties |
197 * message: An error message set when the status is ERROR | 232 * message: An error message set when the status is ERROR |
198 * | 233 * |
199 * == External API == | 234 * == External API == |
200 * | 235 * |
201 * In order to collect the results of multiple pages containing tests, the test | 236 * In order to collect the results of multiple pages containing tests, the test |
202 * harness will, when loaded in a nested browsing context, attempt to call | 237 * harness will, when loaded in a nested browsing context, attempt to call |
203 * certain functions in each ancestor browsing context: | 238 * certain functions in each ancestor and opener browsing context: |
204 * | 239 * |
205 * start - start_callback | 240 * start - start_callback |
206 * result - result_callback | 241 * result - result_callback |
207 * complete - completion_callback | 242 * complete - completion_callback |
208 * | 243 * |
209 * These are given the same arguments as the corresponding internal callbacks | 244 * These are given the same arguments as the corresponding internal callbacks |
210 * described above. | 245 * described above. |
211 * | 246 * |
| 247 * == External API through cross-document messaging == |
| 248 * |
| 249 * Where supported, the test harness will also send messages using |
| 250 * cross-document messaging to each ancestor and opener browsing context. Since |
| 251 * it uses the wildcard keyword (*), cross-origin communication is enabled and |
| 252 * script on different origins can collect the results. |
| 253 * |
| 254 * This API follows similar conventions as those described above only slightly |
| 255 * modified to accommodate message event API. Each message is sent by the harnes
s |
| 256 * is passed a single vanilla object, available as the `data` property of the |
| 257 * event object. These objects are structures as follows: |
| 258 * |
| 259 * start - { type: "start" } |
| 260 * result - { type: "result", test: Test } |
| 261 * complete - { type: "complete", tests: [Test, ...], status: TestsStatus } |
| 262 * |
212 * == List of assertions == | 263 * == List of assertions == |
213 * | 264 * |
214 * assert_true(actual, description) | 265 * assert_true(actual, description) |
215 * asserts that /actual/ is strictly true | 266 * asserts that /actual/ is strictly true |
216 * | 267 * |
217 * assert_false(actual, description) | 268 * assert_false(actual, description) |
218 * asserts that /actual/ is strictly false | 269 * asserts that /actual/ is strictly false |
219 * | 270 * |
220 * assert_equals(actual, expected, description) | 271 * assert_equals(actual, expected, description) |
221 * asserts that /actual/ is the same value as /expected/ | 272 * asserts that /actual/ is the same value as /expected/ |
222 * | 273 * |
223 * assert_not_equals(actual, expected, description) | 274 * assert_not_equals(actual, expected, description) |
224 * asserts that /actual/ is a different value to /expected/. Yes, this means | 275 * asserts that /actual/ is a different value to /expected/. Yes, this means |
225 * that "expected" is a misnomer | 276 * that "expected" is a misnomer |
226 * | 277 * |
227 * assert_in_array(actual, expected, description) | 278 * assert_in_array(actual, expected, description) |
228 * asserts that /expected/ is an Array, and /actual/ is equal to one of the | 279 * asserts that /expected/ is an Array, and /actual/ is equal to one of the |
229 * members -- expected.indexOf(actual) != -1 | 280 * members -- expected.indexOf(actual) != -1 |
230 * | 281 * |
231 * assert_array_equals(actual, expected, description) | 282 * assert_array_equals(actual, expected, description) |
232 * asserts that /actual/ and /expected/ have the same length and the value of | 283 * asserts that /actual/ and /expected/ have the same length and the value of |
233 * each indexed property in /actual/ is the strictly equal to the correspondin
g | 284 * each indexed property in /actual/ is the strictly equal to the correspondin
g |
234 * property value in /expected/ | 285 * property value in /expected/ |
235 * | 286 * |
236 * assert_approx_equals(actual, expected, epsilon, description) | 287 * assert_approx_equals(actual, expected, epsilon, description) |
237 * asserts that /actual/ is a number within +/- /epsilon/ of /expected/ | 288 * asserts that /actual/ is a number within +/- /epsilon/ of /expected/ |
238 * | 289 * |
| 290 * assert_less_than(actual, expected, description) |
| 291 * asserts that /actual/ is a number less than /expected/ |
| 292 * |
| 293 * assert_greater_than(actual, expected, description) |
| 294 * asserts that /actual/ is a number greater than /expected/ |
| 295 * |
| 296 * assert_less_than_equal(actual, expected, description) |
| 297 * asserts that /actual/ is a number less than or equal to /expected/ |
| 298 * |
| 299 * assert_greater_than_equal(actual, expected, description) |
| 300 * asserts that /actual/ is a number greater than or equal to /expected/ |
| 301 * |
239 * assert_regexp_match(actual, expected, description) | 302 * assert_regexp_match(actual, expected, description) |
240 * asserts that /actual/ matches the regexp /expected/ | 303 * asserts that /actual/ matches the regexp /expected/ |
241 * | 304 * |
| 305 * assert_class_string(object, class_name, description) |
| 306 * asserts that the class string of /object/ as returned in |
| 307 * Object.prototype.toString is equal to /class_name/. |
| 308 * |
242 * assert_own_property(object, property_name, description) | 309 * assert_own_property(object, property_name, description) |
243 * assert that object has own property property_name | 310 * assert that object has own property property_name |
244 * | 311 * |
245 * assert_inherits(object, property_name, description) | 312 * assert_inherits(object, property_name, description) |
246 * assert that object does not have an own property named property_name | 313 * assert that object does not have an own property named property_name |
247 * but that property_name is present in the prototype chain for object | 314 * but that property_name is present in the prototype chain for object |
248 * | 315 * |
249 * assert_idl_attribute(object, attribute_name, description) | 316 * assert_idl_attribute(object, attribute_name, description) |
250 * assert that an object that is an instance of some interface has the | 317 * assert that an object that is an instance of some interface has the |
251 * attribute attribute_name following the conditions specified by WebIDL | 318 * attribute attribute_name following the conditions specified by WebIDL |
252 * | 319 * |
253 * assert_readonly(object, property_name, description) | 320 * assert_readonly(object, property_name, description) |
254 * assert that property property_name on object is readonly | 321 * assert that property property_name on object is readonly |
255 * | 322 * |
256 * assert_throws(code, func, description) | 323 * assert_throws(code, func, description) |
257 * code - a DOMException/RangeException code as a string, e.g. "HIERARCHY_REQU
EST_ERR" | 324 * code - the expected exception: |
| 325 * o string: the thrown exception must be a DOMException with the given |
| 326 * name, e.g., "TimeoutError" (for compatibility with existing |
| 327 * tests, a constant is also supported, e.g., "TIMEOUT_ERR") |
| 328 * o object: the thrown exception must have a property called "name" that |
| 329 * matches code.name |
| 330 * o null: allow any exception (in general, one of the options above |
| 331 * should be used) |
258 * func - a function that should throw | 332 * func - a function that should throw |
259 * | 333 * |
260 * assert that func throws a DOMException or RangeException (as appropriate) | |
261 * with the given code. If an object is passed for code instead of a string, | |
262 * checks that the thrown exception has a property called "name" that matches | |
263 * the property of code called "name". Note, this function will probably be | |
264 * rewritten sometime to make more sense. | |
265 * | |
266 * assert_unreached(description) | 334 * assert_unreached(description) |
267 * asserts if called. Used to ensure that some codepath is *not* taken e.g. | 335 * asserts if called. Used to ensure that some codepath is *not* taken e.g. |
268 * an event does not fire. | 336 * an event does not fire. |
269 * | 337 * |
| 338 * assert_any(assert_func, actual, expected_array, extra_arg_1, ... extra_arg_N) |
| 339 * asserts that one assert_func(actual, expected_array_N, extra_arg1, ..., ext
ra_arg_N) |
| 340 * is true for some expected_array_N in expected_array. This only works for as
sert_func |
| 341 * with signature assert_func(actual, expected, args_1, ..., args_N). Note tha
t tests |
| 342 * with multiple allowed pass conditions are bad practice unless the spec spec
ifically |
| 343 * allows multiple behaviours. Test authors should not use this method simply
to hide |
| 344 * UA bugs. |
| 345 * |
270 * assert_exists(object, property_name, description) | 346 * assert_exists(object, property_name, description) |
271 * *** deprecated *** | 347 * *** deprecated *** |
272 * asserts that object has an own property property_name | 348 * asserts that object has an own property property_name |
273 * | 349 * |
274 * assert_not_exists(object, property_name, description) | 350 * assert_not_exists(object, property_name, description) |
275 * *** deprecated *** | 351 * *** deprecated *** |
276 * assert that object does not have own property property_name | 352 * assert that object does not have own property property_name |
277 */ | 353 */ |
278 | 354 |
279 (function () | 355 (function () |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
316 | 392 |
317 /* | 393 /* |
318 * API functions | 394 * API functions |
319 */ | 395 */ |
320 | 396 |
321 var name_counter = 0; | 397 var name_counter = 0; |
322 function next_default_name() | 398 function next_default_name() |
323 { | 399 { |
324 //Don't use document.title to work around an Opera bug in XHTML document
s | 400 //Don't use document.title to work around an Opera bug in XHTML document
s |
325 var prefix = document.getElementsByTagName("title").length > 0 ? | 401 var prefix = document.getElementsByTagName("title").length > 0 ? |
326 document.getElementsByTagName("title")[0].firstChild.da
ta : | 402 document.getElementsByTagName("title")[0].textContent : |
327 "Untitled"; | 403 "Untitled"; |
328 var suffix = name_counter > 0 ? " " + name_counter : ""; | 404 var suffix = name_counter > 0 ? " " + name_counter : ""; |
329 name_counter++; | 405 name_counter++; |
330 return prefix + suffix; | 406 return prefix + suffix; |
331 } | 407 } |
332 | 408 |
333 function test(func, name, properties) | 409 function test(func, name, properties) |
334 { | 410 { |
335 var test_name = name ? name : next_default_name(); | 411 var test_name = name ? name : next_default_name(); |
336 properties = properties ? properties : {}; | 412 properties = properties ? properties : {}; |
337 var test_obj = new Test(test_name, properties); | 413 var test_obj = new Test(test_name, properties); |
338 test_obj.step(func); | 414 test_obj.step(func); |
339 if (test_obj.status === test_obj.NOTRUN) { | 415 if (test_obj.status === test_obj.NOTRUN) { |
340 test_obj.done(); | 416 test_obj.done(); |
341 } | 417 } |
342 } | 418 } |
343 | 419 |
344 function async_test(name, properties) | 420 function async_test(func, name, properties) |
345 { | 421 { |
| 422 if (typeof func !== "function") { |
| 423 properties = name; |
| 424 name = func; |
| 425 func = null; |
| 426 } |
346 var test_name = name ? name : next_default_name(); | 427 var test_name = name ? name : next_default_name(); |
347 properties = properties ? properties : {}; | 428 properties = properties ? properties : {}; |
348 var test_obj = new Test(test_name, properties); | 429 var test_obj = new Test(test_name, properties); |
| 430 if (func) { |
| 431 test_obj.step(func, test_obj, test_obj); |
| 432 } |
349 return test_obj; | 433 return test_obj; |
350 } | 434 } |
351 | 435 |
352 function setup(func_or_properties, maybe_properties) | 436 function setup(func_or_properties, maybe_properties) |
353 { | 437 { |
354 var func = null; | 438 var func = null; |
355 var properties = {}; | 439 var properties = {}; |
356 if (arguments.length === 2) { | 440 if (arguments.length === 2) { |
357 func = func_or_properties; | 441 func = func_or_properties; |
358 properties = maybe_properties; | 442 properties = maybe_properties; |
359 } else if (func_or_properties instanceof Function){ | 443 } else if (func_or_properties instanceof Function){ |
360 func = func_or_properties; | 444 func = func_or_properties; |
361 } else { | 445 } else { |
362 properties = func_or_properties; | 446 properties = func_or_properties; |
363 } | 447 } |
364 tests.setup(func, properties); | 448 tests.setup(func, properties); |
365 output.setup(properties); | 449 output.setup(properties); |
366 } | 450 } |
367 | 451 |
368 function done() { | 452 function done() { |
369 tests.end_wait(); | 453 tests.end_wait(); |
370 } | 454 } |
371 | 455 |
372 function generate_tests(func, args) { | 456 function generate_tests(func, args, properties) { |
373 forEach(args, function(x) | 457 forEach(args, function(x, i) |
374 { | 458 { |
375 var name = x[0]; | 459 var name = x[0]; |
376 test(function() | 460 test(function() |
377 { | 461 { |
378 func.apply(this, x.slice(1)); | 462 func.apply(this, x.slice(1)); |
379 }, name); | 463 }, |
| 464 name, |
| 465 Array.isArray(properties) ? properties[i] : properties)
; |
380 }); | 466 }); |
381 } | 467 } |
382 | 468 |
383 function on_event(object, event, callback) | 469 function on_event(object, event, callback) |
384 { | 470 { |
385 object.addEventListener(event, callback, false); | 471 object.addEventListener(event, callback, false); |
386 } | 472 } |
387 | 473 |
388 expose(test, 'test'); | 474 expose(test, 'test'); |
389 expose(async_test, 'async_test'); | 475 expose(async_test, 'async_test'); |
390 expose(generate_tests, 'generate_tests'); | 476 expose(generate_tests, 'generate_tests'); |
391 expose(setup, 'setup'); | 477 expose(setup, 'setup'); |
392 expose(done, 'done'); | 478 expose(done, 'done'); |
393 expose(on_event, 'on_event'); | 479 expose(on_event, 'on_event'); |
394 | 480 |
395 /* | 481 /* |
396 * Return a string truncated to the given length, with ... added at the end | 482 * Return a string truncated to the given length, with ... added at the end |
397 * if it was longer. | 483 * if it was longer. |
398 */ | 484 */ |
399 function truncate(s, len) | 485 function truncate(s, len) |
400 { | 486 { |
401 if (s.length > len) { | 487 if (s.length > len) { |
402 return s.substring(0, len - 3) + "..."; | 488 return s.substring(0, len - 3) + "..."; |
403 } | 489 } |
404 return s; | 490 return s; |
405 } | 491 } |
406 | 492 |
407 function format_string(str) { | |
408 for (var i = 0; i < 32; i++) { | |
409 var replace = "\\"; | |
410 switch (i) { | |
411 case 0: replace += "0"; break; | |
412 case 1: replace += "x01"; break; | |
413 case 2: replace += "x02"; break; | |
414 case 3: replace += "x03"; break; | |
415 case 4: replace += "x04"; break; | |
416 case 5: replace += "x05"; break; | |
417 case 6: replace += "x06"; break; | |
418 case 7: replace += "x07"; break; | |
419 case 8: replace += "b"; break; | |
420 case 9: replace += "t"; break; | |
421 case 10: replace += "n"; break; | |
422 case 11: replace += "v"; break; | |
423 case 12: replace += "f"; break; | |
424 case 13: replace += "r"; break; | |
425 case 14: replace += "x0e"; break; | |
426 case 15: replace += "x0f"; break; | |
427 case 16: replace += "x10"; break; | |
428 case 17: replace += "x11"; break; | |
429 case 18: replace += "x12"; break; | |
430 case 19: replace += "x13"; break; | |
431 case 20: replace += "x14"; break; | |
432 case 21: replace += "x15"; break; | |
433 case 22: replace += "x16"; break; | |
434 case 23: replace += "x17"; break; | |
435 case 24: replace += "x18"; break; | |
436 case 25: replace += "x19"; break; | |
437 case 26: replace += "x1a"; break; | |
438 case 27: replace += "x1b"; break; | |
439 case 28: replace += "x1c"; break; | |
440 case 29: replace += "x1d"; break; | |
441 case 30: replace += "x1e"; break; | |
442 case 31: replace += "x1f"; break; | |
443 } | |
444 str = str.replace(RegExp(String.fromCharCode(i), "g"), replace); | |
445 } | |
446 return str.replace(/"/g, '\\"') | |
447 } | |
448 | |
449 /* | 493 /* |
450 * Convert a value to a nice, human-readable string | 494 * Convert a value to a nice, human-readable string |
451 */ | 495 */ |
452 function format_value(val) | 496 function format_value(val, seen) |
453 { | 497 { |
| 498 » if (!seen) { |
| 499 » seen = []; |
| 500 } |
| 501 if (typeof val === "object" && val !== null) |
| 502 { |
| 503 if (seen.indexOf(val) >= 0) |
| 504 { |
| 505 return "[...]"; |
| 506 } |
| 507 » seen.push(val); |
| 508 } |
454 if (Array.isArray(val)) | 509 if (Array.isArray(val)) |
455 { | 510 { |
456 return "[" + val.map(format_value).join(", ") + "]"; | 511 return "[" + val.map(function(x) {return format_value(x, seen)}).joi
n(", ") + "]"; |
457 } | 512 } |
458 | 513 |
459 switch (typeof val) | 514 switch (typeof val) |
460 { | 515 { |
461 case "string": | 516 case "string": |
462 return '"' + format_string(val) + '"'; | 517 val = val.replace("\\", "\\\\"); |
| 518 for (var i = 0; i < 32; i++) |
| 519 { |
| 520 var replace = "\\"; |
| 521 switch (i) { |
| 522 case 0: replace += "0"; break; |
| 523 case 1: replace += "x01"; break; |
| 524 case 2: replace += "x02"; break; |
| 525 case 3: replace += "x03"; break; |
| 526 case 4: replace += "x04"; break; |
| 527 case 5: replace += "x05"; break; |
| 528 case 6: replace += "x06"; break; |
| 529 case 7: replace += "x07"; break; |
| 530 case 8: replace += "b"; break; |
| 531 case 9: replace += "t"; break; |
| 532 case 10: replace += "n"; break; |
| 533 case 11: replace += "v"; break; |
| 534 case 12: replace += "f"; break; |
| 535 case 13: replace += "r"; break; |
| 536 case 14: replace += "x0e"; break; |
| 537 case 15: replace += "x0f"; break; |
| 538 case 16: replace += "x10"; break; |
| 539 case 17: replace += "x11"; break; |
| 540 case 18: replace += "x12"; break; |
| 541 case 19: replace += "x13"; break; |
| 542 case 20: replace += "x14"; break; |
| 543 case 21: replace += "x15"; break; |
| 544 case 22: replace += "x16"; break; |
| 545 case 23: replace += "x17"; break; |
| 546 case 24: replace += "x18"; break; |
| 547 case 25: replace += "x19"; break; |
| 548 case 26: replace += "x1a"; break; |
| 549 case 27: replace += "x1b"; break; |
| 550 case 28: replace += "x1c"; break; |
| 551 case 29: replace += "x1d"; break; |
| 552 case 30: replace += "x1e"; break; |
| 553 case 31: replace += "x1f"; break; |
| 554 } |
| 555 val = val.replace(RegExp(String.fromCharCode(i), "g"), replace); |
| 556 } |
| 557 return '"' + val.replace(/"/g, '\\"') + '"'; |
463 case "boolean": | 558 case "boolean": |
464 case "undefined": | 559 case "undefined": |
465 return String(val); | 560 return String(val); |
466 case "number": | 561 case "number": |
467 // In JavaScript, -0 === 0 and String(-0) == "0", so we have to | 562 // In JavaScript, -0 === 0 and String(-0) == "0", so we have to |
468 // special-case. | 563 // special-case. |
469 if (val === -0 && 1/val === -Infinity) | 564 if (val === -0 && 1/val === -Infinity) |
470 { | 565 { |
471 return "-0"; | 566 return "-0"; |
472 } | 567 } |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
555 return x === y; | 650 return x === y; |
556 } | 651 } |
557 } | 652 } |
558 | 653 |
559 function assert_equals(actual, expected, description) | 654 function assert_equals(actual, expected, description) |
560 { | 655 { |
561 /* | 656 /* |
562 * Test if two primitives are equal or two objects | 657 * Test if two primitives are equal or two objects |
563 * are the same object | 658 * are the same object |
564 */ | 659 */ |
| 660 if (typeof actual != typeof expected) |
| 661 { |
| 662 assert(false, "assert_equals", description, |
| 663 "expected (" + typeof expected + ") ${expected} but go
t (" + typeof actual + ") ${actual}", |
| 664 {expected:expected, actual:actual}); |
| 665 return; |
| 666 } |
565 assert(same_value(actual, expected), "assert_equals", description, | 667 assert(same_value(actual, expected), "assert_equals", description, |
566 "expected ${expected} but got ${act
ual}", | 668 "expected ${expected} but got ${act
ual}", |
567 {expected:expected, actual:actual})
; | 669 {expected:expected, actual:actual})
; |
568 }; | 670 }; |
569 expose(assert_equals, "assert_equals"); | 671 expose(assert_equals, "assert_equals"); |
570 | 672 |
571 function assert_not_equals(actual, expected, description) | 673 function assert_not_equals(actual, expected, description) |
572 { | 674 { |
573 /* | 675 /* |
574 * Test if two primitives are unequal or two objects | 676 * Test if two primitives are unequal or two objects |
575 * are different objects | 677 * are different objects |
576 */ | 678 */ |
577 assert(!same_value(actual, expected), "assert_not_equals", description, | 679 assert(!same_value(actual, expected), "assert_not_equals", description, |
578 "got disallowed value ${actual}", | 680 "got disallowed value ${actual}", |
579 {actual:actual}); | 681 {actual:actual}); |
580 }; | 682 }; |
581 expose(assert_not_equals, "assert_not_equals"); | 683 expose(assert_not_equals, "assert_not_equals"); |
582 | 684 |
583 function assert_in_array(actual, expected, description) | 685 function assert_in_array(actual, expected, description) |
584 { | 686 { |
585 assert(expected.indexOf(actual) != -1, "assert_in_array", description, | 687 assert(expected.indexOf(actual) != -1, "assert_in_array", description, |
586 "value ${actual} not in array ${e
xpected}", | 688 "value ${actual} not in array ${e
xpected}", |
587 {actual:actual, expected:expected
}); | 689 {actual:actual, expected:expected
}); |
588 } | 690 } |
589 expose(assert_in_array, "assert_in_array"); | 691 expose(assert_in_array, "assert_in_array"); |
590 | 692 |
591 function assert_object_equals(actual, expected, description) | 693 function assert_object_equals(actual, expected, description) |
592 { | 694 { |
593 //This needs to be improved a great deal | 695 //This needs to be improved a great deal |
594 function check_equal(expected, actual, stack) | 696 function check_equal(actual, expected, stack) |
595 { | 697 { |
596 stack.push(actual); | 698 stack.push(actual); |
597 | 699 |
598 var p; | 700 var p; |
599 for (p in actual) | 701 for (p in actual) |
600 { | 702 { |
601 assert(expected.hasOwnProperty(p), "assert_object_equals", desc
ription, | 703 assert(expected.hasOwnProperty(p), "assert_object_equals", desc
ription, |
602 "unexpected property ${p}",
{p:p}); | 704 "unexpected property ${p}",
{p:p}); |
603 | 705 |
604 if (typeof actual[p] === "object" && actual[p] !== null) | 706 if (typeof actual[p] === "object" && actual[p] !== null) |
605 { | 707 { |
606 if (stack.indexOf(actual[p]) === -1) | 708 if (stack.indexOf(actual[p]) === -1) |
607 { | 709 { |
608 check_equal(actual[p], expected[p], stack); | 710 check_equal(actual[p], expected[p], stack); |
609 } | 711 } |
610 } | 712 } |
611 else | 713 else |
612 { | 714 { |
613 assert(actual[p] === expected[p], "assert_object_equals", d
escription, | 715 assert(same_value(actual[p], expected[p]), "assert_object_e
quals", description, |
614 "property ${p} expected $
{expected} got ${actual}", | 716 "property ${p} expected $
{expected} got ${actual}", |
615 {p:p, expected:expected,
actual:actual}); | 717 {p:p, expected:expected,
actual:actual}); |
616 } | 718 } |
617 } | 719 } |
618 for (p in expected) | 720 for (p in expected) |
619 { | 721 { |
620 assert(actual.hasOwnProperty(p), | 722 assert(actual.hasOwnProperty(p), |
621 "assert_object_equals", description, | 723 "assert_object_equals", description, |
622 "expected property ${p} missing", {p:p}); | 724 "expected property ${p} missing", {p:p}); |
623 } | 725 } |
(...skipping 10 matching lines...) Expand all Loading... |
634 "lengths differ, expected ${expected} got ${actual}", | 736 "lengths differ, expected ${expected} got ${actual}", |
635 {expected:expected.length, actual:actual.length}); | 737 {expected:expected.length, actual:actual.length}); |
636 | 738 |
637 for (var i=0; i < actual.length; i++) | 739 for (var i=0; i < actual.length; i++) |
638 { | 740 { |
639 assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i), | 741 assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i), |
640 "assert_array_equals", description, | 742 "assert_array_equals", description, |
641 "property ${i}, property expected to be $expected but was $ac
tual", | 743 "property ${i}, property expected to be $expected but was $ac
tual", |
642 {i:i, expected:expected.hasOwnProperty(i) ? "present" : "miss
ing", | 744 {i:i, expected:expected.hasOwnProperty(i) ? "present" : "miss
ing", |
643 actual:actual.hasOwnProperty(i) ? "present" : "missing"}); | 745 actual:actual.hasOwnProperty(i) ? "present" : "missing"}); |
644 assert(expected[i] === actual[i], | 746 assert(same_value(expected[i], actual[i]), |
645 "assert_array_equals", description, | 747 "assert_array_equals", description, |
646 "property ${i}, expected ${expected} but got ${actual}", | 748 "property ${i}, expected ${expected} but got ${actual}", |
647 {i:i, expected:expected[i], actual:actual[i]}); | 749 {i:i, expected:expected[i], actual:actual[i]}); |
648 } | 750 } |
649 } | 751 } |
650 expose(assert_array_equals, "assert_array_equals"); | 752 expose(assert_array_equals, "assert_array_equals"); |
651 | 753 |
652 function assert_approx_equals(actual, expected, epsilon, description) | 754 function assert_approx_equals(actual, expected, epsilon, description) |
653 { | 755 { |
654 /* | 756 /* |
655 * Test if two primitive numbers are equal withing +/- epsilon | 757 * Test if two primitive numbers are equal withing +/- epsilon |
656 */ | 758 */ |
657 assert(typeof actual === "number", | 759 assert(typeof actual === "number", |
658 "assert_approx_equals", description, | 760 "assert_approx_equals", description, |
659 "expected a number but got a ${type_actual}", | 761 "expected a number but got a ${type_actual}", |
660 {type_actual:typeof actual}); | 762 {type_actual:typeof actual}); |
661 | 763 |
662 assert(Math.abs(actual - expected) <= epsilon, | 764 assert(Math.abs(actual - expected) <= epsilon, |
663 "assert_approx_equals", description, | 765 "assert_approx_equals", description, |
664 "expected ${expected} +/- ${epsilon} but got ${actual}", | 766 "expected ${expected} +/- ${epsilon} but got ${actual}", |
665 {expected:expected, actual:actual, epsilon:epsilon}); | 767 {expected:expected, actual:actual, epsilon:epsilon}); |
666 }; | 768 }; |
667 expose(assert_approx_equals, "assert_approx_equals"); | 769 expose(assert_approx_equals, "assert_approx_equals"); |
668 | 770 |
| 771 function assert_less_than(actual, expected, description) |
| 772 { |
| 773 /* |
| 774 * Test if a primitive number is less than another |
| 775 */ |
| 776 assert(typeof actual === "number", |
| 777 "assert_less_than", description, |
| 778 "expected a number but got a ${type_actual}", |
| 779 {type_actual:typeof actual}); |
| 780 |
| 781 assert(actual < expected, |
| 782 "assert_less_than", description, |
| 783 "expected a number less than ${expected} but got ${actual}", |
| 784 {expected:expected, actual:actual}); |
| 785 }; |
| 786 expose(assert_less_than, "assert_less_than"); |
| 787 |
| 788 function assert_greater_than(actual, expected, description) |
| 789 { |
| 790 /* |
| 791 * Test if a primitive number is greater than another |
| 792 */ |
| 793 assert(typeof actual === "number", |
| 794 "assert_greater_than", description, |
| 795 "expected a number but got a ${type_actual}", |
| 796 {type_actual:typeof actual}); |
| 797 |
| 798 assert(actual > expected, |
| 799 "assert_greater_than", description, |
| 800 "expected a number greater than ${expected} but got ${actual}", |
| 801 {expected:expected, actual:actual}); |
| 802 }; |
| 803 expose(assert_greater_than, "assert_greater_than"); |
| 804 |
| 805 function assert_less_than_equal(actual, expected, description) |
| 806 { |
| 807 /* |
| 808 * Test if a primitive number is less than or equal to another |
| 809 */ |
| 810 assert(typeof actual === "number", |
| 811 "assert_less_than_equal", description, |
| 812 "expected a number but got a ${type_actual}", |
| 813 {type_actual:typeof actual}); |
| 814 |
| 815 assert(actual <= expected, |
| 816 "assert_less_than", description, |
| 817 "expected a number less than or equal to ${expected} but got ${ac
tual}", |
| 818 {expected:expected, actual:actual}); |
| 819 }; |
| 820 expose(assert_less_than_equal, "assert_less_than_equal"); |
| 821 |
| 822 function assert_greater_than_equal(actual, expected, description) |
| 823 { |
| 824 /* |
| 825 * Test if a primitive number is greater than or equal to another |
| 826 */ |
| 827 assert(typeof actual === "number", |
| 828 "assert_greater_than_equal", description, |
| 829 "expected a number but got a ${type_actual}", |
| 830 {type_actual:typeof actual}); |
| 831 |
| 832 assert(actual >= expected, |
| 833 "assert_greater_than_equal", description, |
| 834 "expected a number greater than or equal to ${expected} but got $
{actual}", |
| 835 {expected:expected, actual:actual}); |
| 836 }; |
| 837 expose(assert_greater_than_equal, "assert_greater_than_equal"); |
| 838 |
669 function assert_regexp_match(actual, expected, description) { | 839 function assert_regexp_match(actual, expected, description) { |
670 /* | 840 /* |
671 * Test if a string (actual) matches a regexp (expected) | 841 * Test if a string (actual) matches a regexp (expected) |
672 */ | 842 */ |
673 assert(expected.test(actual), | 843 assert(expected.test(actual), |
674 "assert_regexp_match", description, | 844 "assert_regexp_match", description, |
675 "expected ${expected} but got ${actual}", | 845 "expected ${expected} but got ${actual}", |
676 {expected:expected, actual:actual}); | 846 {expected:expected, actual:actual}); |
677 } | 847 } |
678 expose(assert_regexp_match, "assert_regexp_match"); | 848 expose(assert_regexp_match, "assert_regexp_match"); |
679 | 849 |
| 850 function assert_class_string(object, class_string, description) { |
| 851 assert_equals({}.toString.call(object), "[object " + class_string + "]", |
| 852 description); |
| 853 } |
| 854 expose(assert_class_string, "assert_class_string"); |
| 855 |
680 | 856 |
681 function _assert_own_property(name) { | 857 function _assert_own_property(name) { |
682 return function(object, property_name, description) | 858 return function(object, property_name, description) |
683 { | 859 { |
684 assert(object.hasOwnProperty(property_name), | 860 assert(object.hasOwnProperty(property_name), |
685 name, description, | 861 name, description, |
686 "expected property ${p} missing", {p:property_name}); | 862 "expected property ${p} missing", {p:property_name}); |
687 }; | 863 }; |
688 } | 864 } |
689 expose(_assert_own_property("assert_exists"), "assert_exists"); | 865 expose(_assert_own_property("assert_exists"), "assert_exists"); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
722 expose(_assert_inherits("assert_inherits"), "assert_inherits"); | 898 expose(_assert_inherits("assert_inherits"), "assert_inherits"); |
723 expose(_assert_inherits("assert_idl_attribute"), "assert_idl_attribute"); | 899 expose(_assert_inherits("assert_idl_attribute"), "assert_idl_attribute"); |
724 | 900 |
725 function assert_readonly(object, property_name, description) | 901 function assert_readonly(object, property_name, description) |
726 { | 902 { |
727 var initial_value = object[property_name]; | 903 var initial_value = object[property_name]; |
728 try { | 904 try { |
729 //Note that this can have side effects in the case where | 905 //Note that this can have side effects in the case where |
730 //the property has PutForwards | 906 //the property has PutForwards |
731 object[property_name] = initial_value + "a"; //XXX use some other v
alue here? | 907 object[property_name] = initial_value + "a"; //XXX use some other v
alue here? |
732 assert(object[property_name] === initial_value, | 908 assert(same_value(object[property_name], initial_value), |
733 "assert_readonly", description, | 909 "assert_readonly", description, |
734 "changing property ${p} succeeded", | 910 "changing property ${p} succeeded", |
735 {p:property_name}); | 911 {p:property_name}); |
736 } | 912 } |
737 finally | 913 finally |
738 { | 914 { |
739 object[property_name] = initial_value; | 915 object[property_name] = initial_value; |
740 } | 916 } |
741 }; | 917 }; |
742 expose(assert_readonly, "assert_readonly"); | 918 expose(assert_readonly, "assert_readonly"); |
743 | 919 |
744 function assert_throws(code, func, description) | 920 function assert_throws(code, func, description) |
745 { | 921 { |
746 try | 922 try |
747 { | 923 { |
748 func.call(this); | 924 func.call(this); |
749 assert(false, "assert_throws", description, | 925 assert(false, "assert_throws", description, |
750 "${func} did not throw", {func:func}); | 926 "${func} did not throw", {func:func}); |
751 } | 927 } |
752 catch(e) | 928 catch(e) |
753 { | 929 { |
754 if (e instanceof AssertionError) { | 930 if (e instanceof AssertionError) { |
755 throw(e); | 931 throw(e); |
756 } | 932 } |
| 933 if (code === null) |
| 934 { |
| 935 return; |
| 936 } |
757 if (typeof code === "object") | 937 if (typeof code === "object") |
758 { | 938 { |
759 assert(typeof e == "object" && "name" in e && e.name == code.nam
e, | 939 assert(typeof e == "object" && "name" in e && e.name == code.nam
e, |
760 "assert_throws", description, | 940 "assert_throws", description, |
761 "${func} threw ${actual} (${actual_name}) expected ${expe
cted} (${expected_name})", | 941 "${func} threw ${actual} (${actual_name}) expected ${expe
cted} (${expected_name})", |
762 {func:func, actual:e, actual_name:e.name, | 942 {func:func, actual:e, actual_name:e.name, |
763 expected:code, | 943 expected:code, |
764 expected_name:code.name}); | 944 expected_name:code.name}); |
765 return; | 945 return; |
766 } | 946 } |
767 var required_props = {}; | 947 |
768 required_props.code = { | 948 var code_name_map = { |
769 INDEX_SIZE_ERR: 1, | 949 INDEX_SIZE_ERR: 'IndexSizeError', |
770 HIERARCHY_REQUEST_ERR: 3, | 950 HIERARCHY_REQUEST_ERR: 'HierarchyRequestError', |
771 WRONG_DOCUMENT_ERR: 4, | 951 WRONG_DOCUMENT_ERR: 'WrongDocumentError', |
772 INVALID_CHARACTER_ERR: 5, | 952 INVALID_CHARACTER_ERR: 'InvalidCharacterError', |
773 NO_MODIFICATION_ALLOWED_ERR: 7, | 953 NO_MODIFICATION_ALLOWED_ERR: 'NoModificationAllowedError', |
774 NOT_FOUND_ERR: 8, | 954 NOT_FOUND_ERR: 'NotFoundError', |
775 NOT_SUPPORTED_ERR: 9, | 955 NOT_SUPPORTED_ERR: 'NotSupportedError', |
776 INVALID_STATE_ERR: 11, | 956 INVALID_STATE_ERR: 'InvalidStateError', |
777 SYNTAX_ERR: 12, | 957 SYNTAX_ERR: 'SyntaxError', |
778 INVALID_MODIFICATION_ERR: 13, | 958 INVALID_MODIFICATION_ERR: 'InvalidModificationError', |
779 NAMESPACE_ERR: 14, | 959 NAMESPACE_ERR: 'NamespaceError', |
780 INVALID_ACCESS_ERR: 15, | 960 INVALID_ACCESS_ERR: 'InvalidAccessError', |
781 TYPE_MISMATCH_ERR: 17, | 961 TYPE_MISMATCH_ERR: 'TypeMismatchError', |
782 SECURITY_ERR: 18, | 962 SECURITY_ERR: 'SecurityError', |
783 NETWORK_ERR: 19, | 963 NETWORK_ERR: 'NetworkError', |
784 ABORT_ERR: 20, | 964 ABORT_ERR: 'AbortError', |
785 URL_MISMATCH_ERR: 21, | 965 URL_MISMATCH_ERR: 'URLMismatchError', |
786 QUOTA_EXCEEDED_ERR: 22, | 966 QUOTA_EXCEEDED_ERR: 'QuotaExceededError', |
787 TIMEOUT_ERR: 23, | 967 TIMEOUT_ERR: 'TimeoutError', |
788 INVALID_NODE_TYPE_ERR: 24, | 968 INVALID_NODE_TYPE_ERR: 'InvalidNodeTypeError', |
789 DATA_CLONE_ERR: 25, | 969 DATA_CLONE_ERR: 'DataCloneError' |
790 }[code]; | 970 }; |
791 if (required_props.code === undefined) | 971 |
| 972 var name = code in code_name_map ? code_name_map[code] : code; |
| 973 |
| 974 var name_code_map = { |
| 975 IndexSizeError: 1, |
| 976 HierarchyRequestError: 3, |
| 977 WrongDocumentError: 4, |
| 978 InvalidCharacterError: 5, |
| 979 NoModificationAllowedError: 7, |
| 980 NotFoundError: 8, |
| 981 NotSupportedError: 9, |
| 982 InvalidStateError: 11, |
| 983 SyntaxError: 12, |
| 984 InvalidModificationError: 13, |
| 985 NamespaceError: 14, |
| 986 InvalidAccessError: 15, |
| 987 TypeMismatchError: 17, |
| 988 SecurityError: 18, |
| 989 NetworkError: 19, |
| 990 AbortError: 20, |
| 991 URLMismatchError: 21, |
| 992 QuotaExceededError: 22, |
| 993 TimeoutError: 23, |
| 994 InvalidNodeTypeError: 24, |
| 995 DataCloneError: 25, |
| 996 |
| 997 UnknownError: 0, |
| 998 ConstraintError: 0, |
| 999 DataError: 0, |
| 1000 TransactionInactiveError: 0, |
| 1001 ReadOnlyError: 0, |
| 1002 VersionError: 0 |
| 1003 }; |
| 1004 |
| 1005 if (!(name in name_code_map)) |
792 { | 1006 { |
793 throw new AssertionError('Test bug: unrecognized DOMException co
de "' + code + '" passed to assert_throws()'); | 1007 throw new AssertionError('Test bug: unrecognized DOMException co
de "' + code + '" passed to assert_throws()'); |
794 } | 1008 } |
795 required_props[code] = required_props.code; | 1009 |
796 //Uncomment this when the latest version of every browser | 1010 var required_props = { code: name_code_map[name] }; |
797 //actually implements the spec; otherwise it just creates | 1011 |
798 //zillions of failures. Also do required_props.type. | 1012 if (required_props.code === 0 |
799 //required_props.name = code; | 1013 || ("name" in e && e.name !== e.name.toUpperCase() && e.name !== "DO
MException")) |
800 // | 1014 { |
| 1015 // New style exception: also test the name property. |
| 1016 required_props.name = name; |
| 1017 } |
| 1018 |
801 //We'd like to test that e instanceof the appropriate interface, | 1019 //We'd like to test that e instanceof the appropriate interface, |
802 //but we can't, because we don't know what window it was created | 1020 //but we can't, because we don't know what window it was created |
803 //in. It might be an instanceof the appropriate interface on some | 1021 //in. It might be an instanceof the appropriate interface on some |
804 //unknown other window. TODO: Work around this somehow? | 1022 //unknown other window. TODO: Work around this somehow? |
805 | 1023 |
806 assert(typeof e == "object", | 1024 assert(typeof e == "object", |
807 "assert_throws", description, | 1025 "assert_throws", description, |
808 "${func} threw ${e} with type ${type}, not an object", | 1026 "${func} threw ${e} with type ${type}, not an object", |
809 {func:func, e:e, type:typeof e}); | 1027 {func:func, e:e, type:typeof e}); |
810 | 1028 |
811 for (var prop in required_props) | 1029 for (var prop in required_props) |
812 { | 1030 { |
813 assert(typeof e == "object" && prop in e && e[prop] == required_
props[prop], | 1031 assert(typeof e == "object" && prop in e && e[prop] == required_
props[prop], |
814 "assert_throws", description, | 1032 "assert_throws", description, |
815 "${func} threw ${e} that is not a DOMException " + code +
": property ${prop} is equal to ${actual}, expected ${expected}", | 1033 "${func} threw ${e} that is not a DOMException " + code +
": property ${prop} is equal to ${actual}, expected ${expected}", |
816 {func:func, e:e, prop:prop, actual:e[prop], expected:requ
ired_props[prop]}); | 1034 {func:func, e:e, prop:prop, actual:e[prop], expected:requ
ired_props[prop]}); |
817 } | 1035 } |
818 } | 1036 } |
819 } | 1037 } |
820 expose(assert_throws, "assert_throws"); | 1038 expose(assert_throws, "assert_throws"); |
821 | 1039 |
822 function assert_unreached(description) { | 1040 function assert_unreached(description) { |
823 assert(false, "assert_unreached", description, | 1041 assert(false, "assert_unreached", description, |
824 "Reached unreachable code"); | 1042 "Reached unreachable code"); |
825 } | 1043 } |
826 expose(assert_unreached, "assert_unreached"); | 1044 expose(assert_unreached, "assert_unreached"); |
827 | 1045 |
| 1046 function assert_any(assert_func, actual, expected_array) |
| 1047 { |
| 1048 var args = [].slice.call(arguments, 3) |
| 1049 var errors = [] |
| 1050 var passed = false; |
| 1051 forEach(expected_array, |
| 1052 function(expected) |
| 1053 { |
| 1054 try { |
| 1055 assert_func.apply(this, [actual, expected].concat(args)) |
| 1056 passed = true; |
| 1057 } catch(e) { |
| 1058 errors.push(e.message); |
| 1059 } |
| 1060 }); |
| 1061 if (!passed) { |
| 1062 throw new AssertionError(errors.join("\n\n")); |
| 1063 } |
| 1064 } |
| 1065 expose(assert_any, "assert_any"); |
| 1066 |
828 function Test(name, properties) | 1067 function Test(name, properties) |
829 { | 1068 { |
830 this.name = name; | 1069 this.name = name; |
831 this.status = this.NOTRUN; | 1070 this.status = this.NOTRUN; |
832 this.timeout_id = null; | 1071 this.timeout_id = null; |
833 this.is_done = false; | 1072 this.is_done = false; |
834 | 1073 |
| 1074 this.properties = properties; |
835 this.timeout_length = properties.timeout ? properties.timeout : settings
.test_timeout; | 1075 this.timeout_length = properties.timeout ? properties.timeout : settings
.test_timeout; |
836 | 1076 |
837 this.message = null; | 1077 this.message = null; |
838 | 1078 |
839 var this_obj = this; | 1079 var this_obj = this; |
840 this.steps = []; | 1080 this.steps = []; |
841 | 1081 |
842 tests.push(this); | 1082 tests.push(this); |
843 } | 1083 } |
844 | 1084 |
845 Test.prototype = { | 1085 Test.statuses = { |
846 PASS:0, | 1086 PASS:0, |
847 FAIL:1, | 1087 FAIL:1, |
848 TIMEOUT:2, | 1088 TIMEOUT:2, |
849 NOTRUN:3 | 1089 NOTRUN:3 |
850 }; | 1090 }; |
851 | 1091 |
| 1092 Test.prototype = merge({}, Test.statuses); |
| 1093 |
| 1094 Test.prototype.structured_clone = function() |
| 1095 { |
| 1096 if(!this._structured_clone) |
| 1097 { |
| 1098 var msg = this.message; |
| 1099 msg = msg ? String(msg) : msg; |
| 1100 this._structured_clone = merge({ |
| 1101 name:String(this.name), |
| 1102 status:this.status, |
| 1103 message:msg |
| 1104 }, Test.statuses); |
| 1105 } |
| 1106 return this._structured_clone; |
| 1107 }; |
852 | 1108 |
853 Test.prototype.step = function(func, this_obj) | 1109 Test.prototype.step = function(func, this_obj) |
854 { | 1110 { |
855 //In case the test has already failed | 1111 //In case the test has already failed |
856 if (this.status !== this.NOTRUN) | 1112 if (this.status !== this.NOTRUN) |
857 { | 1113 { |
858 return; | 1114 return; |
859 } | 1115 } |
860 | 1116 |
861 tests.started = true; | 1117 tests.started = true; |
862 | 1118 |
863 if (this.timeout_id === null) { | 1119 if (this.timeout_id === null) { |
864 this.set_timeout(); | 1120 this.set_timeout(); |
865 } | 1121 } |
866 | 1122 |
867 this.steps.push(func); | 1123 this.steps.push(func); |
868 | 1124 |
869 if (arguments.length === 1) | 1125 if (arguments.length === 1) |
870 { | 1126 { |
871 this_obj = this; | 1127 this_obj = this; |
872 } | 1128 } |
873 | 1129 |
874 try | 1130 try |
875 { | 1131 { |
876 func.apply(this_obj, Array.prototype.slice.call(arguments, 2)); | 1132 return func.apply(this_obj, Array.prototype.slice.call(arguments, 2)
); |
877 } | 1133 } |
878 catch(e) | 1134 catch(e) |
879 { | 1135 { |
880 //This can happen if something called synchronously invoked another | 1136 //This can happen if something called synchronously invoked another |
881 //step | 1137 //step |
882 if (this.status !== this.NOTRUN) | 1138 if (this.status !== this.NOTRUN) |
883 { | 1139 { |
884 return; | 1140 return; |
885 } | 1141 } |
886 this.status = this.FAIL; | 1142 this.status = this.FAIL; |
887 this.message = e.message; | 1143 this.message = (typeof e === "object" && e !== null) ? e.message : e
; |
888 if (typeof e.stack != "undefined" && typeof e.message == "string") { | 1144 if (typeof e.stack != "undefined" && typeof e.message == "string") { |
889 //Try to make it more informative for some exceptions, at least | 1145 //Try to make it more informative for some exceptions, at least |
890 //in Gecko and WebKit. This results in a stack dump instead of | 1146 //in Gecko and WebKit. This results in a stack dump instead of |
891 //just errors like "Cannot read property 'parentNode' of null" | 1147 //just errors like "Cannot read property 'parentNode' of null" |
892 //or "root is null". Makes it a lot longer, of course. | 1148 //or "root is null". Makes it a lot longer, of course. |
893 this.message += "(stack: " + e.stack + ")"; | 1149 this.message += "(stack: " + e.stack + ")"; |
894 } | 1150 } |
895 this.done(); | 1151 this.done(); |
896 if (debug && e.constructor !== AssertionError) { | 1152 if (debug && e.constructor !== AssertionError) { |
897 throw e; | 1153 throw e; |
(...skipping 10 matching lines...) Expand all Loading... |
908 this_obj = test_this; | 1164 this_obj = test_this; |
909 } | 1165 } |
910 | 1166 |
911 return function() | 1167 return function() |
912 { | 1168 { |
913 test_this.step.apply(test_this, [func, this_obj].concat( | 1169 test_this.step.apply(test_this, [func, this_obj].concat( |
914 Array.prototype.slice.call(arguments))); | 1170 Array.prototype.slice.call(arguments))); |
915 }; | 1171 }; |
916 }; | 1172 }; |
917 | 1173 |
| 1174 Test.prototype.step_func_done = function(func, this_obj) |
| 1175 { |
| 1176 var test_this = this; |
| 1177 |
| 1178 if (arguments.length === 1) |
| 1179 { |
| 1180 this_obj = test_this; |
| 1181 } |
| 1182 |
| 1183 return function() |
| 1184 { |
| 1185 test_this.step.apply(test_this, [func, this_obj].concat( |
| 1186 Array.prototype.slice.call(arguments))); |
| 1187 test_this.done(); |
| 1188 }; |
| 1189 }; |
| 1190 |
918 Test.prototype.set_timeout = function() | 1191 Test.prototype.set_timeout = function() |
919 { | 1192 { |
920 var this_obj = this; | 1193 var this_obj = this; |
921 this.timeout_id = setTimeout(function() | 1194 this.timeout_id = setTimeout(function() |
922 { | 1195 { |
923 this_obj.timeout(); | 1196 this_obj.timeout(); |
924 }, this.timeout_length); | 1197 }, this.timeout_length); |
925 }; | 1198 }; |
926 | 1199 |
927 Test.prototype.timeout = function() | 1200 Test.prototype.timeout = function() |
(...skipping 21 matching lines...) Expand all Loading... |
949 | 1222 |
950 /* | 1223 /* |
951 * Harness | 1224 * Harness |
952 */ | 1225 */ |
953 | 1226 |
954 function TestsStatus() | 1227 function TestsStatus() |
955 { | 1228 { |
956 this.status = null; | 1229 this.status = null; |
957 this.message = null; | 1230 this.message = null; |
958 } | 1231 } |
959 TestsStatus.prototype = { | 1232 |
| 1233 TestsStatus.statuses = { |
960 OK:0, | 1234 OK:0, |
961 ERROR:1, | 1235 ERROR:1, |
962 TIMEOUT:2 | 1236 TIMEOUT:2 |
963 }; | 1237 }; |
964 | 1238 |
| 1239 TestsStatus.prototype = merge({}, TestsStatus.statuses); |
| 1240 |
| 1241 TestsStatus.prototype.structured_clone = function() |
| 1242 { |
| 1243 if(!this._structured_clone) |
| 1244 { |
| 1245 var msg = this.message; |
| 1246 msg = msg ? String(msg) : msg; |
| 1247 this._structured_clone = merge({ |
| 1248 status:this.status, |
| 1249 message:msg |
| 1250 }, TestsStatus.statuses); |
| 1251 } |
| 1252 return this._structured_clone; |
| 1253 }; |
| 1254 |
965 function Tests() | 1255 function Tests() |
966 { | 1256 { |
967 this.tests = []; | 1257 this.tests = []; |
968 this.num_pending = 0; | 1258 this.num_pending = 0; |
969 | 1259 |
970 this.phases = { | 1260 this.phases = { |
971 INITIAL:0, | 1261 INITIAL:0, |
972 SETUP:1, | 1262 SETUP:1, |
973 HAVE_TESTS:2, | 1263 HAVE_TESTS:2, |
974 HAVE_RESULTS:3, | 1264 HAVE_RESULTS:3, |
975 COMPLETE:4 | 1265 COMPLETE:4 |
976 }; | 1266 }; |
977 this.phase = this.phases.INITIAL; | 1267 this.phase = this.phases.INITIAL; |
978 | 1268 |
| 1269 this.properties = {}; |
| 1270 |
979 //All tests can't be done until the load event fires | 1271 //All tests can't be done until the load event fires |
980 this.all_loaded = false; | 1272 this.all_loaded = false; |
981 this.wait_for_finish = false; | 1273 this.wait_for_finish = false; |
982 this.processing_callbacks = false; | 1274 this.processing_callbacks = false; |
983 | 1275 |
984 this.timeout_length = settings.timeout; | 1276 this.timeout_length = settings.timeout; |
985 this.timeout_id = null; | 1277 this.timeout_id = null; |
986 this.set_timeout(); | |
987 | 1278 |
988 this.start_callbacks = []; | 1279 this.start_callbacks = []; |
989 this.test_done_callbacks = []; | 1280 this.test_done_callbacks = []; |
990 this.all_done_callbacks = []; | 1281 this.all_done_callbacks = []; |
991 | 1282 |
992 this.status = new TestsStatus(); | 1283 this.status = new TestsStatus(); |
993 | 1284 |
994 var this_obj = this; | 1285 var this_obj = this; |
995 | 1286 |
996 on_event(window, "load", | 1287 on_event(window, "load", |
997 function() | 1288 function() |
998 { | 1289 { |
999 this_obj.all_loaded = true; | 1290 this_obj.all_loaded = true; |
1000 if (this_obj.all_done()) | 1291 if (this_obj.all_done()) |
1001 { | 1292 { |
1002 this_obj.complete(); | 1293 this_obj.complete(); |
1003 } | 1294 } |
1004 }); | 1295 }); |
1005 this.properties = {}; | 1296 |
| 1297 this.set_timeout(); |
1006 } | 1298 } |
1007 | 1299 |
1008 Tests.prototype.setup = function(func, properties) | 1300 Tests.prototype.setup = function(func, properties) |
1009 { | 1301 { |
1010 if (this.phase >= this.phases.HAVE_RESULTS) | 1302 if (this.phase >= this.phases.HAVE_RESULTS) |
1011 { | 1303 { |
1012 return; | 1304 return; |
1013 } | 1305 } |
1014 if (this.phase < this.phases.SETUP) | 1306 if (this.phase < this.phases.SETUP) |
1015 { | 1307 { |
1016 this.phase = this.phases.SETUP; | 1308 this.phase = this.phases.SETUP; |
1017 } | 1309 } |
1018 | 1310 |
1019 for (var p in properties) | 1311 for (var p in properties) |
1020 { | 1312 { |
1021 if (properties.hasOwnProperty(p)) | 1313 if (properties.hasOwnProperty(p)) |
1022 { | 1314 { |
1023 this.properties[p] = properties[p]; | 1315 this.properties[p] = properties[p]; |
1024 } | 1316 } |
1025 } | 1317 } |
1026 | 1318 |
1027 if (properties.timeout) | 1319 if (properties.timeout) |
1028 { | 1320 { |
1029 this.timeout_length = properties.timeout; | 1321 this.timeout_length = properties.timeout; |
1030 this.set_timeout(); | |
1031 } | 1322 } |
1032 if (properties.explicit_done) | 1323 if (properties.explicit_done) |
1033 { | 1324 { |
1034 this.wait_for_finish = true; | 1325 this.wait_for_finish = true; |
1035 } | 1326 } |
| 1327 if (properties.explicit_timeout) { |
| 1328 this.timeout_length = null; |
| 1329 } |
1036 | 1330 |
1037 if (func) | 1331 if (func) |
1038 { | 1332 { |
1039 try | 1333 try |
1040 { | 1334 { |
1041 func(); | 1335 func(); |
1042 } catch(e) | 1336 } catch(e) |
1043 { | 1337 { |
1044 this.status.status = this.status.ERROR; | 1338 this.status.status = this.status.ERROR; |
1045 this.status.message = e; | 1339 this.status.message = e; |
1046 }; | 1340 }; |
1047 } | 1341 } |
| 1342 this.set_timeout(); |
1048 }; | 1343 }; |
1049 | 1344 |
1050 Tests.prototype.set_timeout = function() | 1345 Tests.prototype.set_timeout = function() |
1051 { | 1346 { |
1052 var this_obj = this; | 1347 var this_obj = this; |
1053 clearTimeout(this.timeout_id); | 1348 clearTimeout(this.timeout_id); |
1054 this.timeout_id = setTimeout(function() { | 1349 if (this.timeout_length !== null) |
1055 this_obj.timeout(); | 1350 { |
1056 }, this.timeout_length); | 1351 this.timeout_id = setTimeout(function() { |
| 1352 this_obj.timeout(); |
| 1353 }, this.timeout_length); |
| 1354 } |
1057 }; | 1355 }; |
1058 | 1356 |
1059 Tests.prototype.timeout = function() { | 1357 Tests.prototype.timeout = function() { |
1060 this.status.status = this.status.TIMEOUT; | 1358 this.status.status = this.status.TIMEOUT; |
1061 this.complete(); | 1359 this.complete(); |
1062 }; | 1360 }; |
1063 | 1361 |
1064 Tests.prototype.end_wait = function() | 1362 Tests.prototype.end_wait = function() |
1065 { | 1363 { |
1066 this.wait_for_finish = false; | 1364 this.wait_for_finish = false; |
1067 if (this.all_done()) { | 1365 if (this.all_done()) { |
1068 this.complete(); | 1366 this.complete(); |
1069 } | 1367 } |
1070 }; | 1368 }; |
1071 | 1369 |
1072 Tests.prototype.push = function(test) | 1370 Tests.prototype.push = function(test) |
1073 { | 1371 { |
1074 if (this.phase < this.phases.HAVE_TESTS) { | 1372 if (this.phase < this.phases.HAVE_TESTS) { |
1075 this.notify_start(); | 1373 this.start(); |
1076 } | 1374 } |
1077 this.num_pending++; | 1375 this.num_pending++; |
1078 this.tests.push(test); | 1376 this.tests.push(test); |
1079 }; | 1377 }; |
1080 | 1378 |
1081 Tests.prototype.all_done = function() { | 1379 Tests.prototype.all_done = function() { |
1082 return (this.all_loaded && this.num_pending === 0 && | 1380 return (this.all_loaded && this.num_pending === 0 && |
1083 !this.wait_for_finish && !this.processing_callbacks); | 1381 !this.wait_for_finish && !this.processing_callbacks); |
1084 }; | 1382 }; |
1085 | 1383 |
1086 Tests.prototype.start = function() { | 1384 Tests.prototype.start = function() { |
1087 this.phase = this.phases.HAVE_TESTS; | 1385 this.phase = this.phases.HAVE_TESTS; |
1088 this.notify_start(); | 1386 this.notify_start(); |
1089 }; | 1387 }; |
1090 | 1388 |
1091 Tests.prototype.notify_start = function() { | 1389 Tests.prototype.notify_start = function() { |
1092 var this_obj = this; | 1390 var this_obj = this; |
1093 forEach (this.start_callbacks, | 1391 forEach (this.start_callbacks, |
1094 function(callback) | 1392 function(callback) |
1095 { | 1393 { |
1096 callback(this_obj.properties); | 1394 callback(this_obj.properties); |
1097 }); | 1395 }); |
1098 forEach(ancestor_windows(), | 1396 forEach_windows( |
1099 function(w) | 1397 function(w, is_same_origin) |
1100 { | 1398 { |
1101 if(w.start_callback) | 1399 if(is_same_origin && w.start_callback) |
1102 { | 1400 { |
1103 try | 1401 try |
1104 { | 1402 { |
1105 w.start_callback(this_obj.properties); | 1403 w.start_callback(this_obj.properties); |
1106 } | 1404 } |
1107 catch(e) | 1405 catch(e) |
1108 { | 1406 { |
1109 if (debug) | 1407 if (debug) |
1110 { | 1408 { |
1111 throw(e); | 1409 throw(e); |
1112 } | 1410 } |
1113 } | 1411 } |
1114 } | 1412 } |
| 1413 if (supports_post_message(w) && w !== self) |
| 1414 { |
| 1415 w.postMessage({ |
| 1416 type: "start", |
| 1417 properties: this_obj.properties |
| 1418 }, "*"); |
| 1419 } |
1115 }); | 1420 }); |
1116 }; | 1421 }; |
1117 | 1422 |
1118 Tests.prototype.result = function(test) | 1423 Tests.prototype.result = function(test) |
1119 { | 1424 { |
1120 if (this.phase > this.phases.HAVE_RESULTS) | 1425 if (this.phase > this.phases.HAVE_RESULTS) |
1121 { | 1426 { |
1122 return; | 1427 return; |
1123 } | 1428 } |
1124 this.phase = this.phases.HAVE_RESULTS; | 1429 this.phase = this.phases.HAVE_RESULTS; |
1125 this.num_pending--; | 1430 this.num_pending--; |
1126 this.notify_result(test); | 1431 this.notify_result(test); |
1127 }; | 1432 }; |
1128 | 1433 |
1129 Tests.prototype.notify_result = function(test) { | 1434 Tests.prototype.notify_result = function(test) { |
1130 var this_obj = this; | 1435 var this_obj = this; |
1131 this.processing_callbacks = true; | 1436 this.processing_callbacks = true; |
1132 forEach(this.test_done_callbacks, | 1437 forEach(this.test_done_callbacks, |
1133 function(callback) | 1438 function(callback) |
1134 { | 1439 { |
1135 callback(test, this_obj); | 1440 callback(test, this_obj); |
1136 }); | 1441 }); |
1137 | 1442 |
1138 forEach(ancestor_windows(), | 1443 forEach_windows( |
1139 function(w) | 1444 function(w, is_same_origin) |
1140 { | 1445 { |
1141 if(w.result_callback) | 1446 if(is_same_origin && w.result_callback) |
1142 { | 1447 { |
1143 try | 1448 try |
1144 { | 1449 { |
1145 w.result_callback(test); | 1450 w.result_callback(test); |
1146 } | 1451 } |
1147 catch(e) | 1452 catch(e) |
1148 { | 1453 { |
1149 if(debug) { | 1454 if(debug) { |
1150 throw e; | 1455 throw e; |
1151 } | 1456 } |
1152 } | 1457 } |
1153 } | 1458 } |
| 1459 if (supports_post_message(w) && w !== self) |
| 1460 { |
| 1461 w.postMessage({ |
| 1462 type: "result", |
| 1463 test: test.structured_clone() |
| 1464 }, "*"); |
| 1465 } |
1154 }); | 1466 }); |
1155 this.processing_callbacks = false; | 1467 this.processing_callbacks = false; |
1156 if (this_obj.all_done()) | 1468 if (this_obj.all_done()) |
1157 { | 1469 { |
1158 this_obj.complete(); | 1470 this_obj.complete(); |
1159 } | 1471 } |
1160 }; | 1472 }; |
1161 | 1473 |
1162 Tests.prototype.complete = function() { | 1474 Tests.prototype.complete = function() { |
1163 if (this.phase === this.phases.COMPLETE) { | 1475 if (this.phase === this.phases.COMPLETE) { |
1164 return; | 1476 return; |
1165 } | 1477 } |
1166 this.phase = this.phases.COMPLETE; | 1478 this.phase = this.phases.COMPLETE; |
| 1479 var this_obj = this; |
| 1480 this.tests.forEach( |
| 1481 function(x) |
| 1482 { |
| 1483 if(x.status === x.NOTRUN) |
| 1484 { |
| 1485 this_obj.notify_result(x); |
| 1486 } |
| 1487 } |
| 1488 ); |
1167 this.notify_complete(); | 1489 this.notify_complete(); |
1168 }; | 1490 }; |
1169 | 1491 |
1170 Tests.prototype.notify_complete = function() | 1492 Tests.prototype.notify_complete = function() |
1171 { | 1493 { |
1172 clearTimeout(this.timeout_id); | 1494 clearTimeout(this.timeout_id); |
1173 var this_obj = this; | 1495 var this_obj = this; |
| 1496 var tests = map(this_obj.tests, |
| 1497 function(test) |
| 1498 { |
| 1499 return test.structured_clone(); |
| 1500 }); |
1174 if (this.status.status === null) | 1501 if (this.status.status === null) |
1175 { | 1502 { |
1176 this.status.status = this.status.OK; | 1503 this.status.status = this.status.OK; |
1177 } | 1504 } |
1178 | 1505 |
1179 forEach (this.all_done_callbacks, | 1506 forEach (this.all_done_callbacks, |
1180 function(callback) | 1507 function(callback) |
1181 { | 1508 { |
1182 callback(this_obj.tests, this_obj.status); | 1509 callback(this_obj.tests, this_obj.status); |
1183 }); | 1510 }); |
1184 | 1511 |
1185 forEach(ancestor_windows(), | 1512 forEach_windows( |
1186 function(w) | 1513 function(w, is_same_origin) |
1187 { | 1514 { |
1188 if(w.completion_callback) | 1515 if(is_same_origin && w.completion_callback) |
1189 { | 1516 { |
1190 try | 1517 try |
1191 { | 1518 { |
1192 w.completion_callback(this_obj.tests, this_obj.statu
s); | 1519 w.completion_callback(this_obj.tests, this_obj.statu
s); |
1193 } | 1520 } |
1194 catch(e) | 1521 catch(e) |
1195 { | 1522 { |
1196 if (debug) | 1523 if (debug) |
1197 { | 1524 { |
1198 throw e; | 1525 throw e; |
1199 } | 1526 } |
1200 } | 1527 } |
1201 } | 1528 } |
| 1529 if (supports_post_message(w) && w !== self) |
| 1530 { |
| 1531 w.postMessage({ |
| 1532 type: "complete", |
| 1533 tests: tests, |
| 1534 status: this_obj.status.structured_clone() |
| 1535 }, "*"); |
| 1536 } |
1202 }); | 1537 }); |
1203 }; | 1538 }; |
1204 | 1539 |
1205 var tests = new Tests(); | 1540 var tests = new Tests(); |
1206 | 1541 |
| 1542 function timeout() { |
| 1543 if (tests.timeout_length === null) |
| 1544 { |
| 1545 tests.timeout(); |
| 1546 } |
| 1547 } |
| 1548 expose(timeout, 'timeout'); |
| 1549 |
1207 function add_start_callback(callback) { | 1550 function add_start_callback(callback) { |
1208 tests.start_callbacks.push(callback); | 1551 tests.start_callbacks.push(callback); |
1209 } | 1552 } |
1210 | 1553 |
1211 function add_result_callback(callback) | 1554 function add_result_callback(callback) |
1212 { | 1555 { |
1213 tests.test_done_callbacks.push(callback); | 1556 tests.test_done_callbacks.push(callback); |
1214 } | 1557 } |
1215 | 1558 |
1216 function add_completion_callback(callback) | 1559 function add_completion_callback(callback) |
1217 { | 1560 { |
1218 tests.all_done_callbacks.push(callback); | 1561 tests.all_done_callbacks.push(callback); |
1219 } | 1562 } |
1220 | 1563 |
1221 expose(add_start_callback, 'add_start_callback'); | 1564 expose(add_start_callback, 'add_start_callback'); |
1222 expose(add_result_callback, 'add_result_callback'); | 1565 expose(add_result_callback, 'add_result_callback'); |
1223 expose(add_completion_callback, 'add_completion_callback'); | 1566 expose(add_completion_callback, 'add_completion_callback'); |
1224 | 1567 |
1225 /* | 1568 /* |
1226 * Output listener | 1569 * Output listener |
1227 */ | 1570 */ |
1228 | 1571 |
1229 function Output() { | 1572 function Output() { |
1230 this.output_document = null; | 1573 this.output_document = document; |
1231 this.output_node = null; | 1574 this.output_node = null; |
1232 this.done_count = 0; | 1575 this.done_count = 0; |
1233 this.enabled = settings.output; | 1576 this.enabled = settings.output; |
1234 this.phase = this.INITIAL; | 1577 this.phase = this.INITIAL; |
1235 } | 1578 } |
1236 | 1579 |
1237 Output.prototype.INITIAL = 0; | 1580 Output.prototype.INITIAL = 0; |
1238 Output.prototype.STARTED = 1; | 1581 Output.prototype.STARTED = 1; |
1239 Output.prototype.HAVE_RESULTS = 2; | 1582 Output.prototype.HAVE_RESULTS = 2; |
1240 Output.prototype.COMPLETE = 3; | 1583 Output.prototype.COMPLETE = 3; |
(...skipping 17 matching lines...) Expand all Loading... |
1258 if (properties.output_document) { | 1601 if (properties.output_document) { |
1259 this.output_document = properties.output_document; | 1602 this.output_document = properties.output_document; |
1260 } else { | 1603 } else { |
1261 this.output_document = document; | 1604 this.output_document = document; |
1262 } | 1605 } |
1263 this.phase = this.STARTED; | 1606 this.phase = this.STARTED; |
1264 }; | 1607 }; |
1265 | 1608 |
1266 Output.prototype.resolve_log = function() | 1609 Output.prototype.resolve_log = function() |
1267 { | 1610 { |
1268 if (!this.output_document) { | 1611 var output_document; |
| 1612 if (typeof this.output_document === "function") |
| 1613 { |
| 1614 output_document = this.output_document.apply(undefined); |
| 1615 } else |
| 1616 { |
| 1617 output_document = this.output_document; |
| 1618 } |
| 1619 if (!output_document) |
| 1620 { |
1269 return; | 1621 return; |
1270 } | 1622 } |
1271 var node = this.output_document.getElementById("log"); | 1623 var node = output_document.getElementById("log"); |
1272 if (node) { | 1624 if (node) |
| 1625 { |
| 1626 this.output_document = output_document; |
1273 this.output_node = node; | 1627 this.output_node = node; |
1274 } | 1628 } |
1275 }; | 1629 }; |
1276 | 1630 |
1277 Output.prototype.show_status = function(test) | 1631 Output.prototype.show_status = function(test) |
1278 { | 1632 { |
1279 if (this.phase < this.STARTED) | 1633 if (this.phase < this.STARTED) |
1280 { | 1634 { |
1281 this.init(); | 1635 this.init(); |
1282 } | 1636 } |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1391 { | 1745 { |
1392 e.preventDefault(); | 1746 e.preventDefault(); |
1393 return; | 1747 return; |
1394 } | 1748 } |
1395 var result_class = element.parentNode.getAttrib
ute("class"); | 1749 var result_class = element.parentNode.getAttrib
ute("class"); |
1396 var style_element = output_document.querySelect
or("style#hide-" + result_class); | 1750 var style_element = output_document.querySelect
or("style#hide-" + result_class); |
1397 var input_element = element.querySelector("inpu
t"); | 1751 var input_element = element.querySelector("inpu
t"); |
1398 if (!style_element && !input_element.checked) { | 1752 if (!style_element && !input_element.checked) { |
1399 style_element = output_document.createEleme
ntNS(xhtml_ns, "style"); | 1753 style_element = output_document.createEleme
ntNS(xhtml_ns, "style"); |
1400 style_element.id = "hide-" + result_class; | 1754 style_element.id = "hide-" + result_class; |
1401 style_element.innerHTML = "table#results >
tbody > tr."+result_class+"{display:none}"; | 1755 style_element.textContent = "table#results
> tbody > tr."+result_class+"{display:none}"; |
1402 output_document.body.appendChild(style_elem
ent); | 1756 output_document.body.appendChild(style_elem
ent); |
1403 } else if (style_element && input_element.check
ed) { | 1757 } else if (style_element && input_element.check
ed) { |
1404 style_element.parentNode.removeChild(style_
element); | 1758 style_element.parentNode.removeChild(style_
element); |
1405 } | 1759 } |
1406 }); | 1760 }); |
1407 }); | 1761 }); |
1408 | 1762 |
1409 // This use of innerHTML plus manual escaping is not recommended in | 1763 // This use of innerHTML plus manual escaping is not recommended in |
1410 // general, but is necessary here for performance. Using textContent | 1764 // general, but is necessary here for performance. Using textContent |
1411 // on each individual <td> adds tens of seconds of execution time for | 1765 // on each individual <td> adds tens of seconds of execution time for |
1412 // large test suites (tens of thousands of tests). | 1766 // large test suites (tens of thousands of tests). |
1413 function escape_html(s) | 1767 function escape_html(s) |
1414 { | 1768 { |
1415 return s.replace(/\&/g, "&") | 1769 return s.replace(/\&/g, "&") |
1416 .replace(/</g, "<") | 1770 .replace(/</g, "<") |
1417 .replace(/"/g, """) | 1771 .replace(/"/g, """) |
1418 .replace(/'/g, "'"); | 1772 .replace(/'/g, "'"); |
1419 } | 1773 } |
1420 | 1774 |
1421 log.appendChild(document.createElement("section")); | 1775 function has_assertions() |
1422 var html = "<h2>Details</h2><table id='results'>" | 1776 { |
1423 + "<thead><tr><th>Result</th><th>Test Name</th><th>Message</th></tr>
</thead>" | 1777 for (var i = 0; i < tests.length; i++) { |
| 1778 if (tests[i].properties.hasOwnProperty("assert")) { |
| 1779 return true; |
| 1780 } |
| 1781 } |
| 1782 return false; |
| 1783 } |
| 1784 |
| 1785 function get_assertion(test) |
| 1786 { |
| 1787 if (test.properties.hasOwnProperty("assert")) { |
| 1788 if (Array.isArray(test.properties.assert)) { |
| 1789 return test.properties.assert.join(' '); |
| 1790 } |
| 1791 return test.properties.assert; |
| 1792 } |
| 1793 return ''; |
| 1794 } |
| 1795 |
| 1796 log.appendChild(document.createElementNS(xhtml_ns, "section")); |
| 1797 var assertions = has_assertions(); |
| 1798 var html = "<h2>Details</h2><table id='results' " + (assertions ? "class
='assertions'" : "" ) + ">" |
| 1799 + "<thead><tr><th>Result</th><th>Test Name</th>" |
| 1800 + (assertions ? "<th>Assertion</th>" : "") |
| 1801 + "<th>Message</th></tr></thead>" |
1424 + "<tbody>"; | 1802 + "<tbody>"; |
1425 for (var i = 0; i < tests.length; i++) { | 1803 for (var i = 0; i < tests.length; i++) { |
1426 html += '<tr class="' | 1804 html += '<tr class="' |
1427 + escape_html(status_class(status_text[tests[i].status])) | 1805 + escape_html(status_class(status_text[tests[i].status])) |
1428 + '"><td>' | 1806 + '"><td>' |
1429 + escape_html(status_text[tests[i].status]) | 1807 + escape_html(status_text[tests[i].status]) |
1430 + "</td><td>" | 1808 + "</td><td>" |
1431 + escape_html(format_string(tests[i].name)) | 1809 + escape_html(tests[i].name) |
1432 + "</td><td>" | 1810 + "</td><td>" |
1433 + escape_html(tests[i].message ? format_string(tests[i].message)
: " ") | 1811 + (assertions ? escape_html(get_assertion(tests[i])) + "</td><td
>" : "") |
| 1812 + escape_html(tests[i].message ? tests[i].message : " ") |
1434 + "</td></tr>"; | 1813 + "</td></tr>"; |
1435 } | 1814 } |
1436 log.lastChild.innerHTML = html + "</tbody></table>"; | 1815 html += "</tbody></table>"; |
| 1816 try { |
| 1817 log.lastChild.innerHTML = html; |
| 1818 } catch (e) { |
| 1819 log.appendChild(document.createElementNS(xhtml_ns, "p")) |
| 1820 .textContent = "Setting innerHTML for the log threw an exception.
"; |
| 1821 log.appendChild(document.createElementNS(xhtml_ns, "pre")) |
| 1822 .textContent = html; |
| 1823 } |
1437 }; | 1824 }; |
1438 | 1825 |
1439 var output = new Output(); | 1826 var output = new Output(); |
1440 add_start_callback(function (properties) {output.init(properties);}); | 1827 add_start_callback(function (properties) {output.init(properties);}); |
1441 add_result_callback(function (test) {output.show_status(tests);}); | 1828 add_result_callback(function (test) {output.show_status(tests);}); |
1442 add_completion_callback(function (tests, harness_status) {output.show_result
s(tests, harness_status);}); | 1829 add_completion_callback(function (tests, harness_status) {output.show_result
s(tests, harness_status);}); |
1443 | 1830 |
1444 /* | 1831 /* |
1445 * Template code | 1832 * Template code |
1446 * | 1833 * |
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1733 { | 2120 { |
1734 if (!(components[i] in target)) | 2121 if (!(components[i] in target)) |
1735 { | 2122 { |
1736 target[components[i]] = {}; | 2123 target[components[i]] = {}; |
1737 } | 2124 } |
1738 target = target[components[i]]; | 2125 target = target[components[i]]; |
1739 } | 2126 } |
1740 target[components[components.length - 1]] = object; | 2127 target[components[components.length - 1]] = object; |
1741 } | 2128 } |
1742 | 2129 |
1743 function ancestor_windows() { | 2130 function forEach_windows(callback) { |
1744 //Get the windows [self ... top] as an array | 2131 // Iterate of the the windows [self ... top, opener]. The callback is pa
ssed |
1745 if ("result_cache" in ancestor_windows) | 2132 // two objects, the first one is the windows object itself, the second o
ne |
1746 { | 2133 // is a boolean indicating whether or not its on the same origin as the |
1747 return ancestor_windows.result_cache; | 2134 // current window. |
1748 } | 2135 var cache = forEach_windows.result_cache; |
1749 var rv = [self]; | 2136 if (!cache) { |
1750 var w = self; | 2137 cache = [[self, true]]; |
1751 while (w != w.parent) | 2138 var w = self; |
1752 { | 2139 var i = 0; |
1753 w = w.parent; | 2140 var so; |
1754 rv.push(w); | 2141 var origins = location.ancestorOrigins; |
1755 } | 2142 while (w != w.parent) |
1756 ancestor_windows.result_cache = rv; | 2143 { |
1757 return rv; | 2144 w = w.parent; |
1758 } | 2145 // In WebKit, calls to parent windows' properties that aren't on
the same |
1759 | 2146 // origin cause an error message to be displayed in the error co
nsole but |
| 2147 // don't throw an exception. This is a deviation from the curren
t HTML5 |
| 2148 // spec. See: https://bugs.webkit.org/show_bug.cgi?id=43504 |
| 2149 // The problem with WebKit's behavior is that it pollutes the er
ror console |
| 2150 // with error messages that can't be caught. |
| 2151 // |
| 2152 // This issue can be mitigated by relying on the (for now) propr
ietary |
| 2153 // `location.ancestorOrigins` property which returns an ordered
list of |
| 2154 // the origins of enclosing windows. See: |
| 2155 // http://trac.webkit.org/changeset/113945. |
| 2156 if(origins) { |
| 2157 so = (location.origin == origins[i]); |
| 2158 } |
| 2159 else |
| 2160 { |
| 2161 so = is_same_origin(w); |
| 2162 } |
| 2163 cache.push([w, so]); |
| 2164 i++; |
| 2165 } |
| 2166 w = window.opener; |
| 2167 if(w) |
| 2168 { |
| 2169 // window.opener isn't included in the `location.ancestorOrigins
` prop. |
| 2170 // We'll just have to deal with a simple check and an error msg
on WebKit |
| 2171 // browsers in this case. |
| 2172 cache.push([w, is_same_origin(w)]); |
| 2173 } |
| 2174 forEach_windows.result_cache = cache; |
| 2175 } |
| 2176 |
| 2177 forEach(cache, |
| 2178 function(a) |
| 2179 { |
| 2180 callback.apply(null, a); |
| 2181 }); |
| 2182 } |
| 2183 |
| 2184 function is_same_origin(w) { |
| 2185 try { |
| 2186 'random_prop' in w; |
| 2187 return true; |
| 2188 } catch(e) { |
| 2189 return false; |
| 2190 } |
| 2191 } |
| 2192 |
| 2193 function supports_post_message(w) |
| 2194 { |
| 2195 var supports; |
| 2196 var type; |
| 2197 // Given IE implements postMessage across nested iframes but not across |
| 2198 // windows or tabs, you can't infer cross-origin communication from the
presence |
| 2199 // of postMessage on the current window object only. |
| 2200 // |
| 2201 // Touching the postMessage prop on a window can throw if the window is |
| 2202 // not from the same origin AND post message is not supported in that |
| 2203 // browser. So just doing an existence test here won't do, you also need |
| 2204 // to wrap it in a try..cacth block. |
| 2205 try |
| 2206 { |
| 2207 type = typeof w.postMessage; |
| 2208 if (type === "function") |
| 2209 { |
| 2210 supports = true; |
| 2211 } |
| 2212 // IE8 supports postMessage, but implements it as a host object whic
h |
| 2213 // returns "object" as its `typeof`. |
| 2214 else if (type === "object") |
| 2215 { |
| 2216 supports = true; |
| 2217 } |
| 2218 // This is the case where postMessage isn't supported AND accessing
a |
| 2219 // window property across origins does NOT throw (e.g. old Safari br
owser). |
| 2220 else |
| 2221 { |
| 2222 supports = false; |
| 2223 } |
| 2224 } |
| 2225 catch(e) { |
| 2226 // This is the case where postMessage isn't supported AND accessing
a |
| 2227 // window property across origins throws (e.g. old Firefox browser). |
| 2228 supports = false; |
| 2229 } |
| 2230 return supports; |
| 2231 } |
1760 })(); | 2232 })(); |
1761 // vim: set expandtab shiftwidth=4 tabstop=4: | 2233 // vim: set expandtab shiftwidth=4 tabstop=4: |
OLD | NEW |