|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 // Explicitly set the default version. |
|
7 // See https://bugzilla.mozilla.org/show_bug.cgi?id=522760#c11 |
|
8 if (typeof version != 'undefined') |
|
9 { |
|
10 version(0); |
|
11 } |
|
12 |
|
13 var STATUS = "STATUS: "; |
|
14 var VERBOSE = false; |
|
15 var SECT_PREFIX = 'Section '; |
|
16 var SECT_SUFFIX = ' of test - '; |
|
17 var callStack = new Array(); |
|
18 |
|
19 var gDelayTestDriverEnd = false; |
|
20 |
|
21 var gTestcases = new Array(); |
|
22 var gTc = gTestcases.length; |
|
23 var BUGNUMBER = ''; |
|
24 var summary = ''; |
|
25 var description = ''; |
|
26 var expected = ''; |
|
27 var actual = ''; |
|
28 var msg = ''; |
|
29 |
|
30 var SECTION = ""; |
|
31 var VERSION = ""; |
|
32 var BUGNUMBER = ""; |
|
33 |
|
34 /* |
|
35 * constant strings |
|
36 */ |
|
37 var GLOBAL = this + ''; |
|
38 var PASSED = " PASSED! "; |
|
39 var FAILED = " FAILED! "; |
|
40 |
|
41 var DEBUG = false; |
|
42 |
|
43 var DESCRIPTION; |
|
44 var EXPECTED; |
|
45 |
|
46 /* |
|
47 * Signals to results.py that the current test case should be considered to |
|
48 * have passed if it doesn't throw an exception. |
|
49 * |
|
50 * When the test suite is run in the browser, this function gets overridden by |
|
51 * the same-named function in browser.js. |
|
52 */ |
|
53 function testPassesUnlessItThrows() { |
|
54 print(PASSED); |
|
55 } |
|
56 |
|
57 /* |
|
58 * wrapper for test case constructor that doesn't require the SECTION |
|
59 * argument. |
|
60 */ |
|
61 |
|
62 function AddTestCase( description, expect, actual ) { |
|
63 new TestCase( SECTION, description, expect, actual ); |
|
64 } |
|
65 |
|
66 /* |
|
67 * Set up test environment. |
|
68 * |
|
69 */ |
|
70 function startTest() { |
|
71 // print out bugnumber |
|
72 |
|
73 if ( BUGNUMBER ) { |
|
74 print ("BUGNUMBER: " + BUGNUMBER ); |
|
75 } |
|
76 } |
|
77 |
|
78 function TestCase(n, d, e, a) |
|
79 { |
|
80 this.name = n; |
|
81 this.description = d; |
|
82 this.expect = e; |
|
83 this.actual = a; |
|
84 this.passed = getTestCaseResult(e, a); |
|
85 this.reason = ''; |
|
86 this.bugnumber = typeof(BUGNUMER) != 'undefined' ? BUGNUMBER : ''; |
|
87 this.type = (typeof window == 'undefined' ? 'shell' : 'browser'); |
|
88 gTestcases[gTc++] = this; |
|
89 } |
|
90 |
|
91 gFailureExpected = false; |
|
92 |
|
93 TestCase.prototype.dump = function () { |
|
94 // let reftest handle error reporting, otherwise |
|
95 // output a summary line. |
|
96 if (typeof document != "object" || |
|
97 !document.location.href.match(/jsreftest.html/)) |
|
98 { |
|
99 dump('\njstest: ' + this.path + ' ' + |
|
100 'bug: ' + this.bugnumber + ' ' + |
|
101 'result: ' + (this.passed ? 'PASSED':'FAILED') + ' ' + |
|
102 'type: ' + this.type + ' ' + |
|
103 'description: ' + toPrinted(this.description) + ' ' + |
|
104 // 'expected: ' + toPrinted(this.expect) + ' ' + |
|
105 // 'actual: ' + toPrinted(this.actual) + ' ' + |
|
106 'reason: ' + toPrinted(this.reason) + '\n'); |
|
107 } |
|
108 }; |
|
109 |
|
110 TestCase.prototype.testPassed = (function TestCase_testPassed() { return this.passed; }); |
|
111 TestCase.prototype.testFailed = (function TestCase_testFailed() { return !this.passed; }); |
|
112 TestCase.prototype.testDescription = (function TestCase_testDescription() { return this.description + ' ' + this.reason; }); |
|
113 |
|
114 function getTestCases() |
|
115 { |
|
116 return gTestcases; |
|
117 } |
|
118 |
|
119 /* |
|
120 * The test driver searches for such a phrase in the test output. |
|
121 * If such phrase exists, it will set n as the expected exit code. |
|
122 */ |
|
123 function expectExitCode(n) |
|
124 { |
|
125 print('--- NOTE: IN THIS TESTCASE, WE EXPECT EXIT CODE ' + n + ' ---'); |
|
126 } |
|
127 |
|
128 /* |
|
129 * Statuses current section of a test |
|
130 */ |
|
131 function inSection(x) |
|
132 { |
|
133 return SECT_PREFIX + x + SECT_SUFFIX; |
|
134 } |
|
135 |
|
136 /* |
|
137 * Report a failure in the 'accepted' manner |
|
138 */ |
|
139 function reportFailure (msg) |
|
140 { |
|
141 var lines = msg.split ("\n"); |
|
142 var l; |
|
143 var funcName = currentFunc(); |
|
144 var prefix = (funcName) ? "[reported from " + funcName + "] ": ""; |
|
145 |
|
146 for (var i=0; i<lines.length; i++) |
|
147 print (FAILED + prefix + lines[i]); |
|
148 } |
|
149 |
|
150 /* |
|
151 * Print a non-failure message. |
|
152 */ |
|
153 function printStatus (msg) |
|
154 { |
|
155 /* js1_6 had... |
|
156 msg = String(msg); |
|
157 msg = msg.toString(); |
|
158 */ |
|
159 msg = msg.toString(); |
|
160 var lines = msg.split ("\n"); |
|
161 var l; |
|
162 |
|
163 for (var i=0; i<lines.length; i++) |
|
164 print (STATUS + lines[i]); |
|
165 } |
|
166 |
|
167 /* |
|
168 * Print a bugnumber message. |
|
169 */ |
|
170 function printBugNumber (num) |
|
171 { |
|
172 BUGNUMBER = num; |
|
173 print ('BUGNUMBER: ' + num); |
|
174 } |
|
175 |
|
176 function toPrinted(value) |
|
177 { |
|
178 value = String(value); |
|
179 value = value.replace(/\\n/g, 'NL') |
|
180 .replace(/\n/g, 'NL') |
|
181 .replace(/\\r/g, 'CR') |
|
182 .replace(/[^\x20-\x7E]+/g, escapeString); |
|
183 return value; |
|
184 } |
|
185 |
|
186 function escapeString (str) |
|
187 { |
|
188 var a, b, c, d; |
|
189 var len = str.length; |
|
190 var result = ""; |
|
191 var digits = ["0", "1", "2", "3", "4", "5", "6", "7", |
|
192 "8", "9", "A", "B", "C", "D", "E", "F"]; |
|
193 |
|
194 for (var i=0; i<len; i++) |
|
195 { |
|
196 var ch = str.charCodeAt(i); |
|
197 |
|
198 a = digits[ch & 0xf]; |
|
199 ch >>= 4; |
|
200 b = digits[ch & 0xf]; |
|
201 ch >>= 4; |
|
202 |
|
203 if (ch) |
|
204 { |
|
205 c = digits[ch & 0xf]; |
|
206 ch >>= 4; |
|
207 d = digits[ch & 0xf]; |
|
208 |
|
209 result += "\\u" + d + c + b + a; |
|
210 } |
|
211 else |
|
212 { |
|
213 result += "\\x" + b + a; |
|
214 } |
|
215 } |
|
216 |
|
217 return result; |
|
218 } |
|
219 |
|
220 /* |
|
221 * assertEq(actual, expected [, message]) |
|
222 * Throw if the two arguments are not the same. The sameness of two values |
|
223 * is determined as follows. If both values are zero, they are the same iff |
|
224 * their signs are the same. Otherwise, if both values are NaN, they are the |
|
225 * same. Otherwise, they are the same if they compare equal using ===. |
|
226 * see https://bugzilla.mozilla.org/show_bug.cgi?id=480199 and |
|
227 * https://bugzilla.mozilla.org/show_bug.cgi?id=515285 |
|
228 */ |
|
229 if (typeof assertEq == 'undefined') |
|
230 { |
|
231 var assertEq = |
|
232 function (actual, expected, message) |
|
233 { |
|
234 function SameValue(v1, v2) |
|
235 { |
|
236 if (v1 === 0 && v2 === 0) |
|
237 return 1 / v1 === 1 / v2; |
|
238 if (v1 !== v1 && v2 !== v2) |
|
239 return true; |
|
240 return v1 === v2; |
|
241 } |
|
242 if (!SameValue(actual, expected)) |
|
243 { |
|
244 throw new TypeError('Assertion failed: got "' + actual + '", expected "' + expected + |
|
245 (message ? ": " + message : "")); |
|
246 } |
|
247 }; |
|
248 } |
|
249 |
|
250 /* |
|
251 * Compare expected result to actual result, if they differ (in value and/or |
|
252 * type) report a failure. If description is provided, include it in the |
|
253 * failure report. |
|
254 */ |
|
255 function reportCompare (expected, actual, description) { |
|
256 var expected_t = typeof expected; |
|
257 var actual_t = typeof actual; |
|
258 var output = ""; |
|
259 |
|
260 if (typeof description == "undefined") |
|
261 { |
|
262 description = ''; |
|
263 } |
|
264 else if (VERBOSE) |
|
265 { |
|
266 printStatus ("Comparing '" + description + "'"); |
|
267 } |
|
268 |
|
269 if (expected_t != actual_t) |
|
270 { |
|
271 output += "Type mismatch, expected type " + expected_t + |
|
272 ", actual type " + actual_t + " "; |
|
273 } |
|
274 else if (VERBOSE) |
|
275 { |
|
276 printStatus ("Expected type '" + expected_t + "' matched actual " + |
|
277 "type '" + actual_t + "'"); |
|
278 } |
|
279 |
|
280 if (expected != actual) |
|
281 { |
|
282 output += "Expected value '" + toPrinted(expected) + |
|
283 "', Actual value '" + toPrinted(actual) + "' "; |
|
284 } |
|
285 else if (VERBOSE) |
|
286 { |
|
287 printStatus ("Expected value '" + toPrinted(expected) + |
|
288 "' matched actual value '" + toPrinted(actual) + "'"); |
|
289 } |
|
290 |
|
291 var testcase = new TestCase("unknown-test-name", description, expected, actual); |
|
292 testcase.reason = output; |
|
293 |
|
294 // if running under reftest, let it handle result reporting. |
|
295 if (typeof document != "object" || |
|
296 !document.location.href.match(/jsreftest.html/)) { |
|
297 if (testcase.passed) |
|
298 { |
|
299 print(PASSED + description); |
|
300 } |
|
301 else |
|
302 { |
|
303 reportFailure (description + " : " + output); |
|
304 } |
|
305 } |
|
306 return testcase.passed; |
|
307 } |
|
308 |
|
309 /* |
|
310 * Attempt to match a regular expression describing the result to |
|
311 * the actual result, if they differ (in value and/or |
|
312 * type) report a failure. If description is provided, include it in the |
|
313 * failure report. |
|
314 */ |
|
315 function reportMatch (expectedRegExp, actual, description) { |
|
316 var expected_t = "string"; |
|
317 var actual_t = typeof actual; |
|
318 var output = ""; |
|
319 |
|
320 if (typeof description == "undefined") |
|
321 { |
|
322 description = ''; |
|
323 } |
|
324 else if (VERBOSE) |
|
325 { |
|
326 printStatus ("Comparing '" + description + "'"); |
|
327 } |
|
328 |
|
329 if (expected_t != actual_t) |
|
330 { |
|
331 output += "Type mismatch, expected type " + expected_t + |
|
332 ", actual type " + actual_t + " "; |
|
333 } |
|
334 else if (VERBOSE) |
|
335 { |
|
336 printStatus ("Expected type '" + expected_t + "' matched actual " + |
|
337 "type '" + actual_t + "'"); |
|
338 } |
|
339 |
|
340 var matches = expectedRegExp.test(actual); |
|
341 if (!matches) |
|
342 { |
|
343 output += "Expected match to '" + toPrinted(expectedRegExp) + |
|
344 "', Actual value '" + toPrinted(actual) + "' "; |
|
345 } |
|
346 else if (VERBOSE) |
|
347 { |
|
348 printStatus ("Expected match to '" + toPrinted(expectedRegExp) + |
|
349 "' matched actual value '" + toPrinted(actual) + "'"); |
|
350 } |
|
351 |
|
352 var testcase = new TestCase("unknown-test-name", description, true, matches); |
|
353 testcase.reason = output; |
|
354 |
|
355 // if running under reftest, let it handle result reporting. |
|
356 if (typeof document != "object" || |
|
357 !document.location.href.match(/jsreftest.html/)) { |
|
358 if (testcase.passed) |
|
359 { |
|
360 print(PASSED + description); |
|
361 } |
|
362 else |
|
363 { |
|
364 reportFailure (description + " : " + output); |
|
365 } |
|
366 } |
|
367 return testcase.passed; |
|
368 } |
|
369 |
|
370 /* |
|
371 * Puts funcName at the top of the call stack. This stack is used to show |
|
372 * a function-reported-from field when reporting failures. |
|
373 */ |
|
374 function enterFunc (funcName) |
|
375 { |
|
376 if (!funcName.match(/\(\)$/)) |
|
377 funcName += "()"; |
|
378 |
|
379 callStack.push(funcName); |
|
380 } |
|
381 |
|
382 /* |
|
383 * Pops the top funcName off the call stack. funcName is optional, and can be |
|
384 * used to check push-pop balance. |
|
385 */ |
|
386 function exitFunc (funcName) |
|
387 { |
|
388 var lastFunc = callStack.pop(); |
|
389 |
|
390 if (funcName) |
|
391 { |
|
392 if (!funcName.match(/\(\)$/)) |
|
393 funcName += "()"; |
|
394 |
|
395 if (lastFunc != funcName) |
|
396 reportCompare(funcName, lastFunc, "Test driver failure wrong exit function "); |
|
397 } |
|
398 } |
|
399 |
|
400 /* |
|
401 * Peeks at the top of the call stack. |
|
402 */ |
|
403 function currentFunc() |
|
404 { |
|
405 return callStack[callStack.length - 1]; |
|
406 } |
|
407 |
|
408 /* |
|
409 Calculate the "order" of a set of data points {X: [], Y: []} |
|
410 by computing successive "derivatives" of the data until |
|
411 the data is exhausted or the derivative is linear. |
|
412 */ |
|
413 function BigO(data) |
|
414 { |
|
415 var order = 0; |
|
416 var origLength = data.X.length; |
|
417 |
|
418 while (data.X.length > 2) |
|
419 { |
|
420 var lr = new LinearRegression(data); |
|
421 if (lr.b > 1e-6) |
|
422 { |
|
423 // only increase the order if the slope |
|
424 // is "great" enough |
|
425 order++; |
|
426 } |
|
427 |
|
428 if (lr.r > 0.98 || lr.Syx < 1 || lr.b < 1e-6) |
|
429 { |
|
430 // terminate if close to a line lr.r |
|
431 // small error lr.Syx |
|
432 // small slope lr.b |
|
433 break; |
|
434 } |
|
435 data = dataDeriv(data); |
|
436 } |
|
437 |
|
438 if (2 == origLength - order) |
|
439 { |
|
440 order = Number.POSITIVE_INFINITY; |
|
441 } |
|
442 return order; |
|
443 |
|
444 function LinearRegression(data) |
|
445 { |
|
446 /* |
|
447 y = a + bx |
|
448 for data points (Xi, Yi); 0 <= i < n |
|
449 |
|
450 b = (n*SUM(XiYi) - SUM(Xi)*SUM(Yi))/(n*SUM(Xi*Xi) - SUM(Xi)*SUM(Xi)) |
|
451 a = (SUM(Yi) - b*SUM(Xi))/n |
|
452 */ |
|
453 var i; |
|
454 |
|
455 if (data.X.length != data.Y.length) |
|
456 { |
|
457 throw 'LinearRegression: data point length mismatch'; |
|
458 } |
|
459 if (data.X.length < 3) |
|
460 { |
|
461 throw 'LinearRegression: data point length < 2'; |
|
462 } |
|
463 var n = data.X.length; |
|
464 var X = data.X; |
|
465 var Y = data.Y; |
|
466 |
|
467 this.Xavg = 0; |
|
468 this.Yavg = 0; |
|
469 |
|
470 var SUM_X = 0; |
|
471 var SUM_XY = 0; |
|
472 var SUM_XX = 0; |
|
473 var SUM_Y = 0; |
|
474 var SUM_YY = 0; |
|
475 |
|
476 for (i = 0; i < n; i++) |
|
477 { |
|
478 SUM_X += X[i]; |
|
479 SUM_XY += X[i]*Y[i]; |
|
480 SUM_XX += X[i]*X[i]; |
|
481 SUM_Y += Y[i]; |
|
482 SUM_YY += Y[i]*Y[i]; |
|
483 } |
|
484 |
|
485 this.b = (n * SUM_XY - SUM_X * SUM_Y)/(n * SUM_XX - SUM_X * SUM_X); |
|
486 this.a = (SUM_Y - this.b * SUM_X)/n; |
|
487 |
|
488 this.Xavg = SUM_X/n; |
|
489 this.Yavg = SUM_Y/n; |
|
490 |
|
491 var SUM_Ydiff2 = 0; |
|
492 var SUM_Xdiff2 = 0; |
|
493 var SUM_XdiffYdiff = 0; |
|
494 |
|
495 for (i = 0; i < n; i++) |
|
496 { |
|
497 var Ydiff = Y[i] - this.Yavg; |
|
498 var Xdiff = X[i] - this.Xavg; |
|
499 |
|
500 SUM_Ydiff2 += Ydiff * Ydiff; |
|
501 SUM_Xdiff2 += Xdiff * Xdiff; |
|
502 SUM_XdiffYdiff += Xdiff * Ydiff; |
|
503 } |
|
504 |
|
505 var Syx2 = (SUM_Ydiff2 - Math.pow(SUM_XdiffYdiff/SUM_Xdiff2, 2))/(n - 2); |
|
506 var r2 = Math.pow((n*SUM_XY - SUM_X * SUM_Y), 2) / |
|
507 ((n*SUM_XX - SUM_X*SUM_X)*(n*SUM_YY-SUM_Y*SUM_Y)); |
|
508 |
|
509 this.Syx = Math.sqrt(Syx2); |
|
510 this.r = Math.sqrt(r2); |
|
511 |
|
512 } |
|
513 |
|
514 function dataDeriv(data) |
|
515 { |
|
516 if (data.X.length != data.Y.length) |
|
517 { |
|
518 throw 'length mismatch'; |
|
519 } |
|
520 var length = data.X.length; |
|
521 |
|
522 if (length < 2) |
|
523 { |
|
524 throw 'length ' + length + ' must be >= 2'; |
|
525 } |
|
526 var X = data.X; |
|
527 var Y = data.Y; |
|
528 |
|
529 var deriv = {X: [], Y: [] }; |
|
530 |
|
531 for (var i = 0; i < length - 1; i++) |
|
532 { |
|
533 deriv.X[i] = (X[i] + X[i+1])/2; |
|
534 deriv.Y[i] = (Y[i+1] - Y[i])/(X[i+1] - X[i]); |
|
535 } |
|
536 return deriv; |
|
537 } |
|
538 |
|
539 return 0; |
|
540 } |
|
541 |
|
542 function compareSource(expect, actual, summary) |
|
543 { |
|
544 // compare source |
|
545 var expectP = expect. |
|
546 replace(/([(){},.:\[\]])/mg, ' $1 '). |
|
547 replace(/(\w+)/mg, ' $1 '). |
|
548 replace(/<(\/)? (\w+) (\/)?>/mg, '<$1$2$3>'). |
|
549 replace(/\s+/mg, ' '). |
|
550 replace(/new (\w+)\s*\(\s*\)/mg, 'new $1'); |
|
551 |
|
552 var actualP = actual. |
|
553 replace(/([(){},.:\[\]])/mg, ' $1 '). |
|
554 replace(/(\w+)/mg, ' $1 '). |
|
555 replace(/<(\/)? (\w+) (\/)?>/mg, '<$1$2$3>'). |
|
556 replace(/\s+/mg, ' '). |
|
557 replace(/new (\w+)\s*\(\s*\)/mg, 'new $1'); |
|
558 |
|
559 print('expect:\n' + expectP); |
|
560 print('actual:\n' + actualP); |
|
561 |
|
562 reportCompare(expectP, actualP, summary); |
|
563 |
|
564 // actual must be compilable if expect is? |
|
565 try |
|
566 { |
|
567 var expectCompile = 'No Error'; |
|
568 var actualCompile; |
|
569 |
|
570 eval(expect); |
|
571 try |
|
572 { |
|
573 eval(actual); |
|
574 actualCompile = 'No Error'; |
|
575 } |
|
576 catch(ex1) |
|
577 { |
|
578 actualCompile = ex1 + ''; |
|
579 } |
|
580 reportCompare(expectCompile, actualCompile, |
|
581 summary + ': compile actual'); |
|
582 } |
|
583 catch(ex) |
|
584 { |
|
585 } |
|
586 } |
|
587 |
|
588 function optionsInit() { |
|
589 |
|
590 // record initial values to support resetting |
|
591 // options to their initial values |
|
592 options.initvalues = {}; |
|
593 |
|
594 // record values in a stack to support pushing |
|
595 // and popping options |
|
596 options.stackvalues = []; |
|
597 |
|
598 var optionNames = options().split(','); |
|
599 |
|
600 for (var i = 0; i < optionNames.length; i++) |
|
601 { |
|
602 var optionName = optionNames[i]; |
|
603 if (optionName) |
|
604 { |
|
605 options.initvalues[optionName] = ''; |
|
606 } |
|
607 } |
|
608 } |
|
609 |
|
610 function optionsClear() { |
|
611 |
|
612 // turn off current settings |
|
613 // except jit. |
|
614 var optionNames = options().split(','); |
|
615 for (var i = 0; i < optionNames.length; i++) |
|
616 { |
|
617 var optionName = optionNames[i]; |
|
618 if (optionName && |
|
619 optionName != "methodjit" && |
|
620 optionName != "methodjit_always" && |
|
621 optionName != "ion") |
|
622 { |
|
623 options(optionName); |
|
624 } |
|
625 } |
|
626 } |
|
627 |
|
628 function optionsPush() |
|
629 { |
|
630 var optionsframe = {}; |
|
631 |
|
632 options.stackvalues.push(optionsframe); |
|
633 |
|
634 var optionNames = options().split(','); |
|
635 |
|
636 for (var i = 0; i < optionNames.length; i++) |
|
637 { |
|
638 var optionName = optionNames[i]; |
|
639 if (optionName) |
|
640 { |
|
641 optionsframe[optionName] = ''; |
|
642 } |
|
643 } |
|
644 |
|
645 optionsClear(); |
|
646 } |
|
647 |
|
648 function optionsPop() |
|
649 { |
|
650 var optionsframe = options.stackvalues.pop(); |
|
651 |
|
652 optionsClear(); |
|
653 |
|
654 for (optionName in optionsframe) |
|
655 { |
|
656 options(optionName); |
|
657 } |
|
658 |
|
659 } |
|
660 |
|
661 function optionsReset() { |
|
662 |
|
663 try |
|
664 { |
|
665 optionsClear(); |
|
666 |
|
667 // turn on initial settings |
|
668 for (var optionName in options.initvalues) |
|
669 { |
|
670 if (!options.hasOwnProperty(optionName)) |
|
671 continue; |
|
672 options(optionName); |
|
673 } |
|
674 } |
|
675 catch(ex) |
|
676 { |
|
677 print('optionsReset: caught ' + ex); |
|
678 } |
|
679 |
|
680 } |
|
681 |
|
682 if (typeof options == 'function') |
|
683 { |
|
684 optionsInit(); |
|
685 optionsClear(); |
|
686 } |
|
687 |
|
688 function getTestCaseResult(expected, actual) |
|
689 { |
|
690 if (typeof expected != typeof actual) |
|
691 return false; |
|
692 if (typeof expected != 'number') |
|
693 // Note that many tests depend on the use of '==' here, not '==='. |
|
694 return actual == expected; |
|
695 |
|
696 // Distinguish NaN from other values. Using x != x comparisons here |
|
697 // works even if tests redefine isNaN. |
|
698 if (actual != actual) |
|
699 return expected != expected; |
|
700 if (expected != expected) |
|
701 return false; |
|
702 |
|
703 // Tolerate a certain degree of error. |
|
704 if (actual != expected) |
|
705 return Math.abs(actual - expected) <= 1E-10; |
|
706 |
|
707 // Here would be a good place to distinguish 0 and -0, if we wanted |
|
708 // to. However, doing so would introduce a number of failures in |
|
709 // areas where they don't seem important. For example, the WeekDay |
|
710 // function in ECMA-262 returns -0 for Sundays before the epoch, but |
|
711 // the Date functions in SpiderMonkey specified in terms of WeekDay |
|
712 // often don't. This seems unimportant. |
|
713 return true; |
|
714 } |
|
715 |
|
716 if (typeof dump == 'undefined') |
|
717 { |
|
718 if (typeof window == 'undefined' && |
|
719 typeof print == 'function') |
|
720 { |
|
721 dump = print; |
|
722 } |
|
723 else |
|
724 { |
|
725 dump = (function () {}); |
|
726 } |
|
727 } |
|
728 |
|
729 function test() { |
|
730 for ( gTc=0; gTc < gTestcases.length; gTc++ ) { |
|
731 // temporary hack to work around some unknown issue in 1.7 |
|
732 try |
|
733 { |
|
734 gTestcases[gTc].passed = writeTestCaseResult( |
|
735 gTestcases[gTc].expect, |
|
736 gTestcases[gTc].actual, |
|
737 gTestcases[gTc].description +" = "+ gTestcases[gTc].actual ); |
|
738 gTestcases[gTc].reason += ( gTestcases[gTc].passed ) ? "" : "wrong value "; |
|
739 } |
|
740 catch(e) |
|
741 { |
|
742 print('test(): empty testcase for gTc = ' + gTc + ' ' + e); |
|
743 } |
|
744 } |
|
745 stopTest(); |
|
746 return ( gTestcases ); |
|
747 } |
|
748 |
|
749 /* |
|
750 * Begin printing functions. These functions use the shell's |
|
751 * print function. When running tests in the browser, these |
|
752 * functions, override these functions with functions that use |
|
753 * document.write. |
|
754 */ |
|
755 |
|
756 function writeTestCaseResult( expect, actual, string ) { |
|
757 var passed = getTestCaseResult( expect, actual ); |
|
758 // if running under reftest, let it handle result reporting. |
|
759 if (typeof document != "object" || |
|
760 !document.location.href.match(/jsreftest.html/)) { |
|
761 writeFormattedResult( expect, actual, string, passed ); |
|
762 } |
|
763 return passed; |
|
764 } |
|
765 function writeFormattedResult( expect, actual, string, passed ) { |
|
766 var s = ( passed ? PASSED : FAILED ) + string + ' expected: ' + expect; |
|
767 print(s); |
|
768 return passed; |
|
769 } |
|
770 |
|
771 function writeHeaderToLog( string ) { |
|
772 print( string ); |
|
773 } |
|
774 /* end of print functions */ |
|
775 |
|
776 |
|
777 /* |
|
778 * When running in the shell, run the garbage collector after the |
|
779 * test has completed. |
|
780 */ |
|
781 |
|
782 function stopTest() { |
|
783 var gc; |
|
784 if ( gc != undefined ) { |
|
785 gc(); |
|
786 } |
|
787 } |
|
788 |
|
789 /* |
|
790 * Convenience function for displaying failed test cases. Useful |
|
791 * when running tests manually. |
|
792 * |
|
793 */ |
|
794 function getFailedCases() { |
|
795 for ( var i = 0; i < gTestcases.length; i++ ) { |
|
796 if ( ! gTestcases[i].passed ) { |
|
797 print( gTestcases[i].description + " = " +gTestcases[i].actual + |
|
798 " expected: " + gTestcases[i].expect ); |
|
799 } |
|
800 } |
|
801 } |
|
802 |
|
803 function jsTestDriverEnd() |
|
804 { |
|
805 // gDelayTestDriverEnd is used to |
|
806 // delay collection of the test result and |
|
807 // signal to Spider so that tests can continue |
|
808 // to run after page load has fired. They are |
|
809 // responsible for setting gDelayTestDriverEnd = true |
|
810 // then when completed, setting gDelayTestDriverEnd = false |
|
811 // then calling jsTestDriverEnd() |
|
812 |
|
813 if (gDelayTestDriverEnd) |
|
814 { |
|
815 return; |
|
816 } |
|
817 |
|
818 try |
|
819 { |
|
820 optionsReset(); |
|
821 } |
|
822 catch(ex) |
|
823 { |
|
824 dump('jsTestDriverEnd ' + ex); |
|
825 } |
|
826 |
|
827 for (var i = 0; i < gTestcases.length; i++) |
|
828 { |
|
829 gTestcases[i].dump(); |
|
830 } |
|
831 |
|
832 } |
|
833 |
|
834 function jit(on) |
|
835 { |
|
836 } |
|
837 |
|
838 function assertEqArray(a1, a2) { |
|
839 assertEq(a1.length, a2.length); |
|
840 for (var i = 0; i < a1.length; i++) { |
|
841 try { |
|
842 assertEq(a1[i], a2[i]); |
|
843 } catch (e) { |
|
844 throw new Error("At index " + i + ": " + e); |
|
845 } |
|
846 } |
|
847 } |
|
848 |
|
849 function assertThrows(f) { |
|
850 var ok = false; |
|
851 try { |
|
852 f(); |
|
853 } catch (exc) { |
|
854 ok = true; |
|
855 } |
|
856 if (!ok) |
|
857 throw new Error("Assertion failed: " + f + " did not throw as expected"); |
|
858 } |
|
859 |
|
860 /* |
|
861 * Some tests need to know if we are in Rhino as opposed to SpiderMonkey |
|
862 */ |
|
863 function inRhino() |
|
864 { |
|
865 return (typeof defineClass == "function"); |
|
866 } |
|
867 |
|
868 /* these functions are useful for running tests manually in Rhino */ |
|
869 |
|
870 function GetContext() { |
|
871 return Packages.com.netscape.javascript.Context.getCurrentContext(); |
|
872 } |
|
873 function OptLevel( i ) { |
|
874 i = Number(i); |
|
875 var cx = GetContext(); |
|
876 cx.setOptimizationLevel(i); |
|
877 } |
|
878 /* end of Rhino functions */ |