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 
  36 function assertEq(a, b) {
  37     if (a !== b) {
  38         throw "ASSERTION FAILED: " + a + " should be " + b;
  39     }
  40 }
  41 
  42 function pprint(x) {
  43     if (verbose_run) {
  44     print(x);
  45     }
  46 }
  47 
  48 var runs = 0;
  49 var total_time = 0;
  50 
  51 function 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 
  65 var m_w;
  66 var m_z;
  67 var MAXINT;
  68 
  69 //produce deterministic random numbers for test suite
  70 function 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 
  76 function initrandom() {
  77     m_w = 4711;
  78     m_z = 17;
  79     MAXINT = 0x7fffffff;
  80     Math.random = pseudorandom;
  81 }
  82 
  83 var rtimes = 0;
  84 var dir = (typeof(__DIR__) == 'undefined') ? "test/script/basic/" : __DIR__;
  85 var single;
  86 var verbose_run = false;
  87 var runall = false;
  88 
  89 var args = [];
  90 if (typeof $ARGS !== 'undefined') {
  91     args = $ARGS;
  92 } else if (typeof arguments !== 'undefined' && arguments.length != 0) {
  93     args = arguments;
  94 }
  95 
  96 for (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 
 111 function 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 
 207 function 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 
 218 var 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 
 668 tests.sort(function(a,b) { return a.name.localeCompare(b.name); });
 669 if (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 
 686 runsuite(tests);
 687 
 688 pprint('\n' + runs + "/" + tests.length + " tests were successfully run in " + total_time + " ms ");
 689 
 690 print("Sunspider finished!");