On x86 compilers without fastcall, simulate it when invoking traces and un-simulate...
[wine-gecko.git] / js / tests / shell.js
blob440e48f4e6e2e89f0ef4502758f78189be54d34a
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 // Spidermonkey shell now defaults to 1.8, so set the basic version to
43 // 1.5 for backwards compatibility.
45 if (typeof version != 'undefined')
47   version(150);
50 var STATUS = "STATUS: ";
51 var VERBOSE = false;
52 var SECT_PREFIX = 'Section ';
53 var SECT_SUFFIX = ' of test - ';
54 var callStack = new Array();
56 var gTestfile;
57 var gTestPath;
58 var gTestsuite;
59 var gTestsubsuite;
60 var gDelayTestDriverEnd = false;
62 var gTestcases = new Array();
63 var gTc = gTestcases.length;
64 var BUGNUMBER = '';
65 var summary = '';
66 var description = '';
67 var expected = '';
68 var actual = '';
69 var msg = '';
71 var SECTION = "";
72 var VERSION = "";
73 var BUGNUMBER = "";
76  * constant strings
77  */
78 var GLOBAL = "[object global]";
79 var PASSED = " PASSED! ";
80 var FAILED = " FAILED! ";
82 var DEBUG = false;
84 var DESCRIPTION;
85 var EXPECTED;
88  * wrapper for test case constructor that doesn't require the SECTION
89  * argument.
90  */
92 function AddTestCase( description, expect, actual ) {
93   new TestCase( SECTION, description, expect, actual );
97  * Set up test environment.
98  *
99  */
100 function startTest() {
101   // print out bugnumber
103   if ( BUGNUMBER ) {
104     print ("BUGNUMBER: " + BUGNUMBER );
105   }
106   if ( typeof version != 'function') {
107     return;
108   }
110   // JavaScript 1.3 is supposed to be compliant ecma version 1.0
111   if ( VERSION == "ECMA_1" ) {
112     version ( "130" );
113   }
114   else if ( VERSION == "JS_1.8"  || gTestsuite == 'js1_8') {
115     version ( "180" );
116   }
117   else if ( VERSION == "JS_1.7"  || gTestsuite == 'js1_7') {
118     version ( "170" );
119   }
120   else if ( VERSION == "JS_1.6"  || gTestsuite == 'js1_6') {
121     version ( "160" );
122   }
123   else if ( VERSION == "JS_1.5"  || gTestsuite == 'js1_5') {
124     version ( "150" );
125   }
126   else if ( VERSION == "JS_1.4"  || gTestsuite == 'js1_4') {
127     version ( "140" );
128   }
129   else if ( VERSION == "JS_1.3"  || gTestsuite == 'js1_3') {
130     version ( "130" );
131   }
132   else if ( VERSION == "JS_1.2"  || gTestsuite == 'js1_2') {
133     version ( "120" );
134   }
135   else if ( VERSION  == "JS_1.1" || gTestsuite == 'js1_1') {
136     version ( "110" );
137   }
140 function TestCase(n, d, e, a)
142   this.path = (typeof gTestPath == 'undefined') ?
143     (gTestsuite + '/' + gTestsubsuite + '/' + gTestfile) :
144     gTestPath;
145   this.file = gTestfile;
146   this.name = n;
147   this.description = d;
148   this.expect = e;
149   this.actual = a;
150   this.passed = getTestCaseResult(e, a);
151   this.reason = '';
152   this.bugnumber = typeof(BUGNUMER) != 'undefined' ? BUGNUMBER : '';
153   this.type = (typeof window == 'undefined' ? 'shell' : 'browser');
154   gTestcases[gTc++] = this;
157 TestCase.prototype.dump = function () {
158   dump('\njstest: '      + this.path + ' ' +
159        'bug: '         + this.bugnumber + ' ' +
160        'result: '      + (this.passed ? 'PASSED':'FAILED') + ' ' +
161        'type: '        + this.type + ' ' +
162        'description: ' + toPrinted(this.description) + ' ' +
163 //       'expected: '    + toPrinted(this.expect) + ' ' +
164 //       'actual: '      + toPrinted(this.actual) + ' ' +
165        'reason: '      + toPrinted(this.reason) + '\n');
169  * The test driver searches for such a phrase in the test output.
170  * If such phrase exists, it will set n as the expected exit code.
171  */
172 function expectExitCode(n)
174   print('--- NOTE: IN THIS TESTCASE, WE EXPECT EXIT CODE ' + n + ' ---');
178  * Statuses current section of a test
179  */
180 function inSection(x)
182   return SECT_PREFIX + x + SECT_SUFFIX;
186  * Report a failure in the 'accepted' manner
187  */
188 function reportFailure (msg)
190   var lines = msg.split ("\n");
191   var l;
192   var funcName = currentFunc();
193   var prefix = (funcName) ? "[reported from " + funcName + "] ": "";
194    
195   for (var i=0; i<lines.length; i++)
196     print (FAILED + prefix + lines[i]);
200  * Print a non-failure message.
201  */
202 function printStatus (msg)
204 /* js1_6 had...
205    msg = String(msg);
206    msg = msg.toString();
208   msg = msg.toString();
209   var lines = msg.split ("\n");
210   var l;
212   for (var i=0; i<lines.length; i++)
213     print (STATUS + lines[i]);
217  * Print a bugnumber message.
218  */
219 function printBugNumber (num)
221   BUGNUMBER = num;
222   print ('BUGNUMBER: ' + num);
225 function toPrinted(value)
227   if (typeof value == "xml") 
228   {
229     value = value.toXMLString();
230   } 
231   else 
232   {
233     value = String(value);
234   }
235   value = value.replace(/\\n/g, 'NL')
236                .replace(/\n/g, 'NL')
237                .replace(/\\r/g, 'CR')
238                .replace(/[^\x20-\x7E]+/g, escapeString);
239   return value;
242 function escapeString (str)
244   var a, b, c, d;
245   var len = str.length;
246   var result = "";
247   var digits = ["0", "1", "2", "3", "4", "5", "6", "7",
248                 "8", "9", "A", "B", "C", "D", "E", "F"];
250   for (var i=0; i<len; i++)
251   {
252     var ch = str.charCodeAt(i);
254     a = digits[ch & 0xf];
255     ch >>= 4;
256     b = digits[ch & 0xf];
257     ch >>= 4;
259     if (ch)
260     {
261       c = digits[ch & 0xf];
262       ch >>= 4;
263       d = digits[ch & 0xf];
265       result += "\\u" + d + c + b + a;
266     }
267     else
268     {
269       result += "\\x" + b + a;
270     }
271   }
273   return result;
277  * Compare expected result to actual result, if they differ (in value and/or
278  * type) report a failure.  If description is provided, include it in the
279  * failure report.
280  */
281 function reportCompare (expected, actual, description) {
282   var expected_t = typeof expected;
283   var actual_t = typeof actual;
284   var output = "";
286   if (typeof description == "undefined")
287   {
288     description = '';
289   }
290   else if (VERBOSE)
291   {
292     printStatus ("Comparing '" + description + "'");
293   }
295   if (expected_t != actual_t)
296   {
297     output += "Type mismatch, expected type " + expected_t +
298       ", actual type " + actual_t + " ";
299   }
300   else if (VERBOSE)
301   {
302     printStatus ("Expected type '" + expected_t + "' matched actual " +
303                  "type '" + actual_t + "'");
304   }
306   if (expected != actual)
307   {
308     output += "Expected value '" + toPrinted(expected) +
309       "', Actual value '" + toPrinted(actual) + "' ";
310   }
311   else if (VERBOSE)
312   {
313     printStatus ("Expected value '" + toPrinted(expected) +
314                  "' matched actual value '" + toPrinted(actual) + "'");
315   }
317   var testcase = new TestCase(gTestfile, description, expected, actual);
318   testcase.reason = output;
320   if (testcase.passed)
321   {
322     print(PASSED + description);
323   }
324   else
325   {
326     reportFailure (description + " : " + output);
327   }
329   return testcase.passed;
333  * Attempt to match a regular expression describing the result to
334  * the actual result, if they differ (in value and/or
335  * type) report a failure.  If description is provided, include it in the
336  * failure report.
337  */
338 function reportMatch (expectedRegExp, actual, description) {
339   var expected_t = "string";
340   var actual_t = typeof actual;
341   var output = "";
343   if (typeof description == "undefined")
344   {
345     description = '';
346   }
347   else if (VERBOSE)
348   {
349     printStatus ("Comparing '" + description + "'");
350   }
352   if (expected_t != actual_t)
353   {
354     output += "Type mismatch, expected type " + expected_t +
355       ", actual type " + actual_t + " ";
356   }
357   else if (VERBOSE)
358   {
359     printStatus ("Expected type '" + expected_t + "' matched actual " +
360                  "type '" + actual_t + "'");
361   }
363   var matches = expectedRegExp.test(actual);
364   if (!matches)
365   {
366     output += "Expected match to '" + toPrinted(expectedRegExp) +
367       "', Actual value '" + toPrinted(actual) + "' ";
368   }
369   else if (VERBOSE)
370   {
371     printStatus ("Expected match to '" + toPrinted(expectedRegExp) +
372                  "' matched actual value '" + toPrinted(actual) + "'");
373   }
375   var testcase = new TestCase(gTestfile, description, true, matches);
376   testcase.reason = output;
378   if (testcase.passed)
379   {
380     print(PASSED + description);
381   }
382   else
383   {
384     reportFailure (description + " : " + output);
385   }
387   return testcase.passed;
391  * Puts funcName at the top of the call stack.  This stack is used to show
392  * a function-reported-from field when reporting failures.
393  */
394 function enterFunc (funcName)
396   if (!funcName.match(/\(\)$/))
397     funcName += "()";
399   callStack.push(funcName);
403  * Pops the top funcName off the call stack.  funcName is optional, and can be
404  * used to check push-pop balance.
405  */
406 function exitFunc (funcName)
408   var lastFunc = callStack.pop();
409    
410   if (funcName)
411   {
412     if (!funcName.match(/\(\)$/))
413       funcName += "()";
415     if (lastFunc != funcName)
416       reportCompare(funcName, lastFunc, "Test driver failure wrong exit function ");
417   }
421  * Peeks at the top of the call stack.
422  */
423 function currentFunc()
425   return callStack[callStack.length - 1];
429   Calculate the "order" of a set of data points {X: [], Y: []}
430   by computing successive "derivatives" of the data until
431   the data is exhausted or the derivative is linear.
433 function BigO(data)
435   var order = 0;
436   var origLength = data.X.length;
438   while (data.X.length > 2)
439   {
440     var lr = new LinearRegression(data);
441     if (lr.b > 1e-6)
442     {
443       // only increase the order if the slope
444       // is "great" enough
445       order++;
446     }
448     if (lr.r > 0.98 || lr.Syx < 1 || lr.b < 1e-6)
449     {
450       // terminate if close to a line lr.r
451       // small error lr.Syx
452       // small slope lr.b
453       break;
454     }
455     data = dataDeriv(data);
456   }
458   if (2 == origLength - order)
459   {
460     order = Number.POSITIVE_INFINITY;
461   }
462   return order;
464   function LinearRegression(data)
465   {
466     /*
467       y = a + bx
468       for data points (Xi, Yi); 0 <= i < n
470       b = (n*SUM(XiYi) - SUM(Xi)*SUM(Yi))/(n*SUM(Xi*Xi) - SUM(Xi)*SUM(Xi))
471       a = (SUM(Yi) - b*SUM(Xi))/n
472     */
473     var i;
475     if (data.X.length != data.Y.length)
476     {
477       throw 'LinearRegression: data point length mismatch';
478     }
479     if (data.X.length < 3)
480     {
481       throw 'LinearRegression: data point length < 2';
482     }
483     var n = data.X.length;
484     var X = data.X;
485     var Y = data.Y;
487     this.Xavg = 0;
488     this.Yavg = 0;
490     var SUM_X  = 0;
491     var SUM_XY = 0;
492     var SUM_XX = 0;
493     var SUM_Y  = 0;
494     var SUM_YY = 0;
496     for (i = 0; i < n; i++)
497     {
498       SUM_X  += X[i];
499       SUM_XY += X[i]*Y[i];
500       SUM_XX += X[i]*X[i];
501       SUM_Y  += Y[i];
502       SUM_YY += Y[i]*Y[i];
503     }
505     this.b = (n * SUM_XY - SUM_X * SUM_Y)/(n * SUM_XX - SUM_X * SUM_X);
506     this.a = (SUM_Y - this.b * SUM_X)/n;
508     this.Xavg = SUM_X/n;
509     this.Yavg = SUM_Y/n;
511     var SUM_Ydiff2 = 0;
512     var SUM_Xdiff2 = 0;
513     var SUM_XdiffYdiff = 0;
515     for (i = 0; i < n; i++)
516     {
517       var Ydiff = Y[i] - this.Yavg;
518       var Xdiff = X[i] - this.Xavg;
519        
520       SUM_Ydiff2 += Ydiff * Ydiff;
521       SUM_Xdiff2 += Xdiff * Xdiff;
522       SUM_XdiffYdiff += Xdiff * Ydiff;
523     }
525     var Syx2 = (SUM_Ydiff2 - Math.pow(SUM_XdiffYdiff/SUM_Xdiff2, 2))/(n - 2);
526     var r2   = Math.pow((n*SUM_XY - SUM_X * SUM_Y), 2) /
527       ((n*SUM_XX - SUM_X*SUM_X)*(n*SUM_YY-SUM_Y*SUM_Y));
529     this.Syx = Math.sqrt(Syx2);
530     this.r = Math.sqrt(r2);
532   }
534   function dataDeriv(data)
535   {
536     if (data.X.length != data.Y.length)
537     {
538       throw 'length mismatch';
539     }
540     var length = data.X.length;
542     if (length < 2)
543     {
544       throw 'length ' + length + ' must be >= 2';
545     }
546     var X = data.X;
547     var Y = data.Y;
549     var deriv = {X: [], Y: [] };
551     for (var i = 0; i < length - 1; i++)
552     {
553       deriv.X[i] = (X[i] + X[i+1])/2;
554       deriv.Y[i] = (Y[i+1] - Y[i])/(X[i+1] - X[i]);
555     } 
556     return deriv;
557   }
559   return 0;
562 function compareSource(expect, actual, summary)
564   // compare source
565   var expectP = expect.
566     replace(/([(){},.:\[\]])/mg, ' $1 ').
567     replace(/(\w+)/mg, ' $1 ').
568     replace(/<(\/)? (\w+) (\/)?>/mg, '<$1$2$3>').
569     replace(/\s+/mg, ' ').
570     replace(/new (\w+)\s*\(\s*\)/mg, 'new $1');
572   var actualP = actual.
573     replace(/([(){},.:\[\]])/mg, ' $1 ').
574     replace(/(\w+)/mg, ' $1 ').
575     replace(/<(\/)? (\w+) (\/)?>/mg, '<$1$2$3>').
576     replace(/\s+/mg, ' ').
577     replace(/new (\w+)\s*\(\s*\)/mg, 'new $1');
579   print('expect:\n' + expectP);
580   print('actual:\n' + actualP);
582   reportCompare(expectP, actualP, summary);
584   // actual must be compilable if expect is?
585   try
586   {
587     var expectCompile = 'No Error';
588     var actualCompile;
590     eval(expect);
591     try
592     {
593       eval(actual);
594       actualCompile = 'No Error';
595     }
596     catch(ex1)
597     {
598       actualCompile = ex1 + '';
599     }
600     reportCompare(expectCompile, actualCompile,
601                   summary + ': compile actual');
602   }
603   catch(ex)
604   {
605   }
608 function optionsInit() {
610   // record initial values to support resetting
611   // options to their initial values
612   options.initvalues  = {};
614   // record values in a stack to support pushing
615   // and popping options
616   options.stackvalues = [];
618   var optionNames = options().split(',');
620   for (var i = 0; i < optionNames.length; i++)
621   {
622     var optionName = optionNames[i];
623     if (optionName)
624     {
625       options.initvalues[optionName] = '';
626     }
627   }
630 function optionsClear() {
631        
632   // turn off current settings
633   var optionNames = options().split(',');
634   for (var i = 0; i < optionNames.length; i++)
635   {
636     var optionName = optionNames[i];
637     if (optionName)
638     {
639       options(optionName);
640     }
641   }
644 function optionsPush()
646   var optionsframe = {};
648   options.stackvalues.push(optionsframe);
650   var optionNames = options().split(',');
652   for (var i = 0; i < optionNames.length; i++)
653   {
654     var optionName = optionNames[i];
655     if (optionName)
656     {
657       optionsframe[optionName] = '';
658     }
659   }
661   optionsClear();
664 function optionsPop()
666   var optionsframe = options.stackvalues.pop();
668   optionsClear();
670   for (optionName in optionsframe)
671   {
672     options(optionName);
673   }
677 function optionsReset() {
679   optionsClear();
681   // turn on initial settings
682   for (optionName in options.initvalues)
683   {
684     options(optionName);
685   }
688 if (typeof options == 'function')
690   optionsInit();
691   optionsClear();
694 function getTestCaseResult(expected, actual)
696   var expected_t = typeof expected;
697   var actual_t = typeof actual;
698   var passed = true;
700   // because ( NaN == NaN ) always returns false, need to do
701   // a special compare to see if we got the right result.
702   if ( actual != actual ) 
703   {
704     if ( actual_t == "object" ) 
705     {
706       actual = "NaN object";
707     } 
708     else 
709     {
710       actual = "NaN number";
711     }
712   }
713   if ( expected != expected ) 
714   {
715     if ( expected_t == "object" ) 
716     {
717       expected = "NaN object";
718     } 
719     else 
720     {
721       expected = "NaN number";
722     }
723   }
725   if (expected_t != actual_t)
726   {
727     passed = false;
728   }
729   else if (expected != actual)
730   {
731     if (expected_t != 'number' || (Math.abs(actual - expected) > 1E-10))
732     {
733       passed = false;
734     }
735   }
737   return passed;
740 if (typeof dump == 'undefined')
742   if (typeof window == 'undefined' &&
743       typeof print == 'function')
744   {
745     dump = print;
746   }
747   else
748   {
749     dump = (function () {});
750   }
753 function test() {
754   for ( gTc=0; gTc < gTestcases.length; gTc++ ) {
755     // temporary hack to work around some unknown issue in 1.7
756     try
757     {
758       gTestcases[gTc].passed = writeTestCaseResult(
759         gTestcases[gTc].expect,
760         gTestcases[gTc].actual,
761         gTestcases[gTc].description +" = "+ gTestcases[gTc].actual );
762       gTestcases[gTc].reason += ( gTestcases[gTc].passed ) ? "" : "wrong value ";
763     }
764     catch(e)
765     {
766       print('test(): empty testcase for gTc = ' + gTc + ' ' + e);
767     }
768   }
769   stopTest();
770   return ( gTestcases );
774  * Begin printing functions.  These functions use the shell's
775  * print function.  When running tests in the browser, these
776  * functions, override these functions with functions that use
777  * document.write.
778  */
780 function writeTestCaseResult( expect, actual, string ) {
781   var passed = getTestCaseResult( expect, actual );
782   writeFormattedResult( expect, actual, string, passed );
783   return passed;
785 function writeFormattedResult( expect, actual, string, passed ) {
786   var s = ( passed ? PASSED : FAILED ) + string + ' expected: ' + expect;
787   print(s);
788   return passed;
791 function writeHeaderToLog( string ) {
792   print( string );
794 /* end of print functions */
798  * When running in the shell, run the garbage collector after the
799  * test has completed.
800  */
802 function stopTest() {
803   var gc;
804   if ( gc != undefined ) {
805     gc();
806   }
810  * Convenience function for displaying failed test cases.  Useful
811  * when running tests manually.
813  */
814 function getFailedCases() {
815   for ( var i = 0; i < gTestcases.length; i++ ) {
816     if ( ! gTestcases[i].passed ) {
817       print( gTestcases[i].description + " = " +gTestcases[i].actual +
818              " expected: " + gTestcases[i].expect );
819     }
820   }
823 function jsTestDriverEnd()
825   // gDelayTestDriverEnd is used to
826   // delay collection of the test result and
827   // signal to Spider so that tests can continue
828   // to run after page load has fired. They are
829   // responsible for setting gDelayTestDriverEnd = true
830   // then when completed, setting gDelayTestDriverEnd = false
831   // then calling jsTestDriverEnd()
833   if (gDelayTestDriverEnd)
834   {
835     return;
836   }
838   try
839   {
840     optionsReset();
841   }
842   catch(ex)
843   {
844     dump('jsTestDriverEnd ' + ex);
845   }
847   for (var i = 0; i < gTestcases.length; i++)
848   {
849     gTestcases[i].dump();
850   }
855  * Some tests need to know if we are in Rhino as opposed to SpiderMonkey
856  */
857 function inRhino()
859   return (typeof defineClass == "function");
862 /* these functions are useful for running tests manually in Rhino */
864 function GetContext() {
865   return Packages.com.netscape.javascript.Context.getCurrentContext();
867 function OptLevel( i ) {
868   i = Number(i);
869   var cx = GetContext();
870   cx.setOptimizationLevel(i);
872 /* end of Rhino functions */