js/src/tests/shell.js

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:aed041f5f7e1
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 */

mercurial