tor-browser

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

audit.js (46835B)


      1 // Copyright 2016 The Chromium Authors
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // See https://github.com/web-platform-tests/wpt/issues/12781 for information on
      6 // the purpose of audit.js, and why testharness.js does not suffice.
      7 
      8 /**
      9 * @fileOverview  WebAudio layout test utility library. Built around W3C's
     10 *                testharness.js. Includes asynchronous test task manager,
     11 *                assertion utilities.
     12 * @dependency    testharness.js
     13 */
     14 
     15 
     16 (function() {
     17 
     18  'use strict';
     19 
     20  // Selected methods from testharness.js.
     21  let testharnessProperties = [
     22    'test', 'async_test', 'promise_test', 'promise_rejects_js', 'generate_tests',
     23    'setup', 'done', 'assert_true', 'assert_false'
     24  ];
     25 
     26  // Check if testharness.js is properly loaded. Throw otherwise.
     27  for (let name in testharnessProperties) {
     28    if (!self.hasOwnProperty(testharnessProperties[name]))
     29      throw new Error('Cannot proceed. testharness.js is not loaded.');
     30  }
     31 })();
     32 
     33 
     34 window.Audit = (function() {
     35 
     36  'use strict';
     37 
     38  // NOTE: Moving this method (or any other code above) will change the location
     39  // of 'CONSOLE ERROR...' message in the expected text files.
     40  function _logError(message) {
     41    console.error('[audit.js] ' + message);
     42  }
     43 
     44  function _logPassed(message) {
     45    test(function(arg) {
     46      assert_true(true);
     47    }, message);
     48  }
     49 
     50  function _logFailed(message, detail) {
     51    test(function() {
     52      assert_true(false, detail);
     53    }, message);
     54  }
     55 
     56  function _throwException(message) {
     57    throw new Error(message);
     58  }
     59 
     60  // TODO(hongchan): remove this hack after confirming all the tests are
     61  // finished correctly. (crbug.com/708817)
     62  const _testharnessDone = window.done;
     63  window.done = () => {
     64    _throwException('Do NOT call done() method from the test code.');
     65  };
     66 
     67  // Generate a descriptive string from a target value in various types.
     68  function _generateDescription(target, options) {
     69    let targetString;
     70 
     71    switch (typeof target) {
     72      case 'object':
     73        // Handle Arrays.
     74        if (target instanceof Array || target instanceof Float32Array ||
     75            target instanceof Float64Array || target instanceof Uint8Array) {
     76          let arrayElements = target.length < options.numberOfArrayElements ?
     77              String(target) :
     78              String(target.slice(0, options.numberOfArrayElements)) + '...';
     79          targetString = '[' + arrayElements + ']';
     80        } else if (target === null) {
     81          targetString = String(target);
     82        } else {
     83          targetString = '' + String(target).split(/[\s\]]/)[1];
     84        }
     85        break;
     86      case 'function':
     87        if (Error.isPrototypeOf(target)) {
     88          targetString = "EcmaScript error " + target.name;
     89        } else {
     90          targetString = String(target);
     91        }
     92        break;
     93      default:
     94        targetString = String(target);
     95        break;
     96    }
     97 
     98    return targetString;
     99  }
    100 
    101  // Return a string suitable for printing one failed element in
    102  // |beCloseToArray|.
    103  function _formatFailureEntry(index, actual, expected, abserr, threshold) {
    104    return '\t[' + index + ']\t' + actual.toExponential(16) + '\t' +
    105        expected.toExponential(16) + '\t' + abserr.toExponential(16) + '\t' +
    106        (abserr / Math.abs(expected)).toExponential(16) + '\t' +
    107        threshold.toExponential(16);
    108  }
    109 
    110  // Compute the error threshold criterion for |beCloseToArray|
    111  function _closeToThreshold(abserr, relerr, expected) {
    112    return Math.max(abserr, relerr * Math.abs(expected));
    113  }
    114 
    115  /**
    116   * @class Should
    117   * @description Assertion subtask for the Audit task.
    118   * @param {Task} parentTask           Associated Task object.
    119   * @param {Any} actual                Target value to be tested.
    120   * @param {String} actualDescription  String description of the test target.
    121   */
    122  class Should {
    123    constructor(parentTask, actual, actualDescription) {
    124      this._task = parentTask;
    125 
    126      this._actual = actual;
    127      this._actualDescription = (actualDescription || null);
    128      this._expected = null;
    129      this._expectedDescription = null;
    130 
    131      this._detail = '';
    132      // If true and the test failed, print the actual value at the
    133      // end of the message.
    134      this._printActualForFailure = true;
    135 
    136      this._result = null;
    137 
    138      /**
    139       * @param {Number} numberOfErrors   Number of errors to be printed.
    140       * @param {Number} numberOfArrayElements  Number of array elements to be
    141       *                                        printed in the test log.
    142       * @param {Boolean} verbose         Verbose output from the assertion.
    143       */
    144      this._options = {
    145        numberOfErrors: 4,
    146        numberOfArrayElements: 16,
    147        verbose: false
    148      };
    149    }
    150 
    151    _processArguments(args) {
    152      if (args.length === 0)
    153        return;
    154 
    155      if (args.length > 0)
    156        this._expected = args[0];
    157 
    158      if (typeof args[1] === 'string') {
    159        // case 1: (expected, description, options)
    160        this._expectedDescription = args[1];
    161        Object.assign(this._options, args[2]);
    162      } else if (typeof args[1] === 'object') {
    163        // case 2: (expected, options)
    164        Object.assign(this._options, args[1]);
    165      }
    166    }
    167 
    168    _buildResultText() {
    169      if (this._result === null)
    170        _throwException('Illegal invocation: the assertion is not finished.');
    171 
    172      let actualString = _generateDescription(this._actual, this._options);
    173 
    174      // Use generated text when the description is not provided.
    175      if (!this._actualDescription)
    176        this._actualDescription = actualString;
    177 
    178      if (!this._expectedDescription) {
    179        this._expectedDescription =
    180            _generateDescription(this._expected, this._options);
    181      }
    182 
    183      // For the assertion with a single operand.
    184      this._detail =
    185          this._detail.replace(/\$\{actual\}/g, this._actualDescription);
    186 
    187      // If there is a second operand (i.e. expected value), we have to build
    188      // the string for it as well.
    189      this._detail =
    190          this._detail.replace(/\$\{expected\}/g, this._expectedDescription);
    191 
    192      // If there is any property in |_options|, replace the property name
    193      // with the value.
    194      for (let name in this._options) {
    195        if (name === 'numberOfErrors' || name === 'numberOfArrayElements' ||
    196            name === 'verbose') {
    197          continue;
    198        }
    199 
    200        // The RegExp key string contains special character. Take care of it.
    201        let re = '\$\{' + name + '\}';
    202        re = re.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1');
    203        this._detail = this._detail.replace(
    204            new RegExp(re, 'g'), _generateDescription(this._options[name]));
    205      }
    206 
    207      // If the test failed, add the actual value at the end.
    208      if (this._result === false && this._printActualForFailure === true) {
    209        this._detail += ' Got ' + actualString + '.';
    210      }
    211    }
    212 
    213    _finalize() {
    214      if (this._result) {
    215        _logPassed('  ' + this._detail);
    216      } else {
    217        _logFailed('X ' + this._detail);
    218      }
    219 
    220      // This assertion is finished, so update the parent task accordingly.
    221      this._task.update(this);
    222 
    223      // TODO(hongchan): configurable 'detail' message.
    224    }
    225 
    226    _assert(condition, passDetail, failDetail) {
    227      this._result = Boolean(condition);
    228      this._detail = this._result ? passDetail : failDetail;
    229      this._buildResultText();
    230      this._finalize();
    231 
    232      return this._result;
    233    }
    234 
    235    get result() {
    236      return this._result;
    237    }
    238 
    239    get detail() {
    240      return this._detail;
    241    }
    242 
    243    /**
    244     * should() assertions.
    245     *
    246     * @example All the assertions can have 1, 2 or 3 arguments:
    247     *   should().doAssert(expected);
    248     *   should().doAssert(expected, options);
    249     *   should().doAssert(expected, expectedDescription, options);
    250     *
    251     * @param {Any} expected                  Expected value of the assertion.
    252     * @param {String} expectedDescription    Description of expected value.
    253     * @param {Object} options                Options for assertion.
    254     * @param {Number} options.numberOfErrors Number of errors to be printed.
    255     *                                        (if applicable)
    256     * @param {Number} options.numberOfArrayElements  Number of array elements
    257     *                                                to be printed. (if
    258     *                                                applicable)
    259     * @notes Some assertions can have additional options for their specific
    260     *        testing.
    261     */
    262 
    263    /**
    264     * Check if |actual| exists.
    265     *
    266     * @example
    267     *   should({}, 'An empty object').exist();
    268     * @result
    269     *   "PASS   An empty object does exist."
    270     */
    271    exist() {
    272      return this._assert(
    273          this._actual !== null && this._actual !== undefined,
    274          '${actual} does exist.', '${actual} does not exist.');
    275    }
    276 
    277    /**
    278     * Check if |actual| operation wrapped in a function throws an exception
    279     * with a expected error type correctly. |expected| is optional. If it is an
    280     * instance of DOMException, then the description (second argument) can be
    281     * provided to be more strict about the expected exception type. |expected|
    282     * also can be other generic error types such as TypeError, RangeError or
    283     * etc.
    284     *
    285     * @example
    286     *   should(() => { let a = b; }, 'A bad code').throw();
    287     *   should(() => { new SomeConstructor(); }, 'A bad construction')
    288     *       .throw(DOMException, 'NotSupportedError');
    289     *   should(() => { let c = d; }, 'Assigning d to c')
    290     *       .throw(ReferenceError);
    291     *   should(() => { let e = f; }, 'Assigning e to f')
    292     *       .throw(ReferenceError, { omitErrorMessage: true });
    293     *
    294     * @result
    295     *   "PASS   A bad code threw an exception of ReferenceError: b is not
    296     *       defined."
    297     *   "PASS   A bad construction threw DOMException:NotSupportedError."
    298     *   "PASS   Assigning d to c threw ReferenceError: d is not defined."
    299     *   "PASS   Assigning e to f threw ReferenceError: [error message
    300     *       omitted]."
    301     */
    302    throw() {
    303      this._processArguments(arguments);
    304      this._printActualForFailure = false;
    305 
    306      let didThrowCorrectly = false;
    307      let passDetail, failDetail;
    308 
    309      try {
    310        // This should throw.
    311        this._actual();
    312        // Catch did not happen, so the test is failed.
    313        failDetail = '${actual} did not throw an exception.';
    314      } catch (error) {
    315        let errorMessage = this._options.omitErrorMessage ?
    316            ': [error message omitted]' :
    317            ': "' + error.message + '"';
    318        if (this._expected === null || this._expected === undefined) {
    319          // The expected error type was not given.
    320          didThrowCorrectly = true;
    321          passDetail = '${actual} threw ' + error.name + errorMessage + '.';
    322        } else if (this._expected === DOMException &&
    323                   this._expectedDescription !== undefined) {
    324          // Handles DOMException with an expected exception name.
    325          if (this._expectedDescription === error.name) {
    326            didThrowCorrectly = true;
    327            passDetail = '${actual} threw ${expected}' + errorMessage + '.';
    328          } else {
    329            didThrowCorrectly = false;
    330            failDetail =
    331                '${actual} threw "' + error.name + '" instead of ${expected}.';
    332          }
    333        } else if (this._expected == error.constructor) {
    334          // Handler other error types.
    335          didThrowCorrectly = true;
    336          passDetail = '${actual} threw ' + error.name + errorMessage + '.';
    337        } else {
    338          didThrowCorrectly = false;
    339          failDetail =
    340              '${actual} threw "' + error.name + '" instead of ${expected}.';
    341        }
    342      }
    343 
    344      return this._assert(didThrowCorrectly, passDetail, failDetail);
    345    }
    346 
    347    /**
    348     * Check if |actual| operation wrapped in a function does not throws an
    349     * exception correctly.
    350     *
    351     * @example
    352     *   should(() => { let foo = 'bar'; }, 'let foo = "bar"').notThrow();
    353     *
    354     * @result
    355     *   "PASS   let foo = "bar" did not throw an exception."
    356     */
    357    notThrow() {
    358      this._printActualForFailure = false;
    359 
    360      let didThrowCorrectly = false;
    361      let passDetail, failDetail;
    362 
    363      try {
    364        this._actual();
    365        passDetail = '${actual} did not throw an exception.';
    366      } catch (error) {
    367        didThrowCorrectly = true;
    368        failDetail = '${actual} incorrectly threw ' + error.name + ': "' +
    369            error.message + '".';
    370      }
    371 
    372      return this._assert(!didThrowCorrectly, passDetail, failDetail);
    373    }
    374 
    375    /**
    376     * Check if |actual| promise is resolved correctly. Note that the returned
    377     * result from promise object will be passed to the following then()
    378     * function.
    379     *
    380     * @example
    381     *   should('My promise', promise).beResolve().then((result) => {
    382     *     log(result);
    383     *   });
    384     *
    385     * @result
    386     *   "PASS   My promise resolved correctly."
    387     *   "FAIL X My promise rejected *INCORRECTLY* with _ERROR_."
    388     */
    389    beResolved() {
    390      return this._actual.then(
    391          function(result) {
    392            this._assert(true, '${actual} resolved correctly.', null);
    393            return result;
    394          }.bind(this),
    395          function(error) {
    396            this._assert(
    397                false, null,
    398                '${actual} rejected incorrectly with ' + error + '.');
    399          }.bind(this));
    400    }
    401 
    402    /**
    403     * Check if |actual| promise is rejected correctly.
    404     *
    405     * @example
    406     *   should('My promise', promise).beRejected().then(nextStuff);
    407     *
    408     * @result
    409     *   "PASS   My promise rejected correctly (with _ERROR_)."
    410     *   "FAIL X My promise resolved *INCORRECTLY*."
    411     */
    412    beRejected() {
    413      return this._actual.then(
    414          function() {
    415            this._assert(false, null, '${actual} resolved incorrectly.');
    416          }.bind(this),
    417          function(error) {
    418            this._assert(
    419                true, '${actual} rejected correctly with ' + error + '.', null);
    420          }.bind(this));
    421    }
    422 
    423    /**
    424     * Check if |actual| promise is rejected correctly.
    425     *
    426     * @example
    427     *   should(promise, 'My promise').beRejectedWith('_ERROR_').then();
    428     *
    429     * @result
    430     *   "PASS   My promise rejected correctly with _ERROR_."
    431     *   "FAIL X My promise rejected correctly but got _ACTUAL_ERROR instead of
    432     *           _EXPECTED_ERROR_."
    433     *   "FAIL X My promise resolved incorrectly."
    434     */
    435    beRejectedWith() {
    436      this._processArguments(arguments);
    437 
    438      return this._actual.then(
    439          function() {
    440            this._assert(false, null, '${actual} resolved incorrectly.');
    441          }.bind(this),
    442          function(error) {
    443            if (this._expected !== error.name) {
    444              this._assert(
    445                  false, null,
    446                  '${actual} rejected correctly but got ' + error.name +
    447                      ' instead of ' + this._expected + '.');
    448            } else {
    449              this._assert(
    450                  true,
    451                  '${actual} rejected correctly with ' + this._expected + '.',
    452                  null);
    453            }
    454          }.bind(this));
    455    }
    456 
    457    /**
    458     * Check if |actual| is a boolean true.
    459     *
    460     * @example
    461     *   should(3 < 5, '3 < 5').beTrue();
    462     *
    463     * @result
    464     *   "PASS   3 < 5 is true."
    465     */
    466    beTrue() {
    467      return this._assert(
    468          this._actual === true, '${actual} is true.',
    469          '${actual} is not true.');
    470    }
    471 
    472    /**
    473     * Check if |actual| is a boolean false.
    474     *
    475     * @example
    476     *   should(3 > 5, '3 > 5').beFalse();
    477     *
    478     * @result
    479     *   "PASS   3 > 5 is false."
    480     */
    481    beFalse() {
    482      return this._assert(
    483          this._actual === false, '${actual} is false.',
    484          '${actual} is not false.');
    485    }
    486 
    487    /**
    488     * Check if |actual| is strictly equal to |expected|. (no type coercion)
    489     *
    490     * @example
    491     *   should(1).beEqualTo(1);
    492     *
    493     * @result
    494     *   "PASS   1 is equal to 1."
    495     */
    496    beEqualTo() {
    497      this._processArguments(arguments);
    498      return this._assert(
    499          this._actual === this._expected, '${actual} is equal to ${expected}.',
    500          '${actual} is not equal to ${expected}.');
    501    }
    502 
    503    /**
    504     * Check if |actual| is not equal to |expected|.
    505     *
    506     * @example
    507     *   should(1).notBeEqualTo(2);
    508     *
    509     * @result
    510     *   "PASS   1 is not equal to 2."
    511     */
    512    notBeEqualTo() {
    513      this._processArguments(arguments);
    514      return this._assert(
    515          this._actual !== this._expected,
    516          '${actual} is not equal to ${expected}.',
    517          '${actual} should not be equal to ${expected}.');
    518    }
    519 
    520    /**
    521     * check if |actual| is NaN
    522     *
    523     * @example
    524     *   should(NaN).beNaN();
    525     *
    526     * @result
    527     *   "PASS   NaN is NaN"
    528     *
    529     */
    530    beNaN() {
    531      this._processArguments(arguments);
    532      return this._assert(
    533          isNaN(this._actual),
    534          '${actual} is NaN.',
    535          '${actual} is not NaN but should be.');
    536    }
    537 
    538    /**
    539     * check if |actual| is NOT NaN
    540     *
    541     * @example
    542     *   should(42).notBeNaN();
    543     *
    544     * @result
    545     *   "PASS   42 is not NaN"
    546     *
    547     */
    548    notBeNaN() {
    549      this._processArguments(arguments);
    550      return this._assert(
    551          !isNaN(this._actual),
    552          '${actual} is not NaN.',
    553          '${actual} is NaN but should not be.');
    554    }
    555 
    556    /**
    557     * Check if |actual| is greater than |expected|.
    558     *
    559     * @example
    560     *   should(2).beGreaterThanOrEqualTo(2);
    561     *
    562     * @result
    563     *   "PASS   2 is greater than or equal to 2."
    564     */
    565    beGreaterThan() {
    566      this._processArguments(arguments);
    567      return this._assert(
    568          this._actual > this._expected,
    569          '${actual} is greater than ${expected}.',
    570          '${actual} is not greater than ${expected}.');
    571    }
    572 
    573    /**
    574     * Check if |actual| is greater than or equal to |expected|.
    575     *
    576     * @example
    577     *   should(2).beGreaterThan(1);
    578     *
    579     * @result
    580     *   "PASS   2 is greater than 1."
    581     */
    582    beGreaterThanOrEqualTo() {
    583      this._processArguments(arguments);
    584      return this._assert(
    585          this._actual >= this._expected,
    586          '${actual} is greater than or equal to ${expected}.',
    587          '${actual} is not greater than or equal to ${expected}.');
    588    }
    589 
    590    /**
    591     * Check if |actual| is less than |expected|.
    592     *
    593     * @example
    594     *   should(1).beLessThan(2);
    595     *
    596     * @result
    597     *   "PASS   1 is less than 2."
    598     */
    599    beLessThan() {
    600      this._processArguments(arguments);
    601      return this._assert(
    602          this._actual < this._expected, '${actual} is less than ${expected}.',
    603          '${actual} is not less than ${expected}.');
    604    }
    605 
    606    /**
    607     * Check if |actual| is less than or equal to |expected|.
    608     *
    609     * @example
    610     *   should(1).beLessThanOrEqualTo(1);
    611     *
    612     * @result
    613     *   "PASS   1 is less than or equal to 1."
    614     */
    615    beLessThanOrEqualTo() {
    616      this._processArguments(arguments);
    617      return this._assert(
    618          this._actual <= this._expected,
    619          '${actual} is less than or equal to ${expected}.',
    620          '${actual} is not less than or equal to ${expected}.');
    621    }
    622 
    623    /**
    624     * Check if |actual| array is filled with a constant |expected| value.
    625     *
    626     * @example
    627     *   should([1, 1, 1]).beConstantValueOf(1);
    628     *
    629     * @result
    630     *   "PASS   [1,1,1] contains only the constant 1."
    631     */
    632    beConstantValueOf() {
    633      this._processArguments(arguments);
    634      this._printActualForFailure = false;
    635 
    636      let passed = true;
    637      let passDetail, failDetail;
    638      let errors = {};
    639 
    640      let actual = this._actual;
    641      let expected = this._expected;
    642      for (let index = 0; index < actual.length; ++index) {
    643        if (actual[index] !== expected)
    644          errors[index] = actual[index];
    645      }
    646 
    647      let numberOfErrors = Object.keys(errors).length;
    648      passed = numberOfErrors === 0;
    649 
    650      if (passed) {
    651        passDetail = '${actual} contains only the constant ${expected}.';
    652      } else {
    653        let counter = 0;
    654        failDetail =
    655            '${actual}: Expected ${expected} for all values but found ' +
    656            numberOfErrors + ' unexpected values: ';
    657        failDetail += '\n\tIndex\tActual';
    658        for (let errorIndex in errors) {
    659          failDetail += '\n\t[' + errorIndex + ']' +
    660              '\t' + errors[errorIndex];
    661          if (++counter >= this._options.numberOfErrors) {
    662            failDetail +=
    663                '\n\t...and ' + (numberOfErrors - counter) + ' more errors.';
    664            break;
    665          }
    666        }
    667      }
    668 
    669      return this._assert(passed, passDetail, failDetail);
    670    }
    671 
    672    /**
    673     * Check if |actual| array is not filled with a constant |expected| value.
    674     *
    675     * @example
    676     *   should([1, 0, 1]).notBeConstantValueOf(1);
    677     *   should([0, 0, 0]).notBeConstantValueOf(0);
    678     *
    679     * @result
    680     *   "PASS   [1,0,1] is not constantly 1 (contains 1 different value)."
    681     *   "FAIL X [0,0,0] should have contain at least one value different
    682     *     from 0."
    683     */
    684    notBeConstantValueOf() {
    685      this._processArguments(arguments);
    686      this._printActualForFailure = false;
    687 
    688      let passed = true;
    689      let passDetail;
    690      let failDetail;
    691      let differences = {};
    692 
    693      let actual = this._actual;
    694      let expected = this._expected;
    695      for (let index = 0; index < actual.length; ++index) {
    696        if (actual[index] !== expected)
    697          differences[index] = actual[index];
    698      }
    699 
    700      let numberOfDifferences = Object.keys(differences).length;
    701      passed = numberOfDifferences > 0;
    702 
    703      if (passed) {
    704        let valueString = numberOfDifferences > 1 ? 'values' : 'value';
    705        passDetail = '${actual} is not constantly ${expected} (contains ' +
    706            numberOfDifferences + ' different ' + valueString + ').';
    707      } else {
    708        failDetail = '${actual} should have contain at least one value ' +
    709            'different from ${expected}.';
    710      }
    711 
    712      return this._assert(passed, passDetail, failDetail);
    713    }
    714 
    715    /**
    716     * Check if |actual| array is identical to |expected| array element-wise.
    717     *
    718     * @example
    719     *   should([1, 2, 3]).beEqualToArray([1, 2, 3]);
    720     *
    721     * @result
    722     *   "[1,2,3] is identical to the array [1,2,3]."
    723     */
    724    beEqualToArray() {
    725      this._processArguments(arguments);
    726      this._printActualForFailure = false;
    727 
    728      let passed = true;
    729      let passDetail, failDetail;
    730      let errorIndices = [];
    731 
    732      if (this._actual.length !== this._expected.length) {
    733        passed = false;
    734        failDetail = 'The array length does not match.';
    735        return this._assert(passed, passDetail, failDetail);
    736      }
    737 
    738      let actual = this._actual;
    739      let expected = this._expected;
    740      for (let index = 0; index < actual.length; ++index) {
    741        if (actual[index] !== expected[index])
    742          errorIndices.push(index);
    743      }
    744 
    745      passed = errorIndices.length === 0;
    746 
    747      if (passed) {
    748        passDetail = '${actual} is identical to the array ${expected}.';
    749      } else {
    750        let counter = 0;
    751        failDetail =
    752            '${actual} expected to be equal to the array ${expected} ' +
    753            'but differs in ' + errorIndices.length + ' places:' +
    754            '\n\tIndex\tActual\t\t\tExpected';
    755        for (let index of errorIndices) {
    756          failDetail += '\n\t[' + index + ']' +
    757              '\t' + this._actual[index].toExponential(16) + '\t' +
    758              this._expected[index].toExponential(16);
    759          if (++counter >= this._options.numberOfErrors) {
    760            failDetail += '\n\t...and ' + (errorIndices.length - counter) +
    761                ' more errors.';
    762            break;
    763          }
    764        }
    765      }
    766 
    767      return this._assert(passed, passDetail, failDetail);
    768    }
    769 
    770    /**
    771     * Check if |actual| array contains only the values in |expected| in the
    772     * order of values in |expected|.
    773     *
    774     * @example
    775     *   Should([1, 1, 3, 3, 2], 'My random array').containValues([1, 3, 2]);
    776     *
    777     * @result
    778     *   "PASS   [1,1,3,3,2] contains all the expected values in the correct
    779     *           order: [1,3,2].
    780     */
    781    containValues() {
    782      this._processArguments(arguments);
    783      this._printActualForFailure = false;
    784 
    785      let passed = true;
    786      let indexedActual = [];
    787      let firstErrorIndex = null;
    788 
    789      // Collect the unique value sequence from the actual.
    790      for (let i = 0, prev = null; i < this._actual.length; i++) {
    791        if (this._actual[i] !== prev) {
    792          indexedActual.push({index: i, value: this._actual[i]});
    793          prev = this._actual[i];
    794        }
    795      }
    796 
    797      // Compare against the expected sequence.
    798      let failMessage =
    799          '${actual} expected to have the value sequence of ${expected} but ' +
    800          'got ';
    801      if (this._expected.length === indexedActual.length) {
    802        for (let j = 0; j < this._expected.length; j++) {
    803          if (this._expected[j] !== indexedActual[j].value) {
    804            firstErrorIndex = indexedActual[j].index;
    805            passed = false;
    806            failMessage += this._actual[firstErrorIndex] + ' at index ' +
    807                firstErrorIndex + '.';
    808            break;
    809          }
    810        }
    811      } else {
    812        passed = false;
    813        let indexedValues = indexedActual.map(x => x.value);
    814        failMessage += `${indexedActual.length} values, [${
    815            indexedValues}], instead of ${this._expected.length}.`;
    816      }
    817 
    818      return this._assert(
    819          passed,
    820          '${actual} contains all the expected values in the correct order: ' +
    821              '${expected}.',
    822          failMessage);
    823    }
    824 
    825    /**
    826     * Check if |actual| array does not have any glitches. Note that |threshold|
    827     * is not optional and is to define the desired threshold value.
    828     *
    829     * @example
    830     *   should([0.5, 0.5, 0.55, 0.5, 0.45, 0.5]).notGlitch(0.06);
    831     *
    832     * @result
    833     *   "PASS   [0.5,0.5,0.55,0.5,0.45,0.5] has no glitch above the threshold
    834     *           of 0.06."
    835     *
    836     */
    837    notGlitch() {
    838      this._processArguments(arguments);
    839      this._printActualForFailure = false;
    840 
    841      let passed = true;
    842      let passDetail, failDetail;
    843 
    844      let actual = this._actual;
    845      let expected = this._expected;
    846      for (let index = 0; index < actual.length; ++index) {
    847        let diff = Math.abs(actual[index - 1] - actual[index]);
    848        if (diff >= expected) {
    849          passed = false;
    850          failDetail = '${actual} has a glitch at index ' + index +
    851              ' of size ' + diff + '.';
    852        }
    853      }
    854 
    855      passDetail =
    856          '${actual} has no glitch above the threshold of ${expected}.';
    857 
    858      return this._assert(passed, passDetail, failDetail);
    859    }
    860 
    861    /**
    862     * Check if |actual| is close to |expected| using the given relative error
    863     * |threshold|.
    864     *
    865     * @example
    866     *   should(2.3).beCloseTo(2, { threshold: 0.3 });
    867     *
    868     * @result
    869     *   "PASS    2.3 is 2 within an error of 0.3."
    870     * @param {Object} options              Options for assertion.
    871     * @param {Number} options.threshold    Threshold value for the comparison.
    872     */
    873    beCloseTo() {
    874      this._processArguments(arguments);
    875 
    876      // The threshold is relative except when |expected| is zero, in which case
    877      // it is absolute.
    878      let absExpected = this._expected ? Math.abs(this._expected) : 1;
    879      let error = Math.abs(this._actual - this._expected) / absExpected;
    880 
    881      return this._assert(
    882          error <= this._options.threshold,
    883          '${actual} is ${expected} within an error of ${threshold}.',
    884          '${actual} is not close to ${expected} within a relative error of ' +
    885              '${threshold} (RelErr=' + error + ').');
    886    }
    887 
    888    /**
    889     * Check if |target| array is close to |expected| array element-wise within
    890     * a certain error bound given by the |options|.
    891     *
    892     * The error criterion is:
    893     *   abs(actual[k] - expected[k]) < max(absErr, relErr * abs(expected))
    894     *
    895     * If nothing is given for |options|, then absErr = relErr = 0. If
    896     * absErr = 0, then the error criterion is a relative error. A non-zero
    897     * absErr value produces a mix intended to handle the case where the
    898     * expected value is 0, allowing the target value to differ by absErr from
    899     * the expected.
    900     *
    901     * @param {Number} options.absoluteThreshold    Absolute threshold.
    902     * @param {Number} options.relativeThreshold    Relative threshold.
    903     */
    904    beCloseToArray() {
    905      this._processArguments(arguments);
    906      this._printActualForFailure = false;
    907 
    908      let passed = true;
    909      let passDetail, failDetail;
    910 
    911      // Parsing options.
    912      let absErrorThreshold = (this._options.absoluteThreshold || 0);
    913      let relErrorThreshold = (this._options.relativeThreshold || 0);
    914 
    915      // A collection of all of the values that satisfy the error criterion.
    916      // This holds the absolute difference between the target element and the
    917      // expected element.
    918      let errors = {};
    919 
    920      // Keep track of the max absolute error found.
    921      let maxAbsError = -Infinity, maxAbsErrorIndex = -1;
    922 
    923      // Keep track of the max relative error found, ignoring cases where the
    924      // relative error is Infinity because the expected value is 0.
    925      let maxRelError = -Infinity, maxRelErrorIndex = -1;
    926 
    927      let actual = this._actual;
    928      let expected = this._expected;
    929 
    930      for (let index = 0; index < expected.length; ++index) {
    931        let diff = Math.abs(actual[index] - expected[index]);
    932        let absExpected = Math.abs(expected[index]);
    933        let relError = diff / absExpected;
    934 
    935        if (diff >
    936            Math.max(absErrorThreshold, relErrorThreshold * absExpected)) {
    937          if (diff > maxAbsError) {
    938            maxAbsErrorIndex = index;
    939            maxAbsError = diff;
    940          }
    941 
    942          if (!isNaN(relError) && relError > maxRelError) {
    943            maxRelErrorIndex = index;
    944            maxRelError = relError;
    945          }
    946 
    947          errors[index] = diff;
    948        }
    949      }
    950 
    951      let numberOfErrors = Object.keys(errors).length;
    952      let maxAllowedErrorDetail = JSON.stringify({
    953        absoluteThreshold: absErrorThreshold,
    954        relativeThreshold: relErrorThreshold
    955      });
    956 
    957      if (numberOfErrors === 0) {
    958        // The assertion was successful.
    959        passDetail = '${actual} equals ${expected} with an element-wise ' +
    960            'tolerance of ' + maxAllowedErrorDetail + '.';
    961      } else {
    962        // Failed. Prepare the detailed failure log.
    963        passed = false;
    964        failDetail = '${actual} does not equal ${expected} with an ' +
    965            'element-wise tolerance of ' + maxAllowedErrorDetail + '.\n';
    966 
    967        // Print out actual, expected, absolute error, and relative error.
    968        let counter = 0;
    969        failDetail += '\tIndex\tActual\t\t\tExpected\t\tAbsError' +
    970            '\t\tRelError\t\tTest threshold';
    971        let printedIndices = [];
    972        for (let index in errors) {
    973          failDetail +=
    974              '\n' +
    975              _formatFailureEntry(
    976                  index, actual[index], expected[index], errors[index],
    977                  _closeToThreshold(
    978                      absErrorThreshold, relErrorThreshold, expected[index]));
    979 
    980          printedIndices.push(index);
    981          if (++counter > this._options.numberOfErrors) {
    982            failDetail +=
    983                '\n\t...and ' + (numberOfErrors - counter) + ' more errors.';
    984            break;
    985          }
    986        }
    987 
    988        // Finalize the error log: print out the location of both the maxAbs
    989        // error and the maxRel error so we can adjust thresholds appropriately
    990        // in the test.
    991        failDetail += '\n' +
    992            '\tMax AbsError of ' + maxAbsError.toExponential(16) +
    993            ' at index of ' + maxAbsErrorIndex + '.\n';
    994        if (printedIndices.find(element => {
    995              return element == maxAbsErrorIndex;
    996            }) === undefined) {
    997          // Print an entry for this index if we haven't already.
    998          failDetail +=
    999              _formatFailureEntry(
   1000                  maxAbsErrorIndex, actual[maxAbsErrorIndex],
   1001                  expected[maxAbsErrorIndex], errors[maxAbsErrorIndex],
   1002                  _closeToThreshold(
   1003                      absErrorThreshold, relErrorThreshold,
   1004                      expected[maxAbsErrorIndex])) +
   1005              '\n';
   1006        }
   1007        failDetail += '\tMax RelError of ' + maxRelError.toExponential(16) +
   1008            ' at index of ' + maxRelErrorIndex + '.\n';
   1009        if (printedIndices.find(element => {
   1010              return element == maxRelErrorIndex;
   1011            }) === undefined) {
   1012          // Print an entry for this index if we haven't already.
   1013          failDetail +=
   1014              _formatFailureEntry(
   1015                  maxRelErrorIndex, actual[maxRelErrorIndex],
   1016                  expected[maxRelErrorIndex], errors[maxRelErrorIndex],
   1017                  _closeToThreshold(
   1018                      absErrorThreshold, relErrorThreshold,
   1019                      expected[maxRelErrorIndex])) +
   1020              '\n';
   1021        }
   1022      }
   1023 
   1024      return this._assert(passed, passDetail, failDetail);
   1025    }
   1026 
   1027    /**
   1028     * A temporary escape hat for printing an in-task message. The description
   1029     * for the |actual| is required to get the message printed properly.
   1030     *
   1031     * TODO(hongchan): remove this method when the transition from the old Audit
   1032     * to the new Audit is completed.
   1033     * @example
   1034     *   should(true, 'The message is').message('truthful!', 'false!');
   1035     *
   1036     * @result
   1037     *   "PASS   The message is truthful!"
   1038     */
   1039    message(passDetail, failDetail) {
   1040      return this._assert(
   1041          this._actual, '${actual} ' + passDetail, '${actual} ' + failDetail);
   1042    }
   1043 
   1044    /**
   1045     * Check if |expected| property is truly owned by |actual| object.
   1046     *
   1047     * @example
   1048     *   should(BaseAudioContext.prototype,
   1049     *          'BaseAudioContext.prototype').haveOwnProperty('createGain');
   1050     *
   1051     * @result
   1052     *   "PASS   BaseAudioContext.prototype has an own property of
   1053     *       'createGain'."
   1054     */
   1055    haveOwnProperty() {
   1056      this._processArguments(arguments);
   1057 
   1058      return this._assert(
   1059          this._actual.hasOwnProperty(this._expected),
   1060          '${actual} has an own property of "${expected}".',
   1061          '${actual} does not own the property of "${expected}".');
   1062    }
   1063 
   1064 
   1065    /**
   1066     * Check if |expected| property is not owned by |actual| object.
   1067     *
   1068     * @example
   1069     *   should(BaseAudioContext.prototype,
   1070     *          'BaseAudioContext.prototype')
   1071     *       .notHaveOwnProperty('startRendering');
   1072     *
   1073     * @result
   1074     *   "PASS   BaseAudioContext.prototype does not have an own property of
   1075     *       'startRendering'."
   1076     */
   1077    notHaveOwnProperty() {
   1078      this._processArguments(arguments);
   1079 
   1080      return this._assert(
   1081          !this._actual.hasOwnProperty(this._expected),
   1082          '${actual} does not have an own property of "${expected}".',
   1083          '${actual} has an own the property of "${expected}".')
   1084    }
   1085 
   1086 
   1087    /**
   1088     * Check if an object is inherited from a class. This looks up the entire
   1089     * prototype chain of a given object and tries to find a match.
   1090     *
   1091     * @example
   1092     *   should(sourceNode, 'A buffer source node')
   1093     *       .inheritFrom('AudioScheduledSourceNode');
   1094     *
   1095     * @result
   1096     *   "PASS   A buffer source node inherits from 'AudioScheduledSourceNode'."
   1097     */
   1098    inheritFrom() {
   1099      this._processArguments(arguments);
   1100 
   1101      let prototypes = [];
   1102      let currentPrototype = Object.getPrototypeOf(this._actual);
   1103      while (currentPrototype) {
   1104        prototypes.push(currentPrototype.constructor.name);
   1105        currentPrototype = Object.getPrototypeOf(currentPrototype);
   1106      }
   1107 
   1108      return this._assert(
   1109          prototypes.includes(this._expected),
   1110          '${actual} inherits from "${expected}".',
   1111          '${actual} does not inherit from "${expected}".');
   1112    }
   1113  }
   1114 
   1115 
   1116  // Task Class state enum.
   1117  const TaskState = {PENDING: 0, STARTED: 1, FINISHED: 2};
   1118 
   1119 
   1120  /**
   1121   * @class Task
   1122   * @description WebAudio testing task. Managed by TaskRunner.
   1123   */
   1124  class Task {
   1125    /**
   1126     * Task constructor.
   1127     * @param  {Object} taskRunner Reference of associated task runner.
   1128     * @param  {String||Object} taskLabel Task label if a string is given. This
   1129     *                                    parameter can be a dictionary with the
   1130     *                                    following fields.
   1131     * @param  {String} taskLabel.label Task label.
   1132     * @param  {String} taskLabel.description Description of task.
   1133     * @param  {Function} taskFunction Task function to be performed.
   1134     * @return {Object} Task object.
   1135     */
   1136    constructor(taskRunner, taskLabel, taskFunction) {
   1137      this._taskRunner = taskRunner;
   1138      this._taskFunction = taskFunction;
   1139 
   1140      if (typeof taskLabel === 'string') {
   1141        this._label = taskLabel;
   1142        this._description = null;
   1143      } else if (typeof taskLabel === 'object') {
   1144        if (typeof taskLabel.label !== 'string') {
   1145          _throwException('Task.constructor:: task label must be string.');
   1146        }
   1147        this._label = taskLabel.label;
   1148        this._description = (typeof taskLabel.description === 'string') ?
   1149            taskLabel.description :
   1150            null;
   1151      } else {
   1152        _throwException(
   1153            'Task.constructor:: task label must be a string or ' +
   1154            'a dictionary.');
   1155      }
   1156 
   1157      this._state = TaskState.PENDING;
   1158      this._result = true;
   1159 
   1160      this._totalAssertions = 0;
   1161      this._failedAssertions = 0;
   1162    }
   1163 
   1164    get label() {
   1165      return this._label;
   1166    }
   1167 
   1168    get state() {
   1169      return this._state;
   1170    }
   1171 
   1172    get result() {
   1173      return this._result;
   1174    }
   1175 
   1176    // Start the assertion chain.
   1177    should(actual, actualDescription) {
   1178      // If no argument is given, we cannot proceed. Halt.
   1179      if (arguments.length === 0)
   1180        _throwException('Task.should:: requires at least 1 argument.');
   1181 
   1182      return new Should(this, actual, actualDescription);
   1183    }
   1184 
   1185    // Run this task. |this| task will be passed into the user-supplied test
   1186    // task function.
   1187    run(harnessTest) {
   1188      this._state = TaskState.STARTED;
   1189      this._harnessTest = harnessTest;
   1190      // Print out the task entry with label and description.
   1191      _logPassed(
   1192          '> [' + this._label + '] ' +
   1193          (this._description ? this._description : ''));
   1194 
   1195      return new Promise((resolve, reject) => {
   1196        this._resolve = resolve;
   1197        this._reject = reject;
   1198        let result = this._taskFunction(this, this.should.bind(this));
   1199        if (result && typeof result.then === "function") {
   1200          result.then(() => this.done()).catch(reject);
   1201        }
   1202      });
   1203    }
   1204 
   1205    // Update the task success based on the individual assertion/test inside.
   1206    update(subTask) {
   1207      // After one of tests fails within a task, the result is irreversible.
   1208      if (subTask.result === false) {
   1209        this._result = false;
   1210        this._failedAssertions++;
   1211      }
   1212 
   1213      this._totalAssertions++;
   1214    }
   1215 
   1216    // Finish the current task and start the next one if available.
   1217    done() {
   1218      assert_equals(this._state, TaskState.STARTED)
   1219      this._state = TaskState.FINISHED;
   1220 
   1221      let message = '< [' + this._label + '] ';
   1222 
   1223      if (this._result) {
   1224        message += 'All assertions passed. (total ' + this._totalAssertions +
   1225            ' assertions)';
   1226        _logPassed(message);
   1227      } else {
   1228        message += this._failedAssertions + ' out of ' + this._totalAssertions +
   1229            ' assertions were failed.'
   1230        _logFailed(message);
   1231      }
   1232 
   1233      this._resolve();
   1234    }
   1235 
   1236    // Runs |subTask| |time| milliseconds later. |setTimeout| is not allowed in
   1237    // WPT linter, so a thin wrapper around the harness's |step_timeout| is
   1238    // used here.  Returns a Promise which is resolved after |subTask| runs.
   1239    timeout(subTask, time) {
   1240      return new Promise(resolve => {
   1241        this._harnessTest.step_timeout(() => {
   1242          let result = subTask();
   1243          if (result && typeof result.then === "function") {
   1244            // Chain rejection directly to the harness test Promise, to report
   1245            // the rejection against the subtest even when the caller of
   1246            // timeout does not handle the rejection.
   1247            result.then(resolve, this._reject());
   1248          } else {
   1249            resolve();
   1250          }
   1251        }, time);
   1252      });
   1253    }
   1254 
   1255    isPassed() {
   1256      return this._state === TaskState.FINISHED && this._result;
   1257    }
   1258 
   1259    toString() {
   1260      return '"' + this._label + '": ' + this._description;
   1261    }
   1262  }
   1263 
   1264 
   1265  /**
   1266   * @class TaskRunner
   1267   * @description WebAudio testing task runner. Manages tasks.
   1268   */
   1269  class TaskRunner {
   1270    constructor() {
   1271      this._tasks = {};
   1272      this._taskSequence = [];
   1273 
   1274      // Configure testharness.js for the async operation.
   1275      setup(new Function(), {explicit_done: true});
   1276    }
   1277 
   1278    _finish() {
   1279      let numberOfFailures = 0;
   1280      for (let taskIndex in this._taskSequence) {
   1281        let task = this._tasks[this._taskSequence[taskIndex]];
   1282        numberOfFailures += task.result ? 0 : 1;
   1283      }
   1284 
   1285      let prefix = '# AUDIT TASK RUNNER FINISHED: ';
   1286      if (numberOfFailures > 0) {
   1287        _logFailed(
   1288            prefix + numberOfFailures + ' out of ' + this._taskSequence.length +
   1289            ' tasks were failed.');
   1290      } else {
   1291        _logPassed(
   1292            prefix + this._taskSequence.length + ' tasks ran successfully.');
   1293      }
   1294 
   1295      return Promise.resolve();
   1296    }
   1297 
   1298    // |taskLabel| can be either a string or a dictionary. See Task constructor
   1299    // for the detail.  If |taskFunction| returns a thenable, then the task
   1300    // is considered complete when the thenable is fulfilled; otherwise the
   1301    // task must be completed with an explicit call to |task.done()|.
   1302    define(taskLabel, taskFunction) {
   1303      let task = new Task(this, taskLabel, taskFunction);
   1304      if (this._tasks.hasOwnProperty(task.label)) {
   1305        _throwException('Audit.define:: Duplicate task definition.');
   1306        return;
   1307      }
   1308      this._tasks[task.label] = task;
   1309      this._taskSequence.push(task.label);
   1310    }
   1311 
   1312    // Start running all the tasks scheduled. Multiple task names can be passed
   1313    // to execute them sequentially. Zero argument will perform all defined
   1314    // tasks in the order of definition.
   1315    run() {
   1316      // Display the beginning of the test suite.
   1317      _logPassed('# AUDIT TASK RUNNER STARTED.');
   1318 
   1319      // If the argument is specified, override the default task sequence with
   1320      // the specified one.
   1321      if (arguments.length > 0) {
   1322        this._taskSequence = [];
   1323        for (let i = 0; i < arguments.length; i++) {
   1324          let taskLabel = arguments[i];
   1325          if (!this._tasks.hasOwnProperty(taskLabel)) {
   1326            _throwException('Audit.run:: undefined task.');
   1327          } else if (this._taskSequence.includes(taskLabel)) {
   1328            _throwException('Audit.run:: duplicate task request.');
   1329          } else {
   1330            this._taskSequence.push(taskLabel);
   1331          }
   1332        }
   1333      }
   1334 
   1335      if (this._taskSequence.length === 0) {
   1336        _throwException('Audit.run:: no task to run.');
   1337        return;
   1338      }
   1339 
   1340      for (let taskIndex in this._taskSequence) {
   1341        let task = this._tasks[this._taskSequence[taskIndex]];
   1342        // Some tests assume that tasks run in sequence, which is provided by
   1343        // promise_test().
   1344        promise_test((t) => task.run(t), `Executing "${task.label}"`);
   1345      }
   1346 
   1347      // Schedule a summary report on completion.
   1348      promise_test(() => this._finish(), "Audit report");
   1349 
   1350      // From testharness.js. The harness now need not wait for more subtests
   1351      // to be added.
   1352      _testharnessDone();
   1353    }
   1354  }
   1355 
   1356  /**
   1357   * Load file from a given URL and pass ArrayBuffer to the following promise.
   1358   * @param  {String} fileUrl file URL.
   1359   * @return {Promise}
   1360   *
   1361   * @example
   1362   *   Audit.loadFileFromUrl('resources/my-sound.ogg').then((response) => {
   1363   *       audioContext.decodeAudioData(response).then((audioBuffer) => {
   1364   *           // Do something with AudioBuffer.
   1365   *       });
   1366   *   });
   1367   */
   1368  function loadFileFromUrl(fileUrl) {
   1369    return new Promise((resolve, reject) => {
   1370      let xhr = new XMLHttpRequest();
   1371      xhr.open('GET', fileUrl, true);
   1372      xhr.responseType = 'arraybuffer';
   1373 
   1374      xhr.onload = () => {
   1375        // |status = 0| is a workaround for the run_web_test.py server. We are
   1376        // speculating the server quits the transaction prematurely without
   1377        // completing the request.
   1378        if (xhr.status === 200 || xhr.status === 0) {
   1379          resolve(xhr.response);
   1380        } else {
   1381          let errorMessage = 'loadFile: Request failed when loading ' +
   1382              fileUrl + '. ' + xhr.statusText + '. (status = ' + xhr.status +
   1383              ')';
   1384          if (reject) {
   1385            reject(errorMessage);
   1386          } else {
   1387            new Error(errorMessage);
   1388          }
   1389        }
   1390      };
   1391 
   1392      xhr.onerror = (event) => {
   1393        let errorMessage =
   1394            'loadFile: Network failure when loading ' + fileUrl + '.';
   1395        if (reject) {
   1396          reject(errorMessage);
   1397        } else {
   1398          new Error(errorMessage);
   1399        }
   1400      };
   1401 
   1402      xhr.send();
   1403    });
   1404  }
   1405 
   1406  /**
   1407   * @class Audit
   1408   * @description A WebAudio layout test task manager.
   1409   * @example
   1410   *   let audit = Audit.createTaskRunner();
   1411   *   audit.define('first-task', function (task, should) {
   1412   *     should(someValue).beEqualTo(someValue);
   1413   *     task.done();
   1414   *   });
   1415   *   audit.run();
   1416   */
   1417  return {
   1418 
   1419    /**
   1420     * Creates an instance of Audit task runner.
   1421     * @param {Object}  options                     Options for task runner.
   1422     * @param {Boolean} options.requireResultFile   True if the test suite
   1423     *                                              requires explicit text
   1424     *                                              comparison with the expected
   1425     *                                              result file.
   1426     */
   1427    createTaskRunner: function(options) {
   1428      if (options && options.requireResultFile == true) {
   1429        _logError(
   1430            'this test requires the explicit comparison with the ' +
   1431            'expected result when it runs with run_web_tests.py.');
   1432      }
   1433 
   1434      return new TaskRunner();
   1435    },
   1436 
   1437    /**
   1438     * Load file from a given URL and pass ArrayBuffer to the following promise.
   1439     * See |loadFileFromUrl| method for the detail.
   1440     */
   1441    loadFileFromUrl: loadFileFromUrl
   1442 
   1443  };
   1444 
   1445 })();