js-test-pre.js (21515B)
1 /* 2 Copyright (c) 2019 The Khronos Group Inc. 3 Use of this source code is governed by an MIT-style license that can be 4 found in the LICENSE.txt file. 5 */ 6 7 (function() { 8 var testHarnessInitialized = false; 9 10 var initNonKhronosFramework = function() { 11 if (testHarnessInitialized) { 12 return; 13 } 14 testHarnessInitialized = true; 15 16 /* -- plaform specific code -- */ 17 18 // WebKit Specific code. Add your code here. 19 if (window.testRunner && !window.layoutTestController) { 20 window.layoutTestController = window.testRunner; 21 } 22 23 if (window.layoutTestController) { 24 window.layoutTestController.dumpAsText(); 25 window.layoutTestController.waitUntilDone(); 26 } 27 if (window.internals) { 28 // The WebKit testing system compares console output. 29 // Because the output of the WebGL Tests is GPU dependent 30 // we turn off console messages. 31 window.console.log = function() { }; 32 window.console.error = function() { }; 33 window.internals.settings.setWebGLErrorsToConsoleEnabled(false); 34 } 35 36 /* -- end platform specific code --*/ 37 } 38 39 this.initTestingHarness = function() { 40 initNonKhronosFramework(); 41 } 42 }()); 43 44 var getUrlOptions = (function() { 45 var _urlOptionsParsed = false; 46 var _urlOptions = {}; 47 return function() { 48 if (!_urlOptionsParsed) { 49 var s = window.location.href; 50 var q = s.indexOf("?"); 51 var e = s.indexOf("#"); 52 if (e < 0) { 53 e = s.length; 54 } 55 var query = s.substring(q + 1, e); 56 var pairs = query.split("&"); 57 for (var ii = 0; ii < pairs.length; ++ii) { 58 var keyValue = pairs[ii].split("="); 59 var key = keyValue[0]; 60 var value = decodeURIComponent(keyValue[1]); 61 _urlOptions[key] = value; 62 } 63 _urlOptionsParsed = true; 64 } 65 66 return _urlOptions; 67 } 68 })(); 69 70 if (typeof quietMode == 'undefined') { 71 var quietMode = (function() { 72 var _quietModeChecked = false; 73 var _isQuiet = false; 74 return function() { 75 if (!_quietModeChecked) { 76 _isQuiet = (getUrlOptions().quiet == 1); 77 _quietModeChecked = true; 78 } 79 return _isQuiet; 80 } 81 })(); 82 } 83 84 function nonKhronosFrameworkNotifyDone() { 85 // WebKit Specific code. Add your code here. 86 if (window.layoutTestController) { 87 window.layoutTestController.notifyDone(); 88 } 89 } 90 91 const RESULTS = { 92 pass: 0, 93 fail: 0, 94 }; 95 96 // We cache these values since they will potentially be accessed many (100k+) 97 // times and accessing window can be significantly slower than a local variable. 98 const locationPathname = window.location.pathname; 99 const webglTestHarness = window.parent.webglTestHarness; 100 101 function reportTestResultsToHarness(success, msg) { 102 if (success) { 103 RESULTS.pass += 1; 104 } else { 105 RESULTS.fail += 1; 106 } 107 if (webglTestHarness) { 108 webglTestHarness.reportResults(locationPathname, success, msg); 109 } 110 } 111 112 function reportSkippedTestResultsToHarness(success, msg) { 113 if (webglTestHarness) { 114 webglTestHarness.reportResults(locationPathname, success, msg, true); 115 } 116 } 117 118 function notifyFinishedToHarness() { 119 if (window._didNotifyFinishedToHarness) { 120 testFailed("Duplicate notifyFinishedToHarness()"); 121 } 122 window._didNotifyFinishedToHarness = true; 123 124 if (webglTestHarness) { 125 webglTestHarness.notifyFinished(locationPathname); 126 } 127 if (window.nonKhronosFrameworkNotifyDone) { 128 window.nonKhronosFrameworkNotifyDone(); 129 } 130 } 131 132 var _bufferedConsoleLogs = []; 133 134 function _bufferedLogToConsole(msg) 135 { 136 if (_bufferedConsoleLogs) { 137 _bufferedConsoleLogs.push(msg); 138 } else if (window.console) { 139 window.console.log(msg); 140 } 141 } 142 143 // Public entry point exposed to many other files. 144 function bufferedLogToConsole(msg) 145 { 146 _bufferedLogToConsole(msg); 147 } 148 149 // Called implicitly by testFailed(). 150 function _flushBufferedLogsToConsole() 151 { 152 if (_bufferedConsoleLogs) { 153 if (window.console) { 154 for (var ii = 0; ii < _bufferedConsoleLogs.length; ++ii) { 155 window.console.log(_bufferedConsoleLogs[ii]); 156 } 157 } 158 _bufferedConsoleLogs = null; 159 } 160 } 161 162 var _jsTestPreVerboseLogging = false; 163 164 function enableJSTestPreVerboseLogging() 165 { 166 _jsTestPreVerboseLogging = true; 167 } 168 169 function description(msg) 170 { 171 initTestingHarness(); 172 if (msg === undefined) { 173 msg = document.title; 174 } 175 // For MSIE 6 compatibility 176 var span = document.createElement("span"); 177 span.innerHTML = '<p>' + msg + '</p><p>On success, you will see a series of "<span class="pass">PASS</span>" messages, followed by "<span class="pass">TEST COMPLETE</span>".</p>'; 178 var description = document.getElementById("description"); 179 if (description.firstChild) 180 description.replaceChild(span, description.firstChild); 181 else 182 description.appendChild(span); 183 if (_jsTestPreVerboseLogging) { 184 _bufferedLogToConsole(msg); 185 } 186 } 187 188 function _addSpan(contents) 189 { 190 var span = document.createElement("span"); 191 document.getElementById("console").appendChild(span); // insert it first so XHTML knows the namespace 192 span.innerHTML = contents + '<br />'; 193 } 194 195 function debug(msg) 196 { 197 if (!quietMode()) 198 _addSpan(msg); 199 if (_jsTestPreVerboseLogging) { 200 _bufferedLogToConsole(msg); 201 } 202 } 203 204 function escapeHTML(text) 205 { 206 return text.replace(/&/g, "&").replace(/</g, "<"); 207 } 208 /** 209 * Defines the exception type for a test failure. 210 * @constructor 211 * @param {string} message The error message. 212 */ 213 var TestFailedException = function (message) { 214 this.message = message; 215 this.name = "TestFailedException"; 216 }; 217 218 /** 219 * @param {string=} msg 220 */ 221 function testPassed(msg) { 222 msg = msg || 'Passed'; 223 if (_currentTestName) 224 msg = _currentTestName + ': ' + msg; 225 226 reportTestResultsToHarness(true, msg); 227 228 if (!quietMode()) 229 _addSpan('<span><span class="pass">PASS</span> ' + escapeHTML(msg) + '</span>'); 230 if (_jsTestPreVerboseLogging) { 231 _bufferedLogToConsole('PASS ' + msg); 232 } 233 } 234 235 /** 236 * @param {string=} msg 237 */ 238 function testFailed(msg) { 239 msg = msg || 'Failed'; 240 if (_currentTestName) 241 msg = _currentTestName + ': ' + msg; 242 243 reportTestResultsToHarness(false, msg); 244 _addSpan('<span><span class="fail">FAIL</span> ' + escapeHTML(msg) + '</span>'); 245 _bufferedLogToConsole('FAIL ' + msg); 246 _flushBufferedLogsToConsole(); 247 } 248 249 var _currentTestName; 250 251 /** 252 * Sets the current test name for usage within testPassedOptions/testFailedOptions. 253 * @param {string=} name The name to set as the current test name. 254 */ 255 function setCurrentTestName(name) 256 { 257 _currentTestName = name; 258 } 259 260 /** 261 * Gets the current test name in use within testPassedOptions/testFailedOptions. 262 * @return {string} The name of the current test. 263 */ 264 function getCurrentTestName() 265 { 266 return _currentTestName; 267 } 268 269 /** 270 * Variation of the testPassed function, with the option to not show (and thus not count) the test's pass result. 271 * @param {string} msg The message to be shown in the pass result. 272 * @param {boolean} addSpan Indicates whether the message will be visible (thus counted in the results) or not. 273 */ 274 function testPassedOptions(msg, addSpan) 275 { 276 reportTestResultsToHarness(true, _currentTestName + ": " + msg); 277 if (addSpan && !quietMode()) 278 { 279 _addSpan('<span><span class="pass">PASS</span> ' + escapeHTML(_currentTestName) + ": " + escapeHTML(msg) + '</span>'); 280 } 281 if (_jsTestPreVerboseLogging) { 282 _bufferedLogToConsole('PASS ' + msg); 283 } 284 } 285 286 /** 287 * Report skipped tests. 288 * @param {string} msg The message to be shown in the skip result. 289 * @param {boolean} addSpan Indicates whether the message will be visible (thus counted in the results) or not. 290 */ 291 function testSkippedOptions(msg, addSpan) 292 { 293 reportSkippedTestResultsToHarness(true, _currentTestName + ": " + msg); 294 if (addSpan && !quietMode()) 295 { 296 _addSpan('<span><span class="warn">SKIP</span> ' + escapeHTML(_currentTestName) + ": " + escapeHTML(msg) + '</span>'); 297 } 298 if (_jsTestPreVerboseLogging) { 299 _bufferedLogToConsole('SKIP' + msg); 300 } 301 } 302 303 /** 304 * Variation of the testFailed function, with the option to throw an exception or not. 305 * @param {string} msg The message to be shown in the fail result. 306 * @param {boolean} exthrow Indicates whether the function will throw a TestFailedException or not. 307 */ 308 function testFailedOptions(msg, exthrow) 309 { 310 reportTestResultsToHarness(false, _currentTestName + ": " + msg); 311 _addSpan('<span><span class="fail">FAIL</span> ' + escapeHTML(_currentTestName) + ": " + escapeHTML(msg) + '</span>'); 312 _bufferedLogToConsole('FAIL ' + msg); 313 _flushBufferedLogsToConsole(); 314 if (exthrow) { 315 _currentTestName = ""; //Remembering to set the name of current testcase to empty string. 316 throw new TestFailedException(msg); 317 } 318 } 319 320 function areArraysEqual(_a, _b) 321 { 322 try { 323 if (_a.length !== _b.length) 324 return false; 325 for (var i = 0; i < _a.length; i++) 326 if (_a[i] !== _b[i]) 327 return false; 328 } catch (ex) { 329 return false; 330 } 331 return true; 332 } 333 334 function isMinusZero(n) 335 { 336 // the only way to tell 0 from -0 in JS is the fact that 1/-0 is 337 // -Infinity instead of Infinity 338 return n === 0 && 1/n < 0; 339 } 340 341 function isResultCorrect(_actual, _expected) 342 { 343 if (_expected === 0) 344 return _actual === _expected && (1/_actual) === (1/_expected); 345 if (_actual === _expected) 346 return true; 347 if (typeof(_expected) == "number" && isNaN(_expected)) 348 return typeof(_actual) == "number" && isNaN(_actual); 349 if (Object.prototype.toString.call(_expected) == Object.prototype.toString.call([])) 350 return areArraysEqual(_actual, _expected); 351 return false; 352 } 353 354 function stringify(v) 355 { 356 if (v === 0 && 1/v < 0) 357 return "-0"; 358 else return "" + v; 359 } 360 361 function evalAndLog(_a) 362 { 363 if (typeof _a != "string") 364 debug("WARN: tryAndLog() expects a string argument"); 365 366 // Log first in case things go horribly wrong or this causes a sync event. 367 debug(_a); 368 369 var _av; 370 try { 371 _av = eval(_a); 372 } catch (e) { 373 testFailed(_a + " threw exception " + e); 374 } 375 return _av; 376 } 377 378 function shouldBeString(evalable, expected) { 379 const val = eval(evalable); 380 const text = evalable + " should be " + expected + "."; 381 if (val == expected) { 382 testPassed(text); 383 } else { 384 testFailed(text + " (was " + val + ")"); 385 } 386 } 387 388 function shouldBe(_a, _b, quiet) 389 { 390 if (typeof _a != "string" || typeof _b != "string") 391 debug("WARN: shouldBe() expects string arguments"); 392 var exception; 393 var _av; 394 try { 395 _av = eval(_a); 396 } catch (e) { 397 exception = e; 398 } 399 var _bv = eval(_b); 400 401 if (exception) 402 testFailed(_a + " should be " + _bv + ". Threw exception " + exception); 403 else if (isResultCorrect(_av, _bv)) { 404 if (!quiet) { 405 testPassed(_a + " is " + _b); 406 } 407 } else if (typeof(_av) == typeof(_bv)) 408 testFailed(_a + " should be " + _bv + ". Was " + stringify(_av) + "."); 409 else 410 testFailed(_a + " should be " + _bv + " (of type " + typeof _bv + "). Was " + _av + " (of type " + typeof _av + ")."); 411 } 412 413 function shouldNotBe(_a, _b, quiet) 414 { 415 if (typeof _a != "string" || typeof _b != "string") 416 debug("WARN: shouldNotBe() expects string arguments"); 417 var exception; 418 var _av; 419 try { 420 _av = eval(_a); 421 } catch (e) { 422 exception = e; 423 } 424 var _bv = eval(_b); 425 426 if (exception) 427 testFailed(_a + " should not be " + _bv + ". Threw exception " + exception); 428 else if (!isResultCorrect(_av, _bv)) { 429 if (!quiet) { 430 testPassed(_a + " is not " + _b); 431 } 432 } else 433 testFailed(_a + " should not be " + _bv + "."); 434 } 435 436 function shouldBeTrue(_a) { shouldBe(_a, "true"); } 437 function shouldBeFalse(_a) { shouldBe(_a, "false"); } 438 function shouldBeNaN(_a) { shouldBe(_a, "NaN"); } 439 function shouldBeNull(_a) { shouldBe(_a, "null"); } 440 441 function shouldBeEqualToString(a, b) 442 { 443 var unevaledString = '"' + b.replace(/"/g, "\"") + '"'; 444 shouldBe(a, unevaledString); 445 } 446 447 function shouldEvaluateTo(actual, expected) { 448 // A general-purpose comparator. 'actual' should be a string to be 449 // evaluated, as for shouldBe(). 'expected' may be any type and will be 450 // used without being eval'ed. 451 if (expected == null) { 452 // Do this before the object test, since null is of type 'object'. 453 shouldBeNull(actual); 454 } else if (typeof expected == "undefined") { 455 shouldBeUndefined(actual); 456 } else if (typeof expected == "function") { 457 // All this fuss is to avoid the string-arg warning from shouldBe(). 458 try { 459 var actualValue = eval(actual); 460 } catch (e) { 461 testFailed("Evaluating " + actual + ": Threw exception " + e); 462 return; 463 } 464 shouldBe("'" + actualValue.toString().replace(/\n/g, "") + "'", 465 "'" + expected.toString().replace(/\n/g, "") + "'"); 466 } else if (typeof expected == "object") { 467 shouldBeTrue(actual + " == '" + expected + "'"); 468 } else if (typeof expected == "string") { 469 shouldBe(actual, expected); 470 } else if (typeof expected == "boolean") { 471 shouldBe("typeof " + actual, "'boolean'"); 472 if (expected) 473 shouldBeTrue(actual); 474 else 475 shouldBeFalse(actual); 476 } else if (typeof expected == "number") { 477 shouldBe(actual, stringify(expected)); 478 } else { 479 debug(expected + " is unknown type " + typeof expected); 480 shouldBeTrue(actual, "'" +expected.toString() + "'"); 481 } 482 } 483 484 function shouldBeNonZero(_a) 485 { 486 var exception; 487 var _av; 488 try { 489 _av = eval(_a); 490 } catch (e) { 491 exception = e; 492 } 493 494 if (exception) 495 testFailed(_a + " should be non-zero. Threw exception " + exception); 496 else if (_av != 0) 497 testPassed(_a + " is non-zero."); 498 else 499 testFailed(_a + " should be non-zero. Was " + _av); 500 } 501 502 function shouldBeNonNull(_a) 503 { 504 var exception; 505 var _av; 506 try { 507 _av = eval(_a); 508 } catch (e) { 509 exception = e; 510 } 511 512 if (exception) 513 testFailed(_a + " should be non-null. Threw exception " + exception); 514 else if (_av != null) 515 testPassed(_a + " is non-null."); 516 else 517 testFailed(_a + " should be non-null. Was " + _av); 518 } 519 520 function shouldBeUndefined(_a) 521 { 522 var exception; 523 var _av; 524 try { 525 _av = eval(_a); 526 } catch (e) { 527 exception = e; 528 } 529 530 if (exception) 531 testFailed(_a + " should be undefined. Threw exception " + exception); 532 else if (typeof _av == "undefined") 533 testPassed(_a + " is undefined."); 534 else 535 testFailed(_a + " should be undefined. Was " + _av); 536 } 537 538 function shouldBeDefined(_a) 539 { 540 var exception; 541 var _av; 542 try { 543 _av = eval(_a); 544 } catch (e) { 545 exception = e; 546 } 547 548 if (exception) 549 testFailed(_a + " should be defined. Threw exception " + exception); 550 else if (_av !== undefined) 551 testPassed(_a + " is defined."); 552 else 553 testFailed(_a + " should be defined. Was " + _av); 554 } 555 556 function shouldBeLessThanOrEqual(_a, _b) { 557 if (typeof _a != "string" || typeof _b != "string") 558 debug("WARN: shouldBeLessThanOrEqual expects string arguments"); 559 560 var exception; 561 var _av; 562 try { 563 _av = eval(_a); 564 } catch (e) { 565 exception = e; 566 } 567 var _bv = eval(_b); 568 569 if (exception) 570 testFailed(_a + " should be <= " + _b + ". Threw exception " + exception); 571 else if (typeof _av == "undefined" || _av > _bv) 572 testFailed(_a + " should be >= " + _b + ". Was " + _av + " (of type " + typeof _av + ")."); 573 else 574 testPassed(_a + " is <= " + _b); 575 } 576 577 function shouldBeGreaterThanOrEqual(_a, _b) { 578 if (typeof _a != "string" || typeof _b != "string") 579 debug("WARN: shouldBeGreaterThanOrEqual expects string arguments"); 580 581 var exception; 582 var _av; 583 try { 584 _av = eval(_a); 585 } catch (e) { 586 exception = e; 587 } 588 var _bv = eval(_b); 589 590 if (exception) 591 testFailed(_a + " should be >= " + _b + ". Threw exception " + exception); 592 else if (typeof _av == "undefined" || _av < _bv) 593 testFailed(_a + " should be >= " + _b + ". Was " + _av + " (of type " + typeof _av + ")."); 594 else 595 testPassed(_a + " is >= " + _b); 596 } 597 598 function expectTrue(v, msg) { 599 if (v) { 600 testPassed(msg); 601 } else { 602 testFailed(msg); 603 } 604 } 605 606 function maxArrayDiff(a, b) { 607 if (a.length != b.length) 608 throw new Error(`a and b have different lengths: ${a.length} vs ${b.length}`); 609 610 let diff = 0; 611 for (const i in a) { 612 diff = Math.max(diff, Math.abs(a[i] - b[i])); 613 } 614 return diff; 615 } 616 617 function expectArray(was, expected, maxDiff=0) { 618 const diff = maxArrayDiff(expected, was); 619 let str = `Expected [${expected.toString()}]`; 620 let fn = testPassed; 621 if (maxDiff) { 622 str += ' +/- ' + maxDiff; 623 } 624 if (diff > maxDiff) { 625 fn = testFailed; 626 str += `, was [${was.toString()}]`; 627 } 628 fn(str); 629 } 630 631 function shouldThrow(_a, _e) 632 { 633 var exception; 634 var _av; 635 try { 636 _av = eval(_a); 637 } catch (e) { 638 exception = e; 639 } 640 641 var _ev; 642 if (_e) 643 _ev = eval(_e); 644 645 if (exception) { 646 if (typeof _e == "undefined" || exception == _ev) 647 testPassed(_a + " threw exception " + exception + "."); 648 else 649 testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Threw exception " + exception + "."); 650 } else if (typeof _av == "undefined") 651 testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was undefined."); 652 else 653 testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was " + _av + "."); 654 } 655 656 function shouldNotThrow(evalStr, desc) { 657 desc = desc || `\`${evalStr}\``; 658 try { 659 eval(evalStr); 660 testPassed(`${desc} should not throw.`); 661 } catch (e) { 662 testFailed(`${desc} should not throw, but threw exception ${e}.`); 663 } 664 } 665 666 667 function shouldBeType(_a, _type) { 668 var exception; 669 var _av; 670 try { 671 _av = eval(_a); 672 } catch (e) { 673 exception = e; 674 } 675 676 var _typev = eval(_type); 677 678 if(_typev === Number){ 679 if(_av instanceof Number){ 680 testPassed(_a + " is an instance of Number"); 681 } 682 else if(typeof(_av) === 'number'){ 683 testPassed(_a + " is an instance of Number"); 684 } 685 else{ 686 testFailed(_a + " is not an instance of Number"); 687 } 688 } 689 else if (_av instanceof _typev) { 690 testPassed(_a + " is an instance of " + _type); 691 } else { 692 testFailed(_a + " is not an instance of " + _type); 693 } 694 } 695 696 /** 697 * Shows a message in case expression test fails. 698 * @param {boolean} exp 699 * @param {straing} message 700 */ 701 function checkMessage(exp, message) { 702 if ( !exp ) 703 _addSpan('<span><span class="warn">WARNING</span> ' + escapeHTML(_currentTestName) + ": " + escapeHTML(message) + '</span>'); 704 } 705 706 function assertMsg(assertion, msg) { 707 if (assertion) { 708 testPassed(msg); 709 } else { 710 testFailed(msg); 711 } 712 } 713 714 /** 715 * Variation of the assertMsg function, with the option to not show (and thus not count) the test's pass result, 716 * and throw or not a TestFailedException in case of failure. 717 * @param {boolean} assertion If this is true, means success, else failure. 718 * @param {?string} msg The message to be shown in the result. 719 * @param {boolean} verbose In case of success, determines if the test will show it's result and count in the results. 720 * @param {boolean} exthrow In case of failure, determines if the function will throw a TestFailedException. 721 */ 722 function assertMsgOptions(assertion, msg, verbose, exthrow) { 723 if (assertion) { 724 testPassedOptions(msg, verbose); 725 } else { 726 testFailedOptions(msg, exthrow); 727 } 728 } 729 730 731 function webglHarnessCollectGarbage() { 732 if (window.GCController) { 733 window.GCController.collect(); 734 return; 735 } 736 737 if (window.opera && window.opera.collect) { 738 window.opera.collect(); 739 return; 740 } 741 742 try { 743 window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) 744 .getInterface(Components.interfaces.nsIDOMWindowUtils) 745 .garbageCollect(); 746 return; 747 } catch(e) {} 748 749 if (window.gc) { 750 window.gc(); 751 return; 752 } 753 754 if (window.CollectGarbage) { 755 CollectGarbage(); 756 return; 757 } 758 759 // WebKit's MiniBrowser. 760 if (window.$vm) { 761 window.$vm.gc(); 762 return; 763 } 764 765 function gcRec(n) { 766 if (n < 1) 767 return {}; 768 var temp = {i: "ab" + i + (i / 100000)}; 769 temp += "foo"; 770 gcRec(n-1); 771 } 772 for (var i = 0; i < 1000; i++) 773 gcRec(10); 774 } 775 776 function finishTest() { 777 successfullyParsed = true; 778 var epilogue = document.createElement("script"); 779 var basePath = ""; 780 var expectedBase = "js-test-pre.js"; 781 var scripts = document.getElementsByTagName('script'); 782 for (var script, i = 0; script = scripts[i]; i++) { 783 var src = script.src; 784 var l = src.length; 785 if (src.substr(l - expectedBase.length) == expectedBase) { 786 basePath = src.substr(0, l - expectedBase.length); 787 break; 788 } 789 } 790 epilogue.src = basePath + "js-test-post.js"; 791 document.body.appendChild(epilogue); 792 } 793 794 /// Prefer `call(() => { ... })` to `(() => { ... })()`\ 795 /// This way, it's clear up-front that we're calling not just defining. 796 function call(fn) { 797 return fn(); 798 } 799 800 /// `for (const i of range(3))` => 0, 1, 2 801 /// Don't use `for...in range(n)`, it will not work. 802 function* range(n) { 803 for (let i = 0; i < n; i++) { 804 yield i; 805 } 806 }