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