CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / js / src / tests / shell.js
blobf65435fdfd776fc494fcb74c073f1b3f3d1b17fe
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
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/
10  *
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
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
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.
23  *
24  * Contributor(s):
25  *   Rob Ginda rginda@netscape.com
26  *   Bob Clary bob@bclary.com
27  *
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.
39  *
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')
46   version(0);
49 var STATUS = "STATUS: ";
50 var VERBOSE = false;
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;
59 var BUGNUMBER = '';
60 var summary = '';
61 var description = '';
62 var expected = '';
63 var actual = '';
64 var msg = '';
66 var SECTION = "";
67 var VERSION = "";
68 var BUGNUMBER = "";
71  * constant strings
72  */
73 var GLOBAL = this + '';
74 var PASSED = " PASSED! ";
75 var FAILED = " FAILED! ";
77 var DEBUG = false;
79 var DESCRIPTION;
80 var EXPECTED;
83  * wrapper for test case constructor that doesn't require the SECTION
84  * argument.
85  */
87 function AddTestCase( description, expect, actual ) {
88   new TestCase( SECTION, description, expect, actual );
92  * Set up test environment.
93  *
94  */
95 function startTest() {
96   // print out bugnumber
98   if ( BUGNUMBER ) {
99     print ("BUGNUMBER: " + BUGNUMBER );
100   }
103 function TestCase(n, d, e, a)
105   this.name = n;
106   this.description = d;
107   this.expect = e;
108   this.actual = a;
109   this.passed = getTestCaseResult(e, a);
110   this.reason = '';
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/))
123   {
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');
132   }
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()
141   return gTestcases;
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.
147  */
148 function expectExitCode(n)
150   print('--- NOTE: IN THIS TESTCASE, WE EXPECT EXIT CODE ' + n + ' ---');
154  * Statuses current section of a test
155  */
156 function inSection(x)
158   return SECT_PREFIX + x + SECT_SUFFIX;
162  * Report a failure in the 'accepted' manner
163  */
164 function reportFailure (msg)
166   var lines = msg.split ("\n");
167   var l;
168   var funcName = currentFunc();
169   var prefix = (funcName) ? "[reported from " + funcName + "] ": "";
170    
171   for (var i=0; i<lines.length; i++)
172     print (FAILED + prefix + lines[i]);
176  * Print a non-failure message.
177  */
178 function printStatus (msg)
180 /* js1_6 had...
181    msg = String(msg);
182    msg = msg.toString();
184   msg = msg.toString();
185   var lines = msg.split ("\n");
186   var l;
188   for (var i=0; i<lines.length; i++)
189     print (STATUS + lines[i]);
193  * Print a bugnumber message.
194  */
195 function printBugNumber (num)
197   BUGNUMBER = num;
198   print ('BUGNUMBER: ' + num);
201 function toPrinted(value)
203   if (typeof value == "xml") 
204   {
205     value = value.toXMLString();
206   } 
207   else 
208   {
209     value = String(value);
210   }
211   value = value.replace(/\\n/g, 'NL')
212                .replace(/\n/g, 'NL')
213                .replace(/\\r/g, 'CR')
214                .replace(/[^\x20-\x7E]+/g, escapeString);
215   return value;
218 function escapeString (str)
220   var a, b, c, d;
221   var len = str.length;
222   var result = "";
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++)
227   {
228     var ch = str.charCodeAt(i);
230     a = digits[ch & 0xf];
231     ch >>= 4;
232     b = digits[ch & 0xf];
233     ch >>= 4;
235     if (ch)
236     {
237       c = digits[ch & 0xf];
238       ch >>= 4;
239       d = digits[ch & 0xf];
241       result += "\\u" + d + c + b + a;
242     }
243     else
244     {
245       result += "\\x" + b + a;
246     }
247   }
249   return result;
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
260  */
261 if (typeof assertEq == 'undefined')
263   var assertEq =
264     function (actual, expected, message)
265     {
266       function SameValue(v1, v2)
267       {
268         if (v1 === 0 && v2 === 0)
269           return 1 / v1 === 1 / v2;
270         if (v1 !== v1 && v2 !== v2)
271           return true;
272         return v1 === v2;
273       }
274       if (!SameValue(actual, expected))
275       {
276         throw new TypeError('Assertion failed: got "' + actual + '", expected "' + expected +
277                             (message ? ": " + message : ""));
278       }
279     };
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
285  * failure report.
286  */
287 function reportCompare (expected, actual, description) {
288   var expected_t = typeof expected;
289   var actual_t = typeof actual;
290   var output = "";
292   if (typeof description == "undefined")
293   {
294     description = '';
295   }
296   else if (VERBOSE)
297   {
298     printStatus ("Comparing '" + description + "'");
299   }
301   if (expected_t != actual_t)
302   {
303     output += "Type mismatch, expected type " + expected_t +
304       ", actual type " + actual_t + " ";
305   }
306   else if (VERBOSE)
307   {
308     printStatus ("Expected type '" + expected_t + "' matched actual " +
309                  "type '" + actual_t + "'");
310   }
312   if (expected != actual)
313   {
314     output += "Expected value '" + toPrinted(expected) +
315       "', Actual value '" + toPrinted(actual) + "' ";
316   }
317   else if (VERBOSE)
318   {
319     printStatus ("Expected value '" + toPrinted(expected) +
320                  "' matched actual value '" + toPrinted(actual) + "'");
321   }
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/)) {
329     if (testcase.passed)
330     {
331       print(PASSED + description);
332     }
333     else
334     {
335       reportFailure (description + " : " + output);
336     }
337   }
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
345  * failure report.
346  */
347 function reportMatch (expectedRegExp, actual, description) {
348   var expected_t = "string";
349   var actual_t = typeof actual;
350   var output = "";
352   if (typeof description == "undefined")
353   {
354     description = '';
355   }
356   else if (VERBOSE)
357   {
358     printStatus ("Comparing '" + description + "'");
359   }
361   if (expected_t != actual_t)
362   {
363     output += "Type mismatch, expected type " + expected_t +
364       ", actual type " + actual_t + " ";
365   }
366   else if (VERBOSE)
367   {
368     printStatus ("Expected type '" + expected_t + "' matched actual " +
369                  "type '" + actual_t + "'");
370   }
372   var matches = expectedRegExp.test(actual);
373   if (!matches)
374   {
375     output += "Expected match to '" + toPrinted(expectedRegExp) +
376       "', Actual value '" + toPrinted(actual) + "' ";
377   }
378   else if (VERBOSE)
379   {
380     printStatus ("Expected match to '" + toPrinted(expectedRegExp) +
381                  "' matched actual value '" + toPrinted(actual) + "'");
382   }
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/)) {
390     if (testcase.passed)
391     {
392       print(PASSED + description);
393     }
394     else
395     {
396       reportFailure (description + " : " + output);
397     }
398   }
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.
405  */
406 function enterFunc (funcName)
408   if (!funcName.match(/\(\)$/))
409     funcName += "()";
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.
417  */
418 function exitFunc (funcName)
420   var lastFunc = callStack.pop();
421    
422   if (funcName)
423   {
424     if (!funcName.match(/\(\)$/))
425       funcName += "()";
427     if (lastFunc != funcName)
428       reportCompare(funcName, lastFunc, "Test driver failure wrong exit function ");
429   }
433  * Peeks at the top of the call stack.
434  */
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.
445 function BigO(data)
447   var order = 0;
448   var origLength = data.X.length;
450   while (data.X.length > 2)
451   {
452     var lr = new LinearRegression(data);
453     if (lr.b > 1e-6)
454     {
455       // only increase the order if the slope
456       // is "great" enough
457       order++;
458     }
460     if (lr.r > 0.98 || lr.Syx < 1 || lr.b < 1e-6)
461     {
462       // terminate if close to a line lr.r
463       // small error lr.Syx
464       // small slope lr.b
465       break;
466     }
467     data = dataDeriv(data);
468   }
470   if (2 == origLength - order)
471   {
472     order = Number.POSITIVE_INFINITY;
473   }
474   return order;
476   function LinearRegression(data)
477   {
478     /*
479       y = a + bx
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
484     */
485     var i;
487     if (data.X.length != data.Y.length)
488     {
489       throw 'LinearRegression: data point length mismatch';
490     }
491     if (data.X.length < 3)
492     {
493       throw 'LinearRegression: data point length < 2';
494     }
495     var n = data.X.length;
496     var X = data.X;
497     var Y = data.Y;
499     this.Xavg = 0;
500     this.Yavg = 0;
502     var SUM_X  = 0;
503     var SUM_XY = 0;
504     var SUM_XX = 0;
505     var SUM_Y  = 0;
506     var SUM_YY = 0;
508     for (i = 0; i < n; i++)
509     {
510       SUM_X  += X[i];
511       SUM_XY += X[i]*Y[i];
512       SUM_XX += X[i]*X[i];
513       SUM_Y  += Y[i];
514       SUM_YY += Y[i]*Y[i];
515     }
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;
520     this.Xavg = SUM_X/n;
521     this.Yavg = SUM_Y/n;
523     var SUM_Ydiff2 = 0;
524     var SUM_Xdiff2 = 0;
525     var SUM_XdiffYdiff = 0;
527     for (i = 0; i < n; i++)
528     {
529       var Ydiff = Y[i] - this.Yavg;
530       var Xdiff = X[i] - this.Xavg;
531        
532       SUM_Ydiff2 += Ydiff * Ydiff;
533       SUM_Xdiff2 += Xdiff * Xdiff;
534       SUM_XdiffYdiff += Xdiff * Ydiff;
535     }
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);
544   }
546   function dataDeriv(data)
547   {
548     if (data.X.length != data.Y.length)
549     {
550       throw 'length mismatch';
551     }
552     var length = data.X.length;
554     if (length < 2)
555     {
556       throw 'length ' + length + ' must be >= 2';
557     }
558     var X = data.X;
559     var Y = data.Y;
561     var deriv = {X: [], Y: [] };
563     for (var i = 0; i < length - 1; i++)
564     {
565       deriv.X[i] = (X[i] + X[i+1])/2;
566       deriv.Y[i] = (Y[i+1] - Y[i])/(X[i+1] - X[i]);
567     } 
568     return deriv;
569   }
571   return 0;
574 function compareSource(expect, actual, summary)
576   // compare source
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?
597   try
598   {
599     var expectCompile = 'No Error';
600     var actualCompile;
602     eval(expect);
603     try
604     {
605       eval(actual);
606       actualCompile = 'No Error';
607     }
608     catch(ex1)
609     {
610       actualCompile = ex1 + '';
611     }
612     reportCompare(expectCompile, actualCompile,
613                   summary + ': compile actual');
614   }
615   catch(ex)
616   {
617   }
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++)
633   {
634     var optionName = optionNames[i];
635     if (optionName)
636     {
637       options.initvalues[optionName] = '';
638     }
639   }
642 function optionsClear() {
643        
644   // turn off current settings
645   // except jit.
646   var optionNames = options().split(',');
647   for (var i = 0; i < optionNames.length; i++)
648   {
649     var optionName = optionNames[i];
650     if (optionName &&
651         optionName != "methodjit" &&
652         optionName != "tracejit" &&
653         optionName != "jitprofiling" &&
654         optionName != "methodjit_always")
655     {
656       options(optionName);
657     }
658   }
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++)
670   {
671     var optionName = optionNames[i];
672     if (optionName)
673     {
674       optionsframe[optionName] = '';
675     }
676   }
678   optionsClear();
681 function optionsPop()
683   var optionsframe = options.stackvalues.pop();
685   optionsClear();
687   for (optionName in optionsframe)
688   {
689     options(optionName);
690   }
694 function optionsReset() {
696   try
697   {
698     optionsClear();
700     // turn on initial settings
701     for (var optionName in options.initvalues)
702     {
703       if (!options.hasOwnProperty(optionName))
704         continue;
705       options(optionName);
706     }
707   }
708   catch(ex)
709   {
710     print('optionsReset: caught ' + ex);
711   }
715 if (typeof options == 'function')
717   optionsInit();
718   optionsClear();
721 function getTestCaseResult(expected, actual)
723   if (typeof expected != typeof actual)
724     return false;
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)
734     return false;
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.
746   return true;
749 if (typeof dump == 'undefined')
751   if (typeof window == 'undefined' &&
752       typeof print == 'function')
753   {
754     dump = print;
755   }
756   else
757   {
758     dump = (function () {});
759   }
762 function test() {
763   for ( gTc=0; gTc < gTestcases.length; gTc++ ) {
764     // temporary hack to work around some unknown issue in 1.7
765     try
766     {
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 ";
772     }
773     catch(e)
774     {
775       print('test(): empty testcase for gTc = ' + gTc + ' ' + e);
776     }
777   }
778   stopTest();
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
786  * document.write.
787  */
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 );
795   }
796   return passed;
798 function writeFormattedResult( expect, actual, string, passed ) {
799   var s = ( passed ? PASSED : FAILED ) + string + ' expected: ' + expect;
800   print(s);
801   return passed;
804 function writeHeaderToLog( string ) {
805   print( string );
807 /* end of print functions */
811  * When running in the shell, run the garbage collector after the
812  * test has completed.
813  */
815 function stopTest() {
816   var gc;
817   if ( gc != undefined ) {
818     gc();
819   }
823  * Convenience function for displaying failed test cases.  Useful
824  * when running tests manually.
826  */
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 );
832     }
833   }
836 var JSTest = {
837   waitForExplicitFinish: function () {
838     gDelayTestDriverEnd = true;
839   },
841   testFinished: function () {
842     gDelayTestDriverEnd = false;
843     jsTestDriverEnd();
844     quit();
845   }
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)
859   {
860     return;
861   }
863   try
864   {
865     optionsReset();
866   }
867   catch(ex)
868   {
869     dump('jsTestDriverEnd ' + ex);
870   }
872   for (var i = 0; i < gTestcases.length; i++)
873   {
874     gTestcases[i].dump();
875   }
879 function jit(on)
881   if (on && !options().match(/tracejit/))
882   {
883     options('tracejit');
884   }
885   else if (!on && options().match(/tracejit/))
886   {
887     options('tracejit');
888   }
892  * Some tests need to know if we are in Rhino as opposed to SpiderMonkey
893  */
894 function inRhino()
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 ) {
905   i = Number(i);
906   var cx = GetContext();
907   cx.setOptimizationLevel(i);
909 /* end of Rhino functions */