• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright 2017 The Chromium Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6/*jshint esversion: 6 */
7
8'use strict';
9
10const $ = document.getElementById.bind(document);
11
12const MAIN_FEED_RESOLUTION = {w:1280, h:720};
13
14// This resolution is what we typically get on Hangouts Meet.
15const SMALL_FEED_RESOLUTION = {w:182, h:136};
16
17// This test frequently reports weird resolutions although the visuals look OK.
18// Hence, require lots of consecutive bad resolutions before failure.
19// TODO(kerl): Effectively disabled now, investigate why we get so many bad
20// resolution reports.
21const NUM_BAD_RESOLUTIONS_FOR_FAILURE = Number.MAX_SAFE_INTEGER;
22
23class TestRunner {
24  constructor(numConnections, runtimeSeconds, iterationDelayMillis) {
25    this.runtimeSeconds = runtimeSeconds;
26    this.iterationDelayMillis = iterationDelayMillis;
27    this.videoElements = [];
28    this.mainFeed = null;
29    this.peerConnections = [];
30    this.numConnections = numConnections;
31    this.iteration = 0;
32    this.startTime = 0;  // initialized to dummy value
33    this.status = this.getStatusInternal_();
34  }
35
36  runTest() {
37    for (let i = 0; i < this.numConnections; i++) {
38      const videoElement = document.createElement('video');
39      videoElement.autoplay = true;
40      $('body').appendChild(videoElement);
41      if (!this.mainFeed) {
42        // The first created is the main feed.
43        setSize(videoElement, MAIN_FEED_RESOLUTION);
44        this.mainFeed = videoElement;
45      } else {
46        setSize(videoElement, SMALL_FEED_RESOLUTION);
47        this.videoElements.push(videoElement);
48      }
49      this.peerConnections.push(new PeerConnection(
50          videoElement, [MAIN_FEED_RESOLUTION], cpuOveruseDetection));
51    }
52    const promises = this.peerConnections.map((conn) => conn.start());
53    Promise.all(promises)
54        .then(() => {
55          this.startTime = Date.now();
56          this.switchFeedLoop();
57        })
58        .catch((e) => {throw e});
59  }
60
61  switchFeedLoop() {
62    this.iteration++;
63    this.status = this.getStatusInternal_();
64    $('status').textContent = this.status;
65    if (this.status != 'ok-done') {
66      const switchWith = Math.floor(Math.random() * this.videoElements.length);
67      const newMainSrc = this.videoElements[switchWith].srcObject;
68      this.videoElements[switchWith].srcObject = this.mainFeed.srcObject;
69      this.mainFeed.srcObject = newMainSrc;
70      setTimeout(
71          () => this.switchFeedLoop(), this.iterationDelayMillis);
72    }
73  }
74
75  getStatus() {
76    return this.status;
77  }
78
79  getStatusInternal_() {
80    if (this.iteration == 0) {
81      return 'not-started';
82    }
83    try {
84      this.peerConnections.forEach(
85          (conn) => conn.verifyState(NUM_BAD_RESOLUTIONS_FOR_FAILURE));
86    } catch (e) {
87      return `failure: ${e.message}`;
88    }
89    const timeSpent = Date.now() - this.startTime;
90    if (timeSpent >= this.runtimeSeconds * 1000) {
91      return 'ok-done';
92    } else {
93      return `running, iteration: ${this.iteration}`;
94    }
95  }
96}
97
98function setSize(element, size) {
99  element.setAttribute('style', `width:${size.w}px;height:${size.h}px`);
100}
101
102// Declare testRunner so that the Python code can access it to query status.
103// Also allows us to access it easily in dev tools for debugging.
104let testRunner;
105// Set from the Python test runner
106let cpuOveruseDetection = null;
107
108function startTest(
109    runtimeSeconds, numPeerConnections, iterationDelayMillis) {
110  testRunner = new TestRunner(
111      numPeerConnections, runtimeSeconds, iterationDelayMillis);
112  testRunner.runTest();
113}
114
115function getStatus() {
116  return testRunner ? testRunner.getStatus() : 'not-initialized';
117}
118
119