1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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 ***** */
41
42var FAILED = "FAILED!: ";
43var STATUS = "STATUS: ";
44var BUGNUMBER = "BUGNUMBER: ";
45var VERBOSE = false;
46var SECT_PREFIX = 'Section ';
47var SECT_SUFFIX = ' of test -';
48var callStack = new Array();
49
50function writeLineToLog( string ) {
51    print( string + "\n");
52}
53/*
54 * The test driver searches for such a phrase in the test output.
55 * If such phrase exists, it will set n as the expected exit code.
56 */
57function expectExitCode(n)
58{
59
60    writeLineToLog('--- NOTE: IN THIS TESTCASE, WE EXPECT EXIT CODE ' + n + ' ---');
61
62}
63
64/*
65 * Statuses current section of a test
66 */
67function inSection(x)
68{
69
70    return SECT_PREFIX + x + SECT_SUFFIX;
71
72}
73
74/*
75 * Some tests need to know if we are in Rhino as opposed to SpiderMonkey
76 */
77function inRhino()
78{
79    return (typeof defineClass == "function");
80}
81
82/*
83 * Report a failure in the 'accepted' manner
84 */
85function reportFailure (msg)
86{
87    var lines = msg.split ("\n");
88    var l;
89    var funcName = currentFunc();
90    var prefix = (funcName) ? "[reported from " + funcName + "] ": "";
91
92    for (var i=0; i<lines.length; i++)
93        writeLineToLog (FAILED + prefix + lines[i]);
94
95}
96
97/*
98 * Print a non-failure message.
99 */
100function printStatus (msg)
101{
102    msg = String(msg);
103    msg = msg.toString();
104    var lines = msg.split ("\n");
105    var l;
106
107    for (var i=0; i<lines.length; i++)
108        writeLineToLog (STATUS + lines[i]);
109
110}
111
112/*
113 * Print a bugnumber message.
114 */
115function printBugNumber (num)
116{
117
118    writeLineToLog (BUGNUMBER + num);
119
120}
121
122/*
123 * Compare expected result to actual result, if they differ (in value and/or
124 * type) report a failure.  If description is provided, include it in the
125 * failure report.
126 */
127function reportCompare (expected, actual, description)
128{
129    var expected_t = typeof expected;
130    var actual_t = typeof actual;
131    var output = "";
132
133    if ((VERBOSE) && (typeof description != "undefined"))
134        printStatus ("Comparing '" + description + "'");
135
136    if (expected_t != actual_t)
137        output += "Type mismatch, expected type " + expected_t +
138            ", actual type " + actual_t + "\n";
139    else if (VERBOSE)
140        printStatus ("Expected type '" + actual_t + "' matched actual " +
141                     "type '" + expected_t + "'");
142
143    if (expected != actual)
144        output += "Expected value '" + expected + "', Actual value '" + actual +
145            "'\n";
146    else if (VERBOSE)
147        printStatus ("Expected value '" + actual + "' matched actual " +
148                     "value '" + expected + "'");
149
150    if (output != "")
151    {
152        if (typeof description != "undefined")
153            reportFailure (description);
154        reportFailure (output);
155    }
156    return (output == ""); // true if passed
157}
158
159/*
160 * Puts funcName at the top of the call stack.  This stack is used to show
161 * a function-reported-from field when reporting failures.
162 */
163function enterFunc (funcName)
164{
165
166    if (!funcName.match(/\(\)$/))
167        funcName += "()";
168
169    callStack.push(funcName);
170
171}
172
173/*
174 * Pops the top funcName off the call stack.  funcName is optional, and can be
175 * used to check push-pop balance.
176 */
177function exitFunc (funcName)
178{
179    var lastFunc = callStack.pop();
180
181    if (funcName)
182    {
183        if (!funcName.match(/\(\)$/))
184            funcName += "()";
185
186        if (lastFunc != funcName)
187            reportFailure ("Test driver failure, expected to exit function '" +
188                           funcName + "' but '" + lastFunc + "' came off " +
189                           "the stack");
190    }
191
192}
193
194/*
195 * Peeks at the top of the call stack.
196 */
197function currentFunc()
198{
199
200    return callStack[callStack.length - 1];
201
202}
203
204/*
205  Calculate the "order" of a set of data points {X: [], Y: []}
206  by computing successive "derivatives" of the data until
207  the data is exhausted or the derivative is linear.
208*/
209function BigO(data)
210{
211  var order = 0;
212  var origLength = data.X.length;
213
214  while (data.X.length > 2)
215  {
216    var lr = new LinearRegression(data);
217    if (lr.b > 1e-6)
218    {
219      // only increase the order if the slope
220      // is "great" enough
221      order++;
222    }
223
224    if (lr.r > 0.98 || lr.Syx < 1 || lr.b < 1e-6)
225    {
226      // terminate if close to a line lr.r
227      // small error lr.Syx
228      // small slope lr.b
229      break;
230    }
231    data = dataDeriv(data);
232  }
233
234  if (2 == origLength - order)
235  {
236    order = Number.POSITIVE_INFINITY;
237  }
238  return order;
239
240  function LinearRegression(data)
241    {
242      /*
243        y = a + bx
244        for data points (Xi, Yi); 0 <= i < n
245
246        b = (n*SUM(XiYi) - SUM(Xi)*SUM(Yi))/(n*SUM(Xi*Xi) - SUM(Xi)*SUM(Xi))
247        a = (SUM(Yi) - b*SUM(Xi))/n
248      */
249      var i;
250
251      if (data.X.length != data.Y.length)
252      {
253        throw 'LinearRegression: data point length mismatch';
254      }
255      if (data.X.length < 3)
256      {
257        throw 'LinearRegression: data point length < 2';
258      }
259      var n = data.X.length;
260      var X = data.X;
261      var Y = data.Y;
262
263      this.Xavg = 0;
264      this.Yavg = 0;
265
266      var SUM_X  = 0;
267      var SUM_XY = 0;
268      var SUM_XX = 0;
269      var SUM_Y  = 0;
270      var SUM_YY = 0;
271
272      for (i = 0; i < n; i++)
273      {
274          SUM_X  += X[i];
275          SUM_XY += X[i]*Y[i];
276          SUM_XX += X[i]*X[i];
277          SUM_Y  += Y[i];
278          SUM_YY += Y[i]*Y[i];
279      }
280
281      this.b = (n * SUM_XY - SUM_X * SUM_Y)/(n * SUM_XX - SUM_X * SUM_X);
282      this.a = (SUM_Y - this.b * SUM_X)/n;
283
284      this.Xavg = SUM_X/n;
285      this.Yavg = SUM_Y/n;
286
287      var SUM_Ydiff2 = 0;
288      var SUM_Xdiff2 = 0;
289      var SUM_XdiffYdiff = 0;
290
291      for (i = 0; i < n; i++)
292      {
293        var Ydiff = Y[i] - this.Yavg;
294        var Xdiff = X[i] - this.Xavg;
295
296        SUM_Ydiff2 += Ydiff * Ydiff;
297        SUM_Xdiff2 += Xdiff * Xdiff;
298        SUM_XdiffYdiff += Xdiff * Ydiff;
299      }
300
301      var Syx2 = (SUM_Ydiff2 - Math.pow(SUM_XdiffYdiff/SUM_Xdiff2, 2))/(n - 2);
302      var r2   = Math.pow((n*SUM_XY - SUM_X * SUM_Y), 2) /
303        ((n*SUM_XX - SUM_X*SUM_X)*(n*SUM_YY-SUM_Y*SUM_Y));
304
305      this.Syx = Math.sqrt(Syx2);
306      this.r = Math.sqrt(r2);
307
308    }
309
310  function dataDeriv(data)
311    {
312      if (data.X.length != data.Y.length)
313      {
314        throw 'length mismatch';
315      }
316      var length = data.X.length;
317
318      if (length < 2)
319      {
320        throw 'length ' + length + ' must be >= 2';
321      }
322      var X = data.X;
323      var Y = data.Y;
324
325      var deriv = {X: [], Y: [] };
326
327      for (var i = 0; i < length - 1; i++)
328      {
329        deriv.X[i] = (X[i] + X[i+1])/2;
330        deriv.Y[i] = (Y[i+1] - Y[i])/(X[i+1] - X[i]);
331      }
332      return deriv;
333    }
334
335}
336
337/* JavaScriptOptions
338   encapsulate the logic for setting and retrieving the values
339   of the javascript options.
340
341   Note: in shell, options() takes an optional comma delimited list
342   of option names, toggles the values for each option and returns the
343   list of option names which were set before the call.
344   If no argument is passed to options(), it returns the current
345   options with value true.
346
347   Usage;
348
349   // create and initialize object.
350   jsOptions = new JavaScriptOptions();
351
352   // set a particular option
353   jsOptions.setOption(name, boolean);
354
355   // reset all options to their original values.
356   jsOptions.reset();
357*/
358
359function JavaScriptOptions()
360{
361  this.orig   = {};
362  this.orig.strict = this.strict = false;
363  this.orig.werror = this.werror = false;
364
365  this.privileges = 'UniversalXPConnect';
366
367  if (typeof options == 'function')
368  {
369    // shell
370    var optString = options();
371    if (optString)
372    {
373      var optList = optString.split(',');
374      for (iOpt = 0; i < optList.length; iOpt++)
375      {
376        optName = optList[iOpt];
377        this[optName] = true;
378      }
379    }
380  }
381  else if (typeof document != 'undefined')
382  {
383    // browser
384    netscape.security.PrivilegeManager.enablePrivilege(this.privileges);
385
386    var preferences = Components.classes['@mozilla.org/preferences;1'];
387    if (!preferences)
388    {
389      throw 'JavaScriptOptions: unable to get @mozilla.org/preference;1';
390    }
391
392    var prefService = preferences.
393      getService(Components.interfaces.nsIPrefService);
394
395    if (!prefService)
396    {
397      throw 'JavaScriptOptions: unable to get nsIPrefService';
398    }
399
400    var pref = prefService.getBranch('');
401
402    if (!pref)
403    {
404      throw 'JavaScriptOptions: unable to get prefService branch';
405    }
406
407    try
408    {
409        this.orig.strict = this.strict =
410            pref.getBoolPref('javascript.options.strict');
411    }
412    catch(e)
413    {
414    }
415
416    try
417    {
418        this.orig.werror = this.werror =
419            pref.getBoolPref('javascript.options.werror');
420    }
421    catch(e)
422    {
423    }
424  }
425}
426
427JavaScriptOptions.prototype.setOption =
428function (optionName, optionValue)
429{
430  if (typeof options == 'function')
431  {
432    // shell
433    if (this[optionName] != optionValue)
434    {
435      options(optionName);
436    }
437  }
438  else if (typeof document != 'undefined')
439  {
440    // browser
441    netscape.security.PrivilegeManager.enablePrivilege(this.privileges);
442
443    var preferences = Components.classes['@mozilla.org/preferences;1'];
444    if (!preferences)
445    {
446      throw 'setOption: unable to get @mozilla.org/preference;1';
447    }
448
449    var prefService = preferences.
450    getService(Components.interfaces.nsIPrefService);
451
452    if (!prefService)
453    {
454      throw 'setOption: unable to get nsIPrefService';
455    }
456
457    var pref = prefService.getBranch('');
458
459    if (!pref)
460    {
461      throw 'setOption: unable to get prefService branch';
462    }
463
464    pref.setBoolPref('javascript.options.' + optionName, optionValue);
465  }
466
467  this[optionName] = optionValue;
468
469  return;
470}
471
472
473JavaScriptOptions.prototype.reset = function ()
474{
475  this.setOption('strict', this.orig.strict);
476  this.setOption('werror', this.orig.werror);
477}
478