tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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, "&amp;").replace(/</g, "&lt;");
    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 }