1/*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/**
25 * runsunspider : runs the sunspider tests and checks for compliance
26 *
27 * @test
28 * @option -timezone=PST
29 * @runif external.sunspider
30 */
31
32/**
33 * This is not a test, but a test "framework" for running sunspider tests.
34 */
35
36function assertEq(a, b) {
37    if (a !== b) {
38        throw "ASSERTION FAILED: " + a + " should be " + b;
39    }
40}
41
42function pprint(x) {
43    if (verbose_run) {
44    print(x);
45    }
46}
47
48var runs = 0;
49var total_time = 0;
50
51function runbench(name) {
52    var filename = name.split("/").pop();
53    pprint("Running (warmup/sanity) " + filename);
54
55    var start = new Date;
56    load(name);
57
58    var stop = new Date - start;
59    total_time += stop;
60
61    pprint(filename + " done in " + stop + " ms");
62    runs++;
63}
64
65var m_w;
66var m_z;
67var MAXINT;
68
69//produce deterministic random numbers for test suite
70function pseudorandom() {
71    m_z = 36969 * (m_z & 65535) + (m_z >> 16);
72    m_w = 18000 * (m_w & 65535) + (m_w >> 16);
73    return (Math.abs((m_z << 16) + m_w) & MAXINT) / MAXINT;
74}
75
76function initrandom() {
77    m_w = 4711;
78    m_z = 17;
79    MAXINT = 0x7fffffff;
80    Math.random = pseudorandom;
81}
82
83var rtimes = 0;
84var dir = (typeof(__DIR__) == 'undefined') ? "test/script/basic/" : __DIR__;
85var single;
86var verbose_run = false;
87var runall = false;
88
89var args = [];
90if (typeof $ARGS !== 'undefined') {
91    args = $ARGS;
92} else if (typeof arguments !== 'undefined' && arguments.length != 0) {
93    args = arguments;
94}
95
96for (var i = 0; i < args.length; i++) {
97    if (args[i] === '--verbose') {
98        verbose_run = true;
99    } else if (args[i] === '--times') {
100    i++;
101    rtimes = +args[i];
102    } else if (args[i] === '--single') {
103    i++;
104    single = args[i];
105    } else if (args[i] === '--runall') {
106    i++;
107    runall = true;
108    }
109}
110
111function runsuite(tests) {
112    var changed   = false;
113    var res       = [];
114    var oldRandom = Math.random;
115
116    try {
117    for (var n = 0; n < tests.length; n++) {
118            try {
119                path = dir + '../external/sunspider/tests/sunspider-1.0.2/' + tests[n].name
120
121                initrandom();
122
123                var dd = new Date;
124
125                runbench(path);
126                if (typeof tests[n].actual !== 'undefined') {
127                    assertEq(tests[n].actual(), tests[n].expected());
128                }
129
130                var times = 0;
131                if (typeof tests[n].rerun !== 'undefined' && tests[n].times > 0) {
132                    pprint("rerunning " + tests[n].name + " " + tests[n].times + " times...");
133                    var to = tests[n].times;
134
135                    var elemsPerPercent = to / 100;
136                    var po = 0|(to / 10);
137
138            pprint("Doing warmup.");
139                    for (times = 0; times < to; times++) {
140                        initrandom();
141                        tests[n].rerun();
142                    }
143
144            pprint("Doing hot runs.");
145                    for (times = 0; times < to; times++) {
146                        initrandom();
147                        tests[n].rerun();
148                        if ((times % (po|0)) == 0) {
149                            pprint("\t" + times/to * 100 + "%");
150                        }
151                    }
152                }
153
154                var t = Math.round(((new Date - dd) / (times == 0 ? 1 : times)) * 100 / 100);
155                pprint("time per iteration: " + t + " ms");
156                if (typeof tests[n].actual !== 'undefined') {
157                    assertEq(tests[n].actual(), tests[n].expected());
158                }
159                res.push(t);
160
161                pprint("");
162
163                changed = true;
164            } catch(e) {
165                if (runall) {
166                    print("FAIL!");
167                } else {
168                    throw e;
169                }
170            }
171        }
172    } catch (e) {
173    print("FAIL!");
174    throw e;
175        // no scripting or something, silently fail
176    } finally {
177    Math.random = oldRandom;
178    }
179
180    for (var n = 0; n < tests.length; n++) {
181
182    var time = "" + res[n];
183    while (time.length < 6) {
184        time = " " + time;
185    }
186    time += " ms";
187    if (res[n] == -1) {
188        time = "<couldn't be rerun>";
189    }
190    var str = tests[n].name;
191    for (var spaces = str.length; spaces < 32; spaces++) {
192        str += " ";
193    }
194    str += " ";
195    str += time;
196
197    if (tests[n].times > 0) {
198        str += " [";
199        str += tests[n].times + " reruns]";
200    }
201    pprint(str);
202    }
203
204    return changed;
205}
206
207function hash(str) {
208    var s = "" + str;
209    var h = 0;
210    var off = 0;
211    for (var i = 0; i < s.length; i++) {
212        h = 31 * h + s.charCodeAt(off++);
213        h &= 0x7fffffff;
214    }
215    return h ^ s.length;
216}
217
218var tests = [
219
220    { name: 'regexp-dna.js',
221      actual: function() {
222      return dnaOutputString + dnaInput;
223      },
224      expected: function() {
225      return expectedDNAOutputString + expectedDNAInput;
226      },
227    },
228
229    { name: 'string-base64.js',
230      actual: function() {
231          return hash(str);
232      },
233      expected: function() {
234          return 1544571068;
235      },
236      times: rtimes,
237      rerun: function() {
238      toBinaryTable = [
239          -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
240          -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
241          -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
242              52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
243          -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
244               15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
245          -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
246              41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
247      ];
248      var str = "";
249      for (var i = 0; i < 8192; i++)
250              str += String.fromCharCode((25 * Math.random()) + 97);
251
252      for (var i = 8192; i <= 16384; i *= 2) {
253          var base64;
254          base64 = toBase64(str);
255          var encoded = base64ToString(base64);
256
257          str += str;
258      }
259      toBinaryTable = null;
260      }
261    },
262    { name: 'date-format-xparb.js',
263      actual: function() {
264          return shortFormat + longFormat;
265      },
266      expected: function() {
267          return "2017-09-05Tuesday, September 05, 2017 8:43:48 AM";
268      },
269      times: rtimes,
270      rerun: function() {
271      date = new Date("1/1/2007 1:11:11");
272      for (i = 0; i < 4000; ++i) {
273          var shortFormat = date.dateFormat("Y-m-d");
274          var longFormat = date.dateFormat("l, F d, Y g:i:s A");
275          date.setTime(date.getTime() + 84266956);
276      }
277      }
278
279    },
280    { name: 'string-validate-input.js',
281      actual: function() {
282          return hash(endResult);
283      },
284      expected: function() {
285          return 726038055;
286      },
287      times: rtimes,
288      rerun: function() {
289      doTest();
290      },
291    },
292    { name: '3d-morph.js',
293      actual: function() {
294          var acceptableDelta = 4e-15;
295          return (testOutput - 6.394884621840902e-14) < acceptableDelta;
296      },
297      expected: function() {
298          return true;
299      },
300      times: rtimes,
301      rerun: function() {
302      a = Array()
303      for (var i=0; i < nx*nz*3; ++i)
304          a[i] = 0
305      for (var i = 0; i < loops; ++i) {
306          morph(a, i/loops)
307      }
308      testOutput = 0;
309      for (var i = 0; i < nx; i++)
310          testOutput += a[3*(i*nx+i)+1];
311      a = null;
312
313      }
314    },
315    { name: 'crypto-aes.js',
316      actual: function() {
317          return plainText;
318      },
319      expected: function() {
320          return decryptedText;
321      },
322      times: rtimes,
323      rerun: function() {
324      cipherText = AESEncryptCtr(plainText, password, 256);
325      decryptedText = AESDecryptCtr(cipherText, password, 256);
326
327      }
328    },
329    { name: 'crypto-md5.js',
330      actual: function() {
331          return md5Output;
332      },
333      expected: function() {
334          return "a831e91e0f70eddcb70dc61c6f82f6cd";
335      },
336      times: rtimes,
337      rerun: function() {
338      md5Output = hex_md5(plainText);
339      }
340    },
341
342    { name: 'crypto-sha1.js',
343      actual: function() {
344          return sha1Output;
345      },
346      expected: function() {
347          return "2524d264def74cce2498bf112bedf00e6c0b796d";
348      },
349      times: rtimes,
350      rerun: function() {
351      sha1Output = hex_sha1(plainText);
352      }
353    },
354
355    { name: 'bitops-bitwise-and.js',
356      actual: function() {
357          return result;
358      },
359      expected: function() {
360          return 0;
361      },
362      times: rtimes,
363      rerun: function() {
364      bitwiseAndValue = 4294967296;
365      for (var i = 0; i < 600000; i++) {
366          bitwiseAndValue = bitwiseAndValue & i;
367      }
368      result = bitwiseAndValue;
369      }
370    },
371
372    { name: 'bitops-bits-in-byte.js',
373      actual: function() {
374          return result;
375      },
376      expected: function() {
377          return 358400;
378      },
379      times: rtimes,
380      rerun: function() {
381      result = TimeFunc(bitsinbyte);
382      }
383    },
384
385    { name: 'bitops-nsieve-bits.js',
386      actual: function() {
387          var ret = 0;
388          for (var i = 0; i < result.length; ++i) {
389              ret += result[i];
390          }
391          ret += result.length;
392          return ret;
393      },
394      expected: function() {
395          return -1286749539853;
396      },
397      times: rtimes,
398      rerun: function() {
399      result = sieve();
400      }
401    },
402
403    { name: 'bitops-3bit-bits-in-byte.js',
404      actual: function() {
405          return sum;
406      },
407      expected: function() {
408          return 512000;
409      },
410      times: rtimes,
411      rerun: function() {
412      sum = TimeFunc(fast3bitlookup);
413      }
414    },
415
416    { name: 'access-nbody.js',
417      actual: function() {
418          return ret;
419      },
420      expected: function() {
421            return -1.3524862408537381;
422      },
423      times: rtimes,
424      rerun: function() {
425      var ret = 0;
426      for (var n = 3; n <= 24; n *= 2) {
427          (function(){
428          var bodies = new NBodySystem( Array(
429              Sun(),Jupiter(),Saturn(),Uranus(),Neptune()
430          ));
431          var max = n * 100;
432
433          ret += bodies.energy();
434          for (var i=0; i<max; i++){
435              bodies.advance(0.01);
436          }
437          ret += bodies.energy();
438          })();
439      }
440      }
441    },
442
443    { name: 'access-binary-trees.js',
444      actual: function() {
445          return ret;
446      },
447      expected: function() {
448          return -4;
449      },
450      times: rtimes,
451      rerun: function() {
452      ret = 0;
453
454      for (var n = 4; n <= 7; n += 1) {
455          var minDepth = 4;
456          var maxDepth = Math.max(minDepth + 2, n);
457          var stretchDepth = maxDepth + 1;
458
459          var check = bottomUpTree(0,stretchDepth).itemCheck();
460
461          var longLivedTree = bottomUpTree(0,maxDepth);
462          for (var depth=minDepth; depth<=maxDepth; depth+=2){
463          var iterations = 1 << (maxDepth - depth + minDepth);
464
465          check = 0;
466          for (var i=1; i<=iterations; i++){
467              check += bottomUpTree(i,depth).itemCheck();
468              check += bottomUpTree(-i,depth).itemCheck();
469          }
470          }
471
472          ret += longLivedTree.itemCheck();
473      }
474      }
475    },
476
477    { name: 'access-fannkuch.js',
478      actual: function() {
479          return ret;
480      },
481      expected: function() {
482          return 22;
483      },
484      times: rtimes,
485      rerun: function() {
486      n = 8;
487      ret = fannkuch(n);
488      }
489    },
490
491    { name: 'math-spectral-norm.js',
492      actual: function() {
493          var ret = '';
494          for (var i = 6; i <= 48; i *= 2) {
495              ret += spectralnorm(i) + ',';
496          }
497          return ret;
498      },
499      expected: function() {
500          return "1.2657786149754053,1.2727355112619148,1.273989979775574,1.274190125290389,";
501      },
502      times: rtimes,
503      rerun: function() {
504      total = 0;
505      for (var i = 6; i <= 48; i *= 2) {
506          total += spectralnorm(i);
507      }
508      }
509    },
510
511    { name: '3d-raytrace.js',
512      actual: function() {
513          return hash(testOutput);
514      },
515      expected: function() {
516          return 230692593;
517      },
518      times: rtimes,
519      rerun: function() {
520      testOutput = arrayToCanvasCommands(raytraceScene());
521      }
522    },
523
524    { name: 'math-cordic.js',
525      actual: function() {
526          return total;
527      },
528      expected: function() {
529          return 10362.570468755888;
530      },
531      times: rtimes,
532      rerun: function() {
533      total = 0;
534      cordic(25000);
535      }
536    },
537
538    { name: 'controlflow-recursive.js',
539      actual: function() {
540          var ret = 0;
541          for (var i = 3; i <= 5; i++) {
542              ret += ack(3,i);
543              ret += fib(17.0+i);
544              ret += tak(3*i+3,2*i+2,i+1);
545          }
546          return ret;
547      },
548      expected: function() {
549          return 57775;
550      },
551      times: rtimes,
552      rerun: function() {
553      result = 0;
554      for (var i = 3; i <= 5; i++) {
555          result += ack(3,i);
556          result += fib(17.0+i);
557          result += tak(3*i+3,2*i+2,i+1);
558      }
559      }
560    },
561
562    { name: 'date-format-tofte.js',
563      actual: function() {
564          return shortFormat + longFormat;
565      },
566      expected: function() {
567          return "2008-05-01Thursday, May 01, 2008 6:31:22 PM";
568      },
569      times: rtimes,
570      rerun: function() {
571      date = new Date("1/1/2007 1:11:11");
572      for (i = 0; i < 500; ++i) {
573          var shortFormat = date.formatDate("Y-m-d");
574          var longFormat = date.formatDate("l, F d, Y g:i:s A");
575          date.setTime(date.getTime() + 84266956);
576      }
577      }
578    },
579
580    { name: 'string-tagcloud.js',
581      actual: function() {
582          // The result string embeds floating-point numbers, which can vary a bit on different platforms,
583          // so we truncate them a bit before comparing.
584          var tagcloud_norm = tagcloud.replace(/([0-9.]+)px/g, function(str, p1) { return p1.substr(0, 10) + 'px' })
585          return tagcloud_norm.length;
586      },
587      expected: function() {
588          return 295906;
589      },
590      times: rtimes,
591      rerun: function() {
592      tagInfo = tagInfoJSON.parseJSON(function(a, b) { if (a == "popularity") { return Math.log(b) / log2; } else {return b; } });
593      tagcloud = makeTagCloud(tagInfo);
594      }
595    },
596
597    { name: 'math-partial-sums.js',
598      actual: function() {
599      return total;
600      },
601      expected: function() {
602      return 60.08994194659945;
603      },
604      times: rtimes,
605      rerun: function() {
606      total = 0;
607      for (var i = 1024; i <= 16384; i *= 2) {
608          total += partial(i);
609      }
610      }
611    },
612
613    { name: 'access-nsieve.js',
614      actual: function() {
615      return result;
616      },
617      expected: function() {
618      return 14302;
619      },
620      times: rtimes,
621      rerun: function() {
622      result = sieve();
623      }
624    },
625
626    { name: '3d-cube.js',
627      times: rtimes,
628      rerun: function() {
629      Q = new Array();
630      MTrans = new Array();  // transformation matrix
631      MQube = new Array();  // position information of qube
632      I = new Array();      // entity matrix
633      Origin = new Object();
634      Testing = new Object();
635      for ( var i = 20; i <= 160; i *= 2 ) {
636          Init(i);
637      }
638      }
639    },
640
641    //TODO no easy way to sanity check result
642    { name: 'string-fasta.js',
643      times: rtimes,
644      rerun: function() {
645      ret = 0;
646      count = 7;
647      fastaRepeat(2*count*100000, ALU);
648      fastaRandom(3*count*1000, IUB);
649      fastaRandom(5*count*1000, HomoSap);
650      }
651    },
652
653    //TODO no easy way to sanity check result
654    { name: 'string-unpack-code.js',
655      actual: function() {
656          return decompressedMochiKit.length == 106415 &&
657              decompressedMochiKit[2000] == '5' &&
658              decompressedMochiKit[12000] == '_' &&
659              decompressedMochiKit[82556] == '>';
660      },
661      expected: function() {
662      return true;
663      },
664    },
665
666];
667
668tests.sort(function(a,b) { return a.name.localeCompare(b.name); });
669if (typeof single !== 'undefined') {
670    for (i in tests) {
671    if (tests[i].name === single) {
672        singleTest = tests[i];
673        tests = [singleTest];
674        break;
675    }
676    }
677    if (tests.length != 1) {
678    throw "unknown single test '" + single + "'";
679    }
680}
681
682
683// handle the case this script may be run by a JS engine that doesn't
684// support __DIR__ global variable.
685
686runsuite(tests);
687
688pprint('\n' + runs + "/" + tests.length + " tests were successfully run in " + total_time + " ms ");
689
690print("Sunspider finished!");
691