chromium/chrome/test/data/webrtc/peerconnection_getstats.js

/**
 * Copyright 2016 The Chromium Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */


// Public interface to tests.

/**
 * Gets the result of the promise-based |RTCPeerConnection.getStats| as a
 * dictionary of RTCStats-dictionaries.
 *
 * Returns "ok-" followed by a JSON-stringified dictionary of dictionaries to
 * the test.
 */
function getStatsReportDictionary() {
  return peerConnection_().getStats()
    .then(function(report) {
      if (report == null || report.size == 0)
        throw new Error('report is null or empty.');
      let reportDictionary = {};
      for (let stats of report.values()) {
        reportDictionary[stats.id] = stats;
      }
      return logAndReturn('ok-' + JSON.stringify(reportDictionary));
    },
    function(e) {
      throw new Error('Promise was rejected: ' + e);
    });
}

/**
 * Measures the performance of the promise-based |RTCPeerConnection.getStats|
 * and returns the time it took in milliseconds as a double
 * (DOMHighResTimeStamp, accurate to one thousandth of a millisecond).
 * Verifies that all stats types of the allowlist were contained in the result.
 *
 * Returns "ok-" followed by a double on success.
 */
function measureGetStatsPerformance() {
  let t0 = performance.now();
  return peerConnection_().getStats()
    .then(function(report) {
      let t1 = performance.now();
      return logAndReturn('ok-' + (t1 - t0));
    },
    function(e) {
      throw new Error('Promise was rejected: ' + e);
    });
}

// Internals.

/** Gets a set of all mandatory stats types. */
function mandatoryStatsTypes() {
  const mandatoryTypes = new Set();
  for (let allowlistedStats of gStatsAllowlist.values()) {
    if (allowlistedStats.presence() == Presence.MANDATORY)
      mandatoryTypes.add(allowlistedStats.type());
  }
  return mandatoryTypes;
}

/**
 * Checks if |stats| correctly maps to a a allowlisted RTCStats-derived
 * dictionary, throwing an error if it doesn't. See |gStatsAllowlist|.
 *
 * The "RTCStats.type" must map to a known dictionary description. Every member
 * is optional, but if present it must be present in the allowlisted dictionary
 * description and its type must match.
 * @private
 */
function verifyStatsIsAllowlisted_(stats) {
  if (stats == null)
    throw new Error('stats is null or undefined: ' + stats);
  if (typeof(stats.id) !== 'string')
    throw new Error('stats.id is not a string:' + stats.id);
  if (typeof(stats.timestamp) !== 'number' || !isFinite(stats.timestamp) ||
      stats.timestamp <= 0) {
    throw new Error('stats.timestamp is not a positive finite number: ' +
        stats.timestamp);
  }
  if (typeof(stats.type) !== 'string')
    throw new Error('stats.type is not a string: ' + stats.type);
  let allowlistedStats = gStatsAllowlist.get(stats.type);
  if (allowlistedStats == null)
    throw new Error('stats.type is not a allowlisted type: ' + stats.type);
  allowlistedStats = allowlistedStats.stats();
  for (let propertyName in stats) {
    if (propertyName === 'id' || propertyName === 'timestamp' ||
        propertyName === 'type') {
      continue;
    }
    if (!allowlistedStats.hasOwnProperty(propertyName)) {
      throw new Error(
          stats.type + '.' + propertyName + ' is not a allowlisted ' +
          'member: ' + stats[propertyName]);
    }
    if (allowlistedStats[propertyName] === 'any')
      continue;
    if (!allowlistedStats[propertyName].startsWith('sequence_')) {
      if (typeof(stats[propertyName]) !== allowlistedStats[propertyName]) {
        throw new Error('stats.' + propertyName + ' should have a different ' +
            'type according to the allowlist: ' + stats[propertyName] + ' vs ' +
            allowlistedStats[propertyName]);
      }
    } else {
      if (!Array.isArray(stats[propertyName])) {
        throw new Error('stats.' + propertyName + ' should have a different ' +
            'type according to the allowlist (should be an array): ' +
            JSON.stringify(stats[propertyName]) + ' vs ' +
            allowlistedStats[propertyName]);
      }
      let elementType = allowlistedStats[propertyName].substring(9);
      for (let element in stats[propertyName]) {
        if (typeof(element) !== elementType) {
          throw new Error('stats.' + propertyName +
              ' should have a different ' +
              'type according to the allowlist (an element of the array has ' +
              'the incorrect type): ' + JSON.stringify(stats[propertyName]) +
              ' vs ' + allowlistedStats[propertyName]);
        }
      }
    }
  }
}