1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is Mozilla Communicator client code, released
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
25 * Rob Ginda rginda@netscape.com
26 * Bob Clary bob@bclary.com
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 // Explicitly set the default version.
43 // See https://bugzilla.mozilla.org/show_bug.cgi?id=522760#c11
44 if (typeof version != 'undefined')
49 var STATUS = "STATUS: ";
51 var SECT_PREFIX = 'Section ';
52 var SECT_SUFFIX = ' of test - ';
53 var callStack = new Array();
55 var gDelayTestDriverEnd = false;
57 var gTestcases = new Array();
58 var gTc = gTestcases.length;
73 var GLOBAL = this + '';
74 var PASSED = " PASSED! ";
75 var FAILED = " FAILED! ";
83 * wrapper for test case constructor that doesn't require the SECTION
87 function AddTestCase( description, expect, actual ) {
88 new TestCase( SECTION, description, expect, actual );
92 * Set up test environment.
95 function startTest() {
96 // print out bugnumber
99 print ("BUGNUMBER: " + BUGNUMBER );
103 function TestCase(n, d, e, a)
106 this.description = d;
109 this.passed = getTestCaseResult(e, a);
111 this.bugnumber = typeof(BUGNUMER) != 'undefined' ? BUGNUMBER : '';
112 this.type = (typeof window == 'undefined' ? 'shell' : 'browser');
113 gTestcases[gTc++] = this;
116 gFailureExpected = false;
118 TestCase.prototype.dump = function () {
119 // let reftest handle error reporting, otherwise
120 // output a summary line.
121 if (typeof document != "object" ||
122 !document.location.href.match(/jsreftest.html/))
124 dump('\njstest: ' + this.path + ' ' +
125 'bug: ' + this.bugnumber + ' ' +
126 'result: ' + (this.passed ? 'PASSED':'FAILED') + ' ' +
127 'type: ' + this.type + ' ' +
128 'description: ' + toPrinted(this.description) + ' ' +
129 // 'expected: ' + toPrinted(this.expect) + ' ' +
130 // 'actual: ' + toPrinted(this.actual) + ' ' +
131 'reason: ' + toPrinted(this.reason) + '\n');
135 TestCase.prototype.testPassed = (function TestCase_testPassed() { return this.passed; });
136 TestCase.prototype.testFailed = (function TestCase_testFailed() { return !this.passed; });
137 TestCase.prototype.testDescription = (function TestCase_testDescription() { return this.description + ' ' + this.reason; });
139 function getTestCases()
145 * The test driver searches for such a phrase in the test output.
146 * If such phrase exists, it will set n as the expected exit code.
148 function expectExitCode(n)
150 print('--- NOTE: IN THIS TESTCASE, WE EXPECT EXIT CODE ' + n + ' ---');
154 * Statuses current section of a test
156 function inSection(x)
158 return SECT_PREFIX + x + SECT_SUFFIX;
162 * Report a failure in the 'accepted' manner
164 function reportFailure (msg)
166 var lines = msg.split ("\n");
168 var funcName = currentFunc();
169 var prefix = (funcName) ? "[reported from " + funcName + "] ": "";
171 for (var i=0; i<lines.length; i++)
172 print (FAILED + prefix + lines[i]);
176 * Print a non-failure message.
178 function printStatus (msg)
182 msg = msg.toString();
184 msg = msg.toString();
185 var lines = msg.split ("\n");
188 for (var i=0; i<lines.length; i++)
189 print (STATUS + lines[i]);
193 * Print a bugnumber message.
195 function printBugNumber (num)
198 print ('BUGNUMBER: ' + num);
201 function toPrinted(value)
203 if (typeof value == "xml")
205 value = value.toXMLString();
209 value = String(value);
211 value = value.replace(/\\n/g, 'NL')
212 .replace(/\n/g, 'NL')
213 .replace(/\\r/g, 'CR')
214 .replace(/[^\x20-\x7E]+/g, escapeString);
218 function escapeString (str)
221 var len = str.length;
223 var digits = ["0", "1", "2", "3", "4", "5", "6", "7",
224 "8", "9", "A", "B", "C", "D", "E", "F"];
226 for (var i=0; i<len; i++)
228 var ch = str.charCodeAt(i);
230 a = digits[ch & 0xf];
232 b = digits[ch & 0xf];
237 c = digits[ch & 0xf];
239 d = digits[ch & 0xf];
241 result += "\\u" + d + c + b + a;
245 result += "\\x" + b + a;
253 * assertEq(actual, expected [, message])
254 * Throw if the two arguments are not the same. The sameness of two values
255 * is determined as follows. If both values are zero, they are the same iff
256 * their signs are the same. Otherwise, if both values are NaN, they are the
257 * same. Otherwise, they are the same if they compare equal using ===.
258 * see https://bugzilla.mozilla.org/show_bug.cgi?id=480199 and
259 * https://bugzilla.mozilla.org/show_bug.cgi?id=515285
261 if (typeof assertEq == 'undefined')
264 function (actual, expected, message)
266 function SameValue(v1, v2)
268 if (v1 === 0 && v2 === 0)
269 return 1 / v1 === 1 / v2;
270 if (v1 !== v1 && v2 !== v2)
274 if (!SameValue(actual, expected))
276 throw new TypeError('Assertion failed: got "' + actual + '", expected "' + expected +
277 (message ? ": " + message : ""));
283 * Compare expected result to actual result, if they differ (in value and/or
284 * type) report a failure. If description is provided, include it in the
287 function reportCompare (expected, actual, description) {
288 var expected_t = typeof expected;
289 var actual_t = typeof actual;
292 if (typeof description == "undefined")
298 printStatus ("Comparing '" + description + "'");
301 if (expected_t != actual_t)
303 output += "Type mismatch, expected type " + expected_t +
304 ", actual type " + actual_t + " ";
308 printStatus ("Expected type '" + expected_t + "' matched actual " +
309 "type '" + actual_t + "'");
312 if (expected != actual)
314 output += "Expected value '" + toPrinted(expected) +
315 "', Actual value '" + toPrinted(actual) + "' ";
319 printStatus ("Expected value '" + toPrinted(expected) +
320 "' matched actual value '" + toPrinted(actual) + "'");
323 var testcase = new TestCase("unknown-test-name", description, expected, actual);
324 testcase.reason = output;
326 // if running under reftest, let it handle result reporting.
327 if (typeof document != "object" ||
328 !document.location.href.match(/jsreftest.html/)) {
331 print(PASSED + description);
335 reportFailure (description + " : " + output);
338 return testcase.passed;
342 * Attempt to match a regular expression describing the result to
343 * the actual result, if they differ (in value and/or
344 * type) report a failure. If description is provided, include it in the
347 function reportMatch (expectedRegExp, actual, description) {
348 var expected_t = "string";
349 var actual_t = typeof actual;
352 if (typeof description == "undefined")
358 printStatus ("Comparing '" + description + "'");
361 if (expected_t != actual_t)
363 output += "Type mismatch, expected type " + expected_t +
364 ", actual type " + actual_t + " ";
368 printStatus ("Expected type '" + expected_t + "' matched actual " +
369 "type '" + actual_t + "'");
372 var matches = expectedRegExp.test(actual);
375 output += "Expected match to '" + toPrinted(expectedRegExp) +
376 "', Actual value '" + toPrinted(actual) + "' ";
380 printStatus ("Expected match to '" + toPrinted(expectedRegExp) +
381 "' matched actual value '" + toPrinted(actual) + "'");
384 var testcase = new TestCase("unknown-test-name", description, true, matches);
385 testcase.reason = output;
387 // if running under reftest, let it handle result reporting.
388 if (typeof document != "object" ||
389 !document.location.href.match(/jsreftest.html/)) {
392 print(PASSED + description);
396 reportFailure (description + " : " + output);
399 return testcase.passed;
403 * Puts funcName at the top of the call stack. This stack is used to show
404 * a function-reported-from field when reporting failures.
406 function enterFunc (funcName)
408 if (!funcName.match(/\(\)$/))
411 callStack.push(funcName);
415 * Pops the top funcName off the call stack. funcName is optional, and can be
416 * used to check push-pop balance.
418 function exitFunc (funcName)
420 var lastFunc = callStack.pop();
424 if (!funcName.match(/\(\)$/))
427 if (lastFunc != funcName)
428 reportCompare(funcName, lastFunc, "Test driver failure wrong exit function ");
433 * Peeks at the top of the call stack.
435 function currentFunc()
437 return callStack[callStack.length - 1];
441 Calculate the "order" of a set of data points {X: [], Y: []}
442 by computing successive "derivatives" of the data until
443 the data is exhausted or the derivative is linear.
448 var origLength = data.X.length;
450 while (data.X.length > 2)
452 var lr = new LinearRegression(data);
455 // only increase the order if the slope
460 if (lr.r > 0.98 || lr.Syx < 1 || lr.b < 1e-6)
462 // terminate if close to a line lr.r
463 // small error lr.Syx
467 data = dataDeriv(data);
470 if (2 == origLength - order)
472 order = Number.POSITIVE_INFINITY;
476 function LinearRegression(data)
480 for data points (Xi, Yi); 0 <= i < n
482 b = (n*SUM(XiYi) - SUM(Xi)*SUM(Yi))/(n*SUM(Xi*Xi) - SUM(Xi)*SUM(Xi))
483 a = (SUM(Yi) - b*SUM(Xi))/n
487 if (data.X.length != data.Y.length)
489 throw 'LinearRegression: data point length mismatch';
491 if (data.X.length < 3)
493 throw 'LinearRegression: data point length < 2';
495 var n = data.X.length;
508 for (i = 0; i < n; i++)
517 this.b = (n * SUM_XY - SUM_X * SUM_Y)/(n * SUM_XX - SUM_X * SUM_X);
518 this.a = (SUM_Y - this.b * SUM_X)/n;
525 var SUM_XdiffYdiff = 0;
527 for (i = 0; i < n; i++)
529 var Ydiff = Y[i] - this.Yavg;
530 var Xdiff = X[i] - this.Xavg;
532 SUM_Ydiff2 += Ydiff * Ydiff;
533 SUM_Xdiff2 += Xdiff * Xdiff;
534 SUM_XdiffYdiff += Xdiff * Ydiff;
537 var Syx2 = (SUM_Ydiff2 - Math.pow(SUM_XdiffYdiff/SUM_Xdiff2, 2))/(n - 2);
538 var r2 = Math.pow((n*SUM_XY - SUM_X * SUM_Y), 2) /
539 ((n*SUM_XX - SUM_X*SUM_X)*(n*SUM_YY-SUM_Y*SUM_Y));
541 this.Syx = Math.sqrt(Syx2);
542 this.r = Math.sqrt(r2);
546 function dataDeriv(data)
548 if (data.X.length != data.Y.length)
550 throw 'length mismatch';
552 var length = data.X.length;
556 throw 'length ' + length + ' must be >= 2';
561 var deriv = {X: [], Y: [] };
563 for (var i = 0; i < length - 1; i++)
565 deriv.X[i] = (X[i] + X[i+1])/2;
566 deriv.Y[i] = (Y[i+1] - Y[i])/(X[i+1] - X[i]);
574 function compareSource(expect, actual, summary)
577 var expectP = expect.
578 replace(/([(){},.:\[\]])/mg, ' $1 ').
579 replace(/(\w+)/mg, ' $1 ').
580 replace(/<(\/)? (\w+) (\/)?>/mg, '<$1$2$3>').
581 replace(/\s+/mg, ' ').
582 replace(/new (\w+)\s*\(\s*\)/mg, 'new $1');
584 var actualP = actual.
585 replace(/([(){},.:\[\]])/mg, ' $1 ').
586 replace(/(\w+)/mg, ' $1 ').
587 replace(/<(\/)? (\w+) (\/)?>/mg, '<$1$2$3>').
588 replace(/\s+/mg, ' ').
589 replace(/new (\w+)\s*\(\s*\)/mg, 'new $1');
591 print('expect:\n' + expectP);
592 print('actual:\n' + actualP);
594 reportCompare(expectP, actualP, summary);
596 // actual must be compilable if expect is?
599 var expectCompile = 'No Error';
606 actualCompile = 'No Error';
610 actualCompile = ex1 + '';
612 reportCompare(expectCompile, actualCompile,
613 summary + ': compile actual');
620 function optionsInit() {
622 // record initial values to support resetting
623 // options to their initial values
624 options.initvalues = {};
626 // record values in a stack to support pushing
627 // and popping options
628 options.stackvalues = [];
630 var optionNames = options().split(',');
632 for (var i = 0; i < optionNames.length; i++)
634 var optionName = optionNames[i];
637 options.initvalues[optionName] = '';
642 function optionsClear() {
644 // turn off current settings
646 var optionNames = options().split(',');
647 for (var i = 0; i < optionNames.length; i++)
649 var optionName = optionNames[i];
651 optionName != "methodjit" &&
652 optionName != "tracejit" &&
653 optionName != "jitprofiling" &&
654 optionName != "methodjit_always")
661 function optionsPush()
663 var optionsframe = {};
665 options.stackvalues.push(optionsframe);
667 var optionNames = options().split(',');
669 for (var i = 0; i < optionNames.length; i++)
671 var optionName = optionNames[i];
674 optionsframe[optionName] = '';
681 function optionsPop()
683 var optionsframe = options.stackvalues.pop();
687 for (optionName in optionsframe)
694 function optionsReset() {
700 // turn on initial settings
701 for (var optionName in options.initvalues)
703 if (!options.hasOwnProperty(optionName))
710 print('optionsReset: caught ' + ex);
715 if (typeof options == 'function')
721 function getTestCaseResult(expected, actual)
723 if (typeof expected != typeof actual)
725 if (typeof expected != 'number')
726 // Note that many tests depend on the use of '==' here, not '==='.
727 return actual == expected;
729 // Distinguish NaN from other values. Using x != x comparisons here
730 // works even if tests redefine isNaN.
731 if (actual != actual)
732 return expected != expected;
733 if (expected != expected)
736 // Tolerate a certain degree of error.
737 if (actual != expected)
738 return Math.abs(actual - expected) <= 1E-10;
740 // Here would be a good place to distinguish 0 and -0, if we wanted
741 // to. However, doing so would introduce a number of failures in
742 // areas where they don't seem important. For example, the WeekDay
743 // function in ECMA-262 returns -0 for Sundays before the epoch, but
744 // the Date functions in SpiderMonkey specified in terms of WeekDay
745 // often don't. This seems unimportant.
749 if (typeof dump == 'undefined')
751 if (typeof window == 'undefined' &&
752 typeof print == 'function')
758 dump = (function () {});
763 for ( gTc=0; gTc < gTestcases.length; gTc++ ) {
764 // temporary hack to work around some unknown issue in 1.7
767 gTestcases[gTc].passed = writeTestCaseResult(
768 gTestcases[gTc].expect,
769 gTestcases[gTc].actual,
770 gTestcases[gTc].description +" = "+ gTestcases[gTc].actual );
771 gTestcases[gTc].reason += ( gTestcases[gTc].passed ) ? "" : "wrong value ";
775 print('test(): empty testcase for gTc = ' + gTc + ' ' + e);
779 return ( gTestcases );
783 * Begin printing functions. These functions use the shell's
784 * print function. When running tests in the browser, these
785 * functions, override these functions with functions that use
789 function writeTestCaseResult( expect, actual, string ) {
790 var passed = getTestCaseResult( expect, actual );
791 // if running under reftest, let it handle result reporting.
792 if (typeof document != "object" ||
793 !document.location.href.match(/jsreftest.html/)) {
794 writeFormattedResult( expect, actual, string, passed );
798 function writeFormattedResult( expect, actual, string, passed ) {
799 var s = ( passed ? PASSED : FAILED ) + string + ' expected: ' + expect;
804 function writeHeaderToLog( string ) {
807 /* end of print functions */
811 * When running in the shell, run the garbage collector after the
812 * test has completed.
815 function stopTest() {
817 if ( gc != undefined ) {
823 * Convenience function for displaying failed test cases. Useful
824 * when running tests manually.
827 function getFailedCases() {
828 for ( var i = 0; i < gTestcases.length; i++ ) {
829 if ( ! gTestcases[i].passed ) {
830 print( gTestcases[i].description + " = " +gTestcases[i].actual +
831 " expected: " + gTestcases[i].expect );
837 waitForExplicitFinish: function () {
838 gDelayTestDriverEnd = true;
841 testFinished: function () {
842 gDelayTestDriverEnd = false;
848 function jsTestDriverEnd()
850 // gDelayTestDriverEnd is used to
851 // delay collection of the test result and
852 // signal to Spider so that tests can continue
853 // to run after page load has fired. They are
854 // responsible for setting gDelayTestDriverEnd = true
855 // then when completed, setting gDelayTestDriverEnd = false
856 // then calling jsTestDriverEnd()
858 if (gDelayTestDriverEnd)
869 dump('jsTestDriverEnd ' + ex);
872 for (var i = 0; i < gTestcases.length; i++)
874 gTestcases[i].dump();
881 if (on && !options().match(/tracejit/))
885 else if (!on && options().match(/tracejit/))
892 * Some tests need to know if we are in Rhino as opposed to SpiderMonkey
896 return (typeof defineClass == "function");
899 /* these functions are useful for running tests manually in Rhino */
901 function GetContext() {
902 return Packages.com.netscape.javascript.Context.getCurrentContext();
904 function OptLevel( i ) {
906 var cx = GetContext();
907 cx.setOptimizationLevel(i);
909 /* end of Rhino functions */