tor-browser

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

helpers.js (9008B)


      1 /*
      2  Returns an array (typed or not), of the passed array with removed trailing and ending
      3  zero-valued elements
      4 */
      5 function trimEmptyElements(array) {
      6  var start = 0;
      7  var end = array.length;
      8 
      9  while (start < array.length) {
     10    if (array[start] !== 0) {
     11      break;
     12    }
     13    start++;
     14  }
     15 
     16  while (end > 0) {
     17    end--;
     18    if (array[end] !== 0) {
     19      break;
     20    }
     21  }
     22  return array.subarray(start, end);
     23 }
     24 
     25 
     26 function fuzzyCompare(a, b) {
     27  return Math.abs(a - b) < 9e-3;
     28 }
     29 
     30 function compareChannels(buf1, buf2,
     31                        /*optional*/ length,
     32                        /*optional*/ sourceOffset,
     33                        /*optional*/ destOffset,
     34                        /*optional*/ skipLengthCheck) {
     35  if (!skipLengthCheck) {
     36    assert_equals(buf1.length, buf2.length, "Channels must have the same length");
     37  }
     38  sourceOffset = sourceOffset || 0;
     39  destOffset = destOffset || 0;
     40  if (length == undefined) {
     41    length = buf1.length - sourceOffset;
     42  }
     43  var difference = 0;
     44  var maxDifference = 0;
     45  var firstBadIndex = -1;
     46  for (var i = 0; i < length; ++i) {
     47    if (!fuzzyCompare(buf1[i + sourceOffset], buf2[i + destOffset])) {
     48      difference++;
     49      maxDifference = Math.max(maxDifference, Math.abs(buf1[i + sourceOffset] - buf2[i + destOffset]));
     50      if (firstBadIndex == -1) {
     51        firstBadIndex = i;
     52      }
     53    }
     54  };
     55 
     56  assert_equals(difference, 0, "maxDifference: " + maxDifference +
     57     ", first bad index: " + firstBadIndex + " with test-data offset " +
     58     sourceOffset + " and expected-data offset " + destOffset +
     59     "; corresponding values " + buf1[firstBadIndex + sourceOffset] + " and " +
     60     buf2[firstBadIndex + destOffset] + " --- differences");
     61 }
     62 
     63 function compareBuffers(got, expected) {
     64  if (got.numberOfChannels != expected.numberOfChannels) {
     65    assert_equals(got.numberOfChannels, expected.numberOfChannels,
     66                  "Correct number of buffer channels");
     67    return;
     68  }
     69  if (got.length != expected.length) {
     70    assert_equals(got.length, expected.length,
     71                  "Correct buffer length");
     72    return;
     73  }
     74  if (got.sampleRate != expected.sampleRate) {
     75    assert_equals(got.sampleRate, expected.sampleRate,
     76                  "Correct sample rate");
     77    return;
     78  }
     79 
     80  for (var i = 0; i < got.numberOfChannels; ++i) {
     81    compareChannels(got.getChannelData(i), expected.getChannelData(i),
     82                    got.length, 0, 0, true);
     83  }
     84 }
     85 
     86 /**
     87 * This function assumes that the test is a "single page test" [0], and defines a
     88 * single gTest variable with the following properties and methods:
     89 *
     90 * + numberOfChannels: optional property which specifies the number of channels
     91 *                     in the output.  The default value is 2.
     92 * + createGraph: mandatory method which takes a context object and does
     93 *                everything needed in order to set up the Web Audio graph.
     94 *                This function returns the node to be inspected.
     95 * + createGraphAsync: async version of createGraph.  This function takes
     96 *                     a callback which should be called with an argument
     97 *                     set to the node to be inspected when the callee is
     98 *                     ready to proceed with the test.  Either this function
     99 *                     or createGraph must be provided.
    100 * + createExpectedBuffers: optional method which takes a context object and
    101 *                          returns either one expected buffer or an array of
    102 *                          them, designating what is expected to be observed
    103 *                          in the output.  If omitted, the output is expected
    104 *                          to be silence.  All buffers must have the same
    105 *                          length, which must be a bufferSize supported by
    106 *                          ScriptProcessorNode.  This function is guaranteed
    107 *                          to be called before createGraph.
    108 * + length: property equal to the total number of frames which we are waiting
    109 *           to see in the output, mandatory if createExpectedBuffers is not
    110 *           provided, in which case it must be a bufferSize supported by
    111 *           ScriptProcessorNode (256, 512, 1024, 2048, 4096, 8192, or 16384).
    112 *           If createExpectedBuffers is provided then this must be equal to
    113 *           the number of expected buffers * the expected buffer length.
    114 *
    115 * + skipOfflineContextTests: optional. when true, skips running tests on an offline
    116 *                            context by circumventing testOnOfflineContext.
    117 *
    118 * [0]: https://web-platform-tests.org/writing-tests/testharness-api.html#single-page-tests
    119 */
    120 function runTest(name)
    121 {
    122  function runTestFunction () {
    123    if (!gTest.numberOfChannels) {
    124      gTest.numberOfChannels = 2; // default
    125    }
    126 
    127    var testLength;
    128 
    129    function runTestOnContext(context, callback, testOutput) {
    130      if (!gTest.createExpectedBuffers) {
    131        // Assume that the output is silence
    132        var expectedBuffers = getEmptyBuffer(context, gTest.length);
    133      } else {
    134        var expectedBuffers = gTest.createExpectedBuffers(context);
    135      }
    136      if (!(expectedBuffers instanceof Array)) {
    137        expectedBuffers = [expectedBuffers];
    138      }
    139      var expectedFrames = 0;
    140      for (var i = 0; i < expectedBuffers.length; ++i) {
    141        assert_equals(expectedBuffers[i].numberOfChannels, gTest.numberOfChannels,
    142                      "Correct number of channels for expected buffer " + i);
    143        expectedFrames += expectedBuffers[i].length;
    144      }
    145      if (gTest.length && gTest.createExpectedBuffers) {
    146        assert_equals(expectedFrames,
    147                      gTest.length, "Correct number of expected frames");
    148      }
    149 
    150      if (gTest.createGraphAsync) {
    151        gTest.createGraphAsync(context, function(nodeToInspect) {
    152          testOutput(nodeToInspect, expectedBuffers, callback);
    153        });
    154      } else {
    155        testOutput(gTest.createGraph(context), expectedBuffers, callback);
    156      }
    157    }
    158 
    159    function testOnNormalContext(callback) {
    160      function testOutput(nodeToInspect, expectedBuffers, callback) {
    161        testLength = 0;
    162        var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels, 1);
    163        nodeToInspect.connect(sp).connect(context.destination);
    164        sp.onaudioprocess = function(e) {
    165          var expectedBuffer = expectedBuffers.shift();
    166          testLength += expectedBuffer.length;
    167          compareBuffers(e.inputBuffer, expectedBuffer);
    168          if (expectedBuffers.length == 0) {
    169            sp.onaudioprocess = null;
    170            callback();
    171          }
    172        };
    173      }
    174      var context = new AudioContext();
    175      runTestOnContext(context, callback, testOutput);
    176    }
    177 
    178    function testOnOfflineContext(callback, sampleRate) {
    179      function testOutput(nodeToInspect, expectedBuffers, callback) {
    180        nodeToInspect.connect(context.destination);
    181        context.oncomplete = function(e) {
    182          var samplesSeen = 0;
    183          while (expectedBuffers.length) {
    184            var expectedBuffer = expectedBuffers.shift();
    185            assert_equals(e.renderedBuffer.numberOfChannels, expectedBuffer.numberOfChannels,
    186                          "Correct number of input buffer channels");
    187            for (var i = 0; i < e.renderedBuffer.numberOfChannels; ++i) {
    188              compareChannels(e.renderedBuffer.getChannelData(i),
    189                             expectedBuffer.getChannelData(i),
    190                             expectedBuffer.length,
    191                             samplesSeen,
    192                             undefined,
    193                             true);
    194            }
    195            samplesSeen += expectedBuffer.length;
    196          }
    197          callback();
    198        };
    199        context.startRendering();
    200      }
    201 
    202      var context = new OfflineAudioContext(gTest.numberOfChannels, testLength, sampleRate);
    203      runTestOnContext(context, callback, testOutput);
    204    }
    205 
    206    testOnNormalContext(function() {
    207      if (!gTest.skipOfflineContextTests) {
    208        testOnOfflineContext(function() {
    209          testOnOfflineContext(done, 44100);
    210        }, 48000);
    211      } else {
    212        done();
    213      }
    214    });
    215  };
    216 
    217  runTestFunction();
    218 }
    219 
    220 // Simpler than audit.js, but still logs the message. Requires
    221 // `setup("explicit_done": true)` if testing code that runs after the "load"
    222 // event.
    223 function equals(a, b, msg) {
    224  test(function() {
    225    assert_equals(a, b);
    226  }, msg);
    227 }
    228 function is_true(a, msg) {
    229  test(function() {
    230    assert_true(a);
    231  }, msg);
    232 }
    233 
    234 // This allows writing AudioWorkletProcessor code in the same file as the rest
    235 // of the test, for quick one off AudioWorkletProcessor testing.
    236 function URLFromScriptsElements(ids)
    237 {
    238  var scriptTexts = [];
    239  for (let id of ids) {
    240 
    241    const e = document.querySelector("script#"+id)
    242    if (!e) {
    243      throw id+" is not the id of a <script> tag";
    244    }
    245    scriptTexts.push(e.innerText);
    246  }
    247  const blob = new Blob(scriptTexts, {type: "application/javascript"});
    248 
    249  return URL.createObjectURL(blob);
    250 }