Moving src/webrtc into src/.

In order to eliminate the WebRTC Subtree mirror in Chromium, 
WebRTC is moving the content of the src/webrtc directory up
to the src/ directory.

NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
TBR=tommi@webrtc.org

Bug: chromium:611808
Change-Id: Iac59c5b51b950f174119565bac87955a7994bc38
Reviewed-on: https://webrtc-review.googlesource.com/1560
Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Henrik Kjellander <kjellander@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#19845}
This commit is contained in:
Mirko Bonadei
2017-09-15 06:15:48 +02:00
committed by Commit Bot
parent 6674846b4a
commit bb547203bf
4576 changed files with 1092 additions and 1196 deletions

1
rtc_tools/rtcbot/OWNERS Normal file
View File

@ -0,0 +1 @@
andresp@webrtc.org

59
rtc_tools/rtcbot/README Normal file
View File

@ -0,0 +1,59 @@
=== RTCBot ===
RTCBot is a framework to write tests that need to spawn multiple webrtc
endpoints.
== Description ==
RTCBot is a framework that allows to write tests where logic runs on a single
host that controls multiple endpoints ("bots"). It allows creating complex
scenarios that would otherwise require non-trival signalling between multiple
parties.
The host runs in node.js, but the test code is run in an isolated context with
no access to node.js specifics other than the exposed api via a test variable.
Part of the exposed api (test.spawnBot) allows a test to spawn a bot and
access its exposed API. Details are in botmanager.js.
== How to run the test ==
$ cd trunk/webrtc/tool/rtcbot
$ npm install express browserify ws websocket-stream dnode
$ mkdir configurations
$ cd configurations
$ openssl genrsa -out priv.pem 1024
$ openssl req -x509 -new -key priv.pem -days 3650 -out cert.crt
$ cd trunk/webrtc/tool/rtcbot
$ node main.js "<test_name>"
* Note:
In first time you will use rtcBot you will receive a warning telling
you that your connection is not private. Just avoid this warning and
click Proceed to localhost (unsafe).
== How can I see the list of available tests? ==
$ node main.js
== Example on how to install nodejs ==
$ cd /work/tools/
$ git clone https://github.com/creationix/nvm.git
$ export NVM_DIR=/work/tools/nvm; source $NVM_DIR/nvm.sh
$ nvm install 0.10
$ nvm use 0.10
== Why generating the private key and self signed certificate? ==
- Private key and certificate are used for creating HTTPs server in
rtcBot for loading the required files on the different types of the bots.
== Supported Bot Types ==
- "chrome": chrome on host machine.
- "android-chrome": chrome on android device. Details in "Android" Section.
* Bot type is specified directly by the test.
== Android ==
Before running test with Android one MUST forward the device port 8080 to the
host machine. That is easy to achieve with chrome port forwarding tools.
- Visit chrome://inspect/devices on the host machine.
- Configure and enable port forwarding 8080 -> localhost:8080
- Open chrome on you Android device before running test, and leave it
running until the end of test.
- Run your test.

View File

@ -0,0 +1,37 @@
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//
// This file exposes the api for the bot to connect to the host script
// waiting a websocket connection and using dnode for javascript rpc.
//
// This file is served to the browser via browserify to resolve the
// dnode requires.
var WebSocketStream = require('websocket-stream');
var Dnode = require('dnode');
function connectToServer(api) {
var stream = new WebSocketStream("wss://localhost:8080/");
var dnode = new Dnode(api);
dnode.on('error', function (error) { console.log(error); });
dnode.pipe(stream).pipe(dnode);
}
// Dnode loses certain method calls when exposing native browser objects such as
// peer connections. This methods helps work around that by allowing one to
// redefine a non-native method in a target "obj" from "src" that applies a list
// of casts to the arguments (types are lost in dnode).
function expose(obj, src, method, casts) {
obj[method] = function () {
for (index in casts)
arguments[index] = new (casts[index])(arguments[index]);
src[method].apply(src, arguments);
}
}
window.expose = expose;
window.connectToServer = connectToServer;

View File

@ -0,0 +1,140 @@
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//
var localStreams = [];
var remoteStreams = [];
function ping(callback) {
callback("pong");
}
function getUserMedia(constraints, onSuccessCallback, onFailCallback){
console.log("Getting user media.");
navigator.webkitGetUserMedia(constraints,
onSuccessCallbackWraper, onFailCallback);
function onSuccessCallbackWraper(stream) {
console.log("GetUserMedia success.");
localStreams[stream.id] = stream;
onSuccessCallback(stream);
}
}
function createPeerConnection(config, doneCallback, failCallback) {
console.log("Creating peer connection");
var obj = {};
var pc = new webkitRTCPeerConnection(config);
expose(obj, pc, "close");
expose(obj, pc, "createOffer");
expose(obj, pc, "createAnswer");
expose(obj, pc, "addEventListener");
expose(obj, pc, "addIceCandidate", { 0: RTCIceCandidate});
expose(obj, pc, "setRemoteDescription", { 0: RTCSessionDescription });
expose(obj, pc, "setLocalDescription", { 0: RTCSessionDescription });
obj.addStream = function(stream) {
console.log("Adding local stream.");
var tempStream = localStreams[stream.id];
if (!tempStream) {
console.log("Undefined stream!");
return;
}
pc.addStream(tempStream);
};
// Return an array of Objects, each Object is a copy of RTCStateReport
// and has the following attributes (id, type, names, and stats).
// names: array originaly returned by calling RTCStateReport.names().
// stats: dictionary of stat name as key and stat value as dictionary
// value.
obj.getStats = function(callback, mediaTrack) {
pc.getStats(onStatsReady, mediaTrack);
function onStatsReady(stateResponse) {
var outputReports = [];
var reports = stateResponse.result();
for (index in reports) {
var report = {};
report.id = reports[index].id;
report.type = reports[index].type;
report.names = reports[index].names();
report.stats = [];
populateStats(reports[index], report.stats);
outputReports.push(report);
}
callback(outputReports);
}
function populateStats(report, stats) {
var names = report.names();
for (index in names) {
stats.push({
name: names[index],
stat: report.stat(names[index]),
});
}
}
};
pc.addEventListener('addstream', function(event) {
remoteStreams[event.stream.id] = event.stream;
});
doneCallback(obj);
};
function showStream(streamId, autoplay, muted) {
var stream = getStreamFromIdentifier_(streamId);
var video = document.createElement('video');
video.autoplay = autoplay;
video.muted = muted;
document.body.appendChild(video);
video.src = URL.createObjectURL(stream);
console.log("Stream " + stream.id + " attached to video element");
};
function getStreamFromIdentifier_(id) {
var tempStream = localStreams[id];
if (tempStream)
return tempStream;
tempStream = remoteStreams[id];
if (tempStream)
return tempStream;
console.log(id + " is not id for stream.");
return null;
};
function downloadFile(path, onSuccess, onError) {
var xhr = new XMLHttpRequest();
function onResult() {
if (xhr.readyState != 4)
return;
if (xhr.status != 200) {
onError("Download request failed!");
return;
}
onSuccess(xhr.responseText);
}
xhr.onreadystatechange = onResult;
xhr.open('GET', path, true);
xhr.send();
};
connectToServer({
ping: ping,
getUserMedia: getUserMedia,
createPeerConnection: createPeerConnection,
showStream: showStream,
downloadFile: downloadFile,
});

View File

@ -0,0 +1,11 @@
<!--
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
-->
<script src="../api.js"></script>
<script src="bot.js"></script>

View File

@ -0,0 +1,216 @@
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//
// botmanager.js module allows a test to spawn bots that expose an RPC API
// to be controlled by tests.
var https = require('https');
var fs = require('fs');
var child = require('child_process');
var Browserify = require('browserify');
var Dnode = require('dnode');
var Express = require('express');
var WebSocketServer = require('ws').Server;
var WebSocketStream = require('websocket-stream');
// BotManager runs a HttpsServer that serves bots assets and and WebSocketServer
// that listens to incoming connections. Once a connection is available it
// connects it to bots pending endpoints.
//
// TODO(andresp): There should be a way to control which bot was spawned
// and what bot instance it gets connected to.
BotManager = function () {
this.webSocketServer_ = null;
this.bots_ = [];
this.pendingConnections_ = [];
this.androidDeviceManager_ = new AndroidDeviceManager();
}
BotManager.BotTypes = {
CHROME : 'chrome',
ANDROID_CHROME : 'android-chrome',
};
BotManager.prototype = {
createBot_: function (name, botType, callback) {
switch(botType) {
case BotManager.BotTypes.CHROME:
return new BrowserBot(name, callback);
case BotManager.BotTypes.ANDROID_CHROME:
return new AndroidChromeBot(name, this.androidDeviceManager_,
callback);
default:
console.log('Error: Type ' + botType + ' not supported by rtc-Bot!');
process.exit(1);
}
},
spawnNewBot: function (name, botType, callback) {
this.startWebSocketServer_();
var bot = this.createBot_(name, botType, callback);
this.bots_.push(bot);
this.pendingConnections_.push(bot.onBotConnected.bind(bot));
},
startWebSocketServer_: function () {
if (this.webSocketServer_) return;
this.app_ = new Express();
this.app_.use('/bot/api.js',
this.serveBrowserifyFile_.bind(this,
__dirname + '/bot/api.js'));
this.app_.use('/bot/', Express.static(__dirname + '/bot'));
var options = options = {
key: fs.readFileSync('configurations/priv.pem', 'utf8'),
cert: fs.readFileSync('configurations/cert.crt', 'utf8')
};
this.server_ = https.createServer(options, this.app_);
this.webSocketServer_ = new WebSocketServer({ server: this.server_ });
this.webSocketServer_.on('connection', this.onConnection_.bind(this));
this.server_.listen(8080);
},
onConnection_: function (ws) {
var callback = this.pendingConnections_.shift();
callback(new WebSocketStream(ws));
},
serveBrowserifyFile_: function (file, request, result) {
// TODO(andresp): Cache browserify result for future serves.
var browserify = new Browserify();
browserify.add(file);
browserify.bundle().pipe(result);
}
}
// A basic bot waits for onBotConnected to be called with a stream to the actual
// endpoint with the bot. Once that stream is available it establishes a dnode
// connection and calls the callback with the other endpoint interface so the
// test can interact with it.
Bot = function (name, callback) {
this.name_ = name;
this.onbotready_ = callback;
}
Bot.prototype = {
log: function (msg) {
console.log("bot:" + this.name_ + " > " + msg);
},
name: function () { return this.name_; },
onBotConnected: function (stream) {
this.log('Connected');
this.stream_ = stream;
this.dnode_ = new Dnode();
this.dnode_.on('remote', this.onRemoteFromDnode_.bind(this));
this.dnode_.pipe(this.stream_).pipe(this.dnode_);
},
onRemoteFromDnode_: function (remote) {
this.onbotready_(remote);
}
}
// BrowserBot spawns a process to open "https://localhost:8080/bot/browser".
//
// That page once loaded, connects to the websocket server run by BotManager
// and exposes the bot api.
BrowserBot = function (name, callback) {
Bot.call(this, name, callback);
this.spawnBotProcess_();
}
BrowserBot.prototype = {
spawnBotProcess_: function () {
this.log('Spawning browser');
child.exec('google-chrome "https://localhost:8080/bot/browser/"');
},
__proto__: Bot.prototype
}
// AndroidChromeBot spawns a process to open
// "https://localhost:8080/bot/browser/" on chrome for Android.
AndroidChromeBot = function (name, androidDeviceManager, callback) {
Bot.call(this, name, callback);
androidDeviceManager.getNewDevice(function (serialNumber) {
this.serialNumber_ = serialNumber;
this.spawnBotProcess_();
}.bind(this));
}
AndroidChromeBot.prototype = {
spawnBotProcess_: function () {
this.log('Spawning Android device with serial ' + this.serialNumber_);
var runChrome = 'adb -s ' + this.serialNumber_ + ' shell am start ' +
'-n com.android.chrome/com.google.android.apps.chrome.Main ' +
'-d https://localhost:8080/bot/browser/';
child.exec(runChrome, function (error, stdout, stderr) {
if (error) {
this.log(error);
process.exit(1);
}
this.log('Opening Chrome for Android...');
this.log(stdout);
}.bind(this));
},
__proto__: Bot.prototype
}
AndroidDeviceManager = function () {
this.connectedDevices_ = [];
}
AndroidDeviceManager.prototype = {
getNewDevice: function (callback) {
this.listDevices_(function (devices) {
for (var i = 0; i < devices.length; i++) {
if (!this.connectedDevices_[devices[i]]) {
this.connectedDevices_[devices[i]] = devices[i];
callback(this.connectedDevices_[devices[i]]);
return;
}
}
if (devices.length == 0) {
console.log('Error: No connected devices!');
} else {
console.log('Error: There is no enough connected devices.');
}
process.exit(1);
}.bind(this));
},
listDevices_: function (callback) {
child.exec('adb devices' , function (error, stdout, stderr) {
var devices = [];
if (error || stderr) {
console.log(error || stderr);
}
if (stdout) {
// The first line is "List of devices attached"
// and the following lines:
// <serial number> <device/emulator>
var tempList = stdout.split("\n").slice(1);
for (var i = 0; i < tempList.length; i++) {
if (tempList[i] == "") {
continue;
}
devices.push(tempList[i].split("\t")[0]);
}
}
callback(devices);
});
},
}
module.exports = BotManager;

102
rtc_tools/rtcbot/main.js Normal file
View File

@ -0,0 +1,102 @@
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//
// This script loads all the test/* files into a very small context that
// only exposes a minimal set of functions that allows to register tests.
//
// Once all files are loaded it runs the specific test on the command line.
// If no arguments are given it lists all the registered tests.
//
// Note: the small context where the scripts are loaded is intended to keep
// nodejs-isms away from the test code and isolate implementation details away
// from them.
var fs = require('fs');
var vm = require('vm');
var Test = require('./test.js');
var testSuites = {};
function registerTest(name, func) {
testSuites[name] = func;
}
function registerBotTest(name, func, bots) {
registerTest(name, bootstrap);
function bootstrap(test) {
var callbacks = [];
for (var i = 0; i != bots.length; ++i)
callbacks.push(test.spawnBot.bind(test, "", bots[i]));
test.wait(callbacks, func.bind(test, test));
}
}
function loadTestFile(filename, doneCallback) {
var loadTestContext = {
setTimeout: setTimeout,
registerTest: registerTest,
registerBotTest: registerBotTest
};
var script = vm.createScript(fs.readFileSync(filename), filename);
script.runInNewContext(loadTestContext);
doneCallback();
}
function iterateOverTestFiles(foreachCallback, doneCallback) {
fs.readdir('test', function (error, list) {
function iterateNextFile() {
if (list.length === 0) {
doneCallback();
} else {
var filename = list.pop();
if (filename[0] === '.' || filename.slice(-3) !== '.js') {
// Skip hidden and non .js files on that directory.
iterateNextFile();
} else {
foreachCallback('test/' + filename, iterateNextFile);
}
}
}
if (error !== null) {
throw error;
}
iterateNextFile();
});
}
function runTest(testname) {
if (testname in testSuites) {
console.log("Running test: " + testname);
var test = new Test();
testSuites[testname](test);
} else {
console.log("Unknown test: " + testname);
}
}
function printUsage() {
console.log('Run as:\n $ '
+ process.argv[0] + ' ' + process.argv[1]
+ ' <testname>');
console.log('These are the existent ones:');
for (var testname in testSuites)
console.log(' ' + testname);
}
function main() {
// TODO(andresp): support multiple tests.
var testList = process.argv.slice(2);
if (testList.length === 1)
runTest(testList[0]);
else
printUsage();
}
iterateOverTestFiles(loadTestFile, main);

View File

@ -0,0 +1,14 @@
<!--
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
-->
<html>
<script src="https://www.google.com/jsapi"></script>
<script type="text/javascript" src="main.js"></script>
<input type="file" onchange="openFiles(event)" multiple>
</html>

View File

@ -0,0 +1,191 @@
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//
google.load("visualization", "1", {packages:["corechart"]});
function openFiles(event) {
var files = event.target.files;
readAndAnalyzeFiles(files)
}
function readAndAnalyzeFiles(files) {
if(!files) {
alert("No files have been selected!");
return;
}
var reports = [];
var filesNames = [];
missingFiles = files.length;
for(var i = 0; i < files.length; i++) {
var reader = new FileReader();
reader.onload = onReaderLoad.bind(reader, files[i].name);
reader.readAsText(files[i]);
}
function onReaderLoad(fileName) {
reports.push(JSON.parse(this.result));
filesNames.push(fileName);
missingFiles--;
if(missingFiles == 0) {
analyzeReports_(reports, filesNames);
}
}
}
// TODO(houssainy) take the input stats from the select list or
// drop down menu in html.
function analyzeReports_(reports, filesNames) {
filesNames.unshift(""); // ned
// Rtt
analyzeRttData(reports, filesNames, "bot1");
analyzeRttData(reports, filesNames, "bot2");
// Send Packets Lost
analyzePacketsLostData(reports, filesNames, "bot1");
analyzePacketsLostData(reports, filesNames, "bot2");
// Send bandwidth
analyzeData(reports, filesNames, "Available Send Bandwidth-bot1", "bot1",
"bweforvideo", "googAvailableSendBandwidth");
analyzeData(reports, filesNames, "Available Send Bandwidth-bot2", "bot2",
"bweforvideo", "googAvailableSendBandwidth");
// Receive bandwidth
analyzeData(reports, filesNames, "Available Receive Bandwidth-bot1", "bot1",
"bweforvideo", "googAvailableReceiveBandwidth");
analyzeData(reports, filesNames, "Available Receive Bandwidth-bot2", "bot2",
"bweforvideo", "googAvailableReceiveBandwidth");
drawSeparatorLine();
}
function analyzeRttData(reports, filesNames, botName) {
var outPut = [];
outPut.push(filesNames);
var avergaData = ['Average Rtt x10'];
var maxData = ['Max Rtt'];
var average;
var max;
for(var index in reports) {
average = getStateAverage(reports[index], botName, "Conn-audio-1-0",
"googRtt");
avergaData.push(average*10);
max = getStateMax(reports[index], botName, "Conn-audio-1-0",
"googRtt");
maxData.push(max);
}
outPut.push(avergaData);
outPut.push(maxData);
drawChart("Rtt-" + botName, outPut);
}
function analyzePacketsLostData(reports, filesNames, botName) {
var outPut = [];
outPut.push(filesNames);
var maxData = ['Max Send PacketsLost'];
var max;
for(var index in reports) {
max = getStateMax(reports[index], botName, "ssrc_[0-9]+_send",
"packetsLost");
maxData.push(max);
}
outPut.push(maxData);
drawChart("Send PacketsLost-" + botName, outPut);
}
function analyzeData(reports, filesNames, chartName, botName, reportId,
statName) {
var outPut = [];
outPut.push(filesNames);
var avergaData = ['Average ' + statName];
var maxData = ['Max ' + statName];
var average;
var max;
for(var index in reports) {
average = getStateAverage(reports[index], botName, reportId, statName);
avergaData.push(average);
max = getStateMax(reports[index], botName, reportId, statName);
maxData.push(max);
}
outPut.push(avergaData);
outPut.push(maxData);
drawChart(chartName, outPut);
}
function getStateAverage(reports, botName, reportId, statName) {
var sum = 0;
var count = 0;
for (var index in reports) {
var data = reports[index].data;
if(index == 0 || !data.hasOwnProperty(botName))
continue;
var stats = data[botName];
for (var key in stats) {
if(key.search(reportId) != -1) {
var value = parseInt(stats[key][statName]);
sum += value;
count++;
}
}
}
return Math.round(sum/count);
}
function getStateMax(reports, botName, reportId, statName) {
var max = -1;
for (var index in reports) {
var data = reports[index].data;
if(index == 0 || !data.hasOwnProperty(botName))
continue;
var stats = data[botName];
for (var key in stats) {
if(key.search(reportId) != -1) {
var value = parseInt(stats[key][statName]);
max = Math.max(value, max);
}
}
}
return max;
}
function drawChart(title, data) {
var dataTable = google.visualization.arrayToDataTable(data);
var options = {
title: title,
};
var div = document.createElement('div');
document.body.appendChild(div);
var chart = new google.visualization.ColumnChart(div);
chart.draw(dataTable, options);
}
function drawSeparatorLine() {
var hr = document.createElement('hr');
document.body.appendChild(hr);
}

155
rtc_tools/rtcbot/test.js Normal file
View File

@ -0,0 +1,155 @@
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//
// Provides a Test class that exposes api to the tests.
// Read test.prototype to see what methods are exposed.
var fs = require('fs');
var request = require('request');
var BotManager = require('./botmanager.js');
function Test() {
this.timeout_ = setTimeout(
this.fail.bind(this, "Test timeout!"),
100000);
}
Test.prototype = {
log: function () {
console.log.apply(console.log, arguments);
},
abort: function (error) {
var error = new Error(error || "Test aborted");
console.log(error.stack);
process.exit(1);
},
assert: function (value, message) {
if (value !== true) {
this.abort(message || "Assert failed.");
}
},
fail: function () {
this.assert(false, "Test failed.");
},
done: function () {
clearTimeout(this.timeout_);
console.log("Test succeeded");
process.exit(0);
},
// Utility method to wait for multiple callbacks to be executed.
// functions - array of functions to call with a callback.
// doneCallback - called when all callbacks on the array have completed.
wait: function (functions, doneCallback) {
var result = new Array(functions.length);
var missingResult = functions.length;
for (var i = 0; i != functions.length; ++i)
functions[i](complete.bind(this, i));
function complete(index, value) {
missingResult--;
result[index] = value;
if (missingResult == 0)
doneCallback.apply(null, result);
}
},
spawnBot: function (name, botType, doneCallback) {
// Lazy initialization of botmanager.
if (!this.botManager_)
this.botManager_ = new BotManager();
this.botManager_.spawnNewBot(name, botType, doneCallback);
},
createStatisticsReport: function (outputFileName) {
return new StatisticsReport(outputFileName);
},
// Ask computeengineondemand to give us TURN server credentials and URIs.
createTurnConfig: function (onSuccess, onError) {
request('https://computeengineondemand.appspot.com/turn?username=1234&key=5678',
function (error, response, body) {
if (error || response.statusCode != 200) {
onError('TURN request failed');
return;
}
var response = JSON.parse(body);
var iceServer = {
'username': response.username,
'credential': response.password,
'urls': response.uris
};
onSuccess({ 'iceServers': [ iceServer ] });
}
);
},
}
StatisticsReport = function (outputFileName) {
this.output_ = [];
this.output_.push("Version: 1");
this.outputFileName_ = outputFileName;
}
StatisticsReport.prototype = {
collectStatsFromPeerConnection: function (prefix, pc) {
setInterval(this.addPeerConnectionStats.bind(this, prefix, pc), 100);
},
addPeerConnectionStats: function (prefix, pc) {
pc.getStats(onStatsReady.bind(this));
function onStatsReady(reports) {
for (index in reports) {
var stats = {};
stats[reports[index].id] = collectStats(reports[index].stats);
var data = {};
data[prefix] = stats;
this.output_.push({
type: "UpdateCounters",
startTime: (new Date()).getTime(),
data: data,
});
}
};
function collectStats(stats) {
var outputStats = {};
for (index in stats) {
var statValue = parseFloat(stats[index].stat);
outputStats[stats[index].name] = isNaN(statValue)?
stats[index].stat : statValue;
}
return outputStats;
};
},
finish: function (doneCallback) {
fs.exists("test/reports/", function (exists) {
if(exists) {
writeFile.bind(this)();
} else {
fs.mkdir("test/reports/", 0777, writeFile.bind(this));
}
}.bind(this));
function writeFile () {
fs.writeFile("test/reports/" + this.outputFileName_ + "_" +
(new Date()).getTime() +".json", JSON.stringify(this.output_),
doneCallback);
}
},
};
module.exports = Test;

View File

@ -0,0 +1,122 @@
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//
// A unidirectional video and audio flowing test from bot 1 to bot 2,
// and download a file from a server after 2 seconds of establishing
// the call.
//
// The test succeeds after collecting stats for 10 seconds from both bots
// and then write these stats to a file.
//
// Note: the source of the video and audio stream is getUserMedia().
//
function testOneWayVideoWithDownloading(test, bot1, bot2) {
var report = test.createStatisticsReport("testOneWayVideoWithDownloading");
test.wait([
createPeerConnection.bind(bot1),
createPeerConnection.bind(bot2) ],
onPeerConnectionCreated);
function createPeerConnection(done) {
test.createTurnConfig(onTurnConfig.bind(this), test.fail);
function onTurnConfig(config) {
this.createPeerConnection(config, done, test.fail);
};
}
function onPeerConnectionCreated(pc1, pc2) {
test.log("RTC Peers created.");
pc1.addEventListener('addstream', test.fail);
pc2.addEventListener('addstream', onAddStream);
pc1.addEventListener('icecandidate', onIceCandidate.bind(pc2));
pc2.addEventListener('icecandidate', onIceCandidate.bind(pc1));
bot1.getUserMedia({video:true, audio:true}, onUserMediaSuccess, test.fail);
function onUserMediaSuccess(stream) {
test.log("User has granted access to local media.");
pc1.addStream(stream);
bot1.showStream(stream.id, true, true);
createOfferAndAnswer(pc1, pc2);
}
}
function onAddStream(event) {
test.log("On Add stream.");
bot2.showStream(event.stream.id, true, false);
}
function onIceCandidate(event) {
if(event.candidate) {
test.log(event.candidate.candidate);
this.addIceCandidate(event.candidate,
onAddIceCandidateSuccess, test.fail);
}
function onAddIceCandidateSuccess() {
test.log("Candidate added successfully");
}
}
function createOfferAndAnswer(pc1, pc2) {
test.log("Creating offer.");
pc1.createOffer(gotOffer, test.fail);
function gotOffer(offer) {
test.log("Got offer");
pc1.setLocalDescription(offer, onSetSessionDescriptionSuccess, test.fail);
pc2.setRemoteDescription(offer, onSetSessionDescriptionSuccess,
test.fail);
test.log("Creating answer");
pc2.createAnswer(gotAnswer, test.fail);
}
function gotAnswer(answer) {
test.log("Got answer");
pc2.setLocalDescription(answer, onSetSessionDescriptionSuccess,
test.fail);
pc1.setRemoteDescription(answer, onSetSessionDescriptionSuccess,
test.fail);
collectStats();
setTimeout(function() {
downloadFile(bot1, "bot1");
downloadFile(bot2, "bot2");
}, 2000);
}
function onSetSessionDescriptionSuccess() {
test.log("Set session description success.");
}
function collectStats() {
report.collectStatsFromPeerConnection("bot1", pc1);
report.collectStatsFromPeerConnection("bot2", pc2);
setTimeout(function() {
report.finish(test.done);
}, 10000);
}
function downloadFile(bot, name) {
bot.downloadFile("https://test.webrtc.org/test-download-file/9000KB.data",
onDownloadSuccess.bind(null, name), test.fail);
function onDownloadSuccess(name, data) {
test.log( name + " downloaded " +
Math.round(data.length/(1024*1024)) + "MB.");
}
}
}
}
registerBotTest('testOneWayVideoWithDownloading/chrome-chrome',
testOneWayVideoWithDownloading, ['chrome', 'chrome']);

View File

@ -0,0 +1,20 @@
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//
function testPingPong(test, bot) {
test.assert(typeof bot.ping === 'function', 'Bot does not exposes ping.');
bot.ping(gotAnswer);
function gotAnswer(answer) {
test.log('bot > ' + answer);
test.done();
}
}
registerBotTest('testPingPong/chrome', testPingPong, ['chrome']);

View File

@ -0,0 +1,48 @@
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//
// Test that offer/answer between 2 peers completes successfully.
//
// Note: This test does not performs ice candidate exchange and
// does not verifies that media can flow between the peers.
function testOfferAnswer(test, bot1, bot2) {
test.wait( [ bot1.createPeerConnection.bind(bot1, null),
bot2.createPeerConnection.bind(bot2, null) ],
run);
function run(pc1, pc2) {
test.log("Establishing call.");
pc1.createOffer(gotOffer);
function gotOffer(offer) {
test.log("Got offer");
expectedCall();
pc1.setLocalDescription(offer, expectedCall, test.fail);
pc2.setRemoteDescription(offer, expectedCall, test.fail);
pc2.createAnswer(gotAnswer, test.fail);
}
function gotAnswer(answer) {
test.log("Got answer");
expectedCall();
pc2.setLocalDescription(answer, expectedCall, test.fail);
pc1.setRemoteDescription(answer, expectedCall, test.fail);
}
// TODO(andresp): Implement utilities in test to write expectations
// that certain methods must be called.
var expectedCalls = 0;
function expectedCall() {
if (++expectedCalls == 6)
test.done();
}
}
}
registerBotTest('testOfferAnswer/chrome-chrome',
testOfferAnswer, ['chrome', 'chrome']);

View File

@ -0,0 +1,135 @@
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//
// A video conference between 3 bots streaming video and audio between
// each other.
// The test succeeds after establishing the call between the three
// devices.
//
// Note: the source of the video and audio stream is getUserMedia().
function testTwoWayVideoStreaming(test, bot1, bot2, bot3) {
var answersCount = 0;
var statsCollector;
test.wait([
createBotPeerConnectionsWithLocalStream.bind(bot1),
createBotPeerConnectionsWithLocalStream.bind(bot2),
createBotPeerConnectionsWithLocalStream.bind(bot3)],
onPeerConnectionCreated);
// done() callback is called with list of peers as argument.
function createBotPeerConnectionsWithLocalStream(done) {
var peerConnections = [];
this.getUserMedia({video:true, audio:true},
onUserMediaSuccess.bind(this), test.fail);
function onUserMediaSuccess(stream) {
test.log("User has granted access to local media.");
this.showStream(stream.id, true, true);
test.createTurnConfig(onTurnConfig.bind(this), test.fail);
function onTurnConfig(config) {
this.createPeerConnection(config, addStream.bind(this),
test.fail);
this.createPeerConnection(config, addStream.bind(this),
test.fail);
}
function addStream(pc) {
pc.addStream(stream);
pc.addEventListener('addstream', onAddStream.bind(this));
peerConnections.push(pc);
if(peerConnections.length == 2)
done(peerConnections);
}
}
}
function onPeerConnectionCreated(peerConnections1,
peerConnections2, peerConnections3) {
test.log("RTC Peers created.");
// Bot1 and Bot2
establichCall(peerConnections1[0], peerConnections2[1]);
// Bot2 and Bot3
establichCall(peerConnections2[0], peerConnections3[1]);
// Bot3 and Bot1
establichCall(peerConnections3[0], peerConnections1[1]);
}
function establichCall(pc1, pc2) {
pc1.addEventListener('icecandidate', onIceCandidate.bind(pc2));
pc2.addEventListener('icecandidate', onIceCandidate.bind(pc1));
createOfferAndAnswer(pc1, pc2);
}
function onAddStream(event) {
test.log("On Add stream.");
this.showStream(event.stream.id, true, false);
}
function onIceCandidate(event) {
if(event.candidate) {
this.addIceCandidate(event.candidate,
onAddIceCandidateSuccess, test.fail);
};
function onAddIceCandidateSuccess() {
test.log("Candidate added successfully");
};
}
function createOfferAndAnswer(pc1, pc2) {
test.log("Creating offer.");
pc1.createOffer(gotOffer, test.fail);
function gotOffer(offer) {
test.log("Got offer");
pc1.setLocalDescription(offer, onSetSessionDescriptionSuccess, test.fail);
pc2.setRemoteDescription(offer, onSetSessionDescriptionSuccess,
test.fail);
test.log("Creating answer");
pc2.createAnswer(gotAnswer, test.fail);
}
function gotAnswer(answer) {
test.log("Got answer");
pc2.setLocalDescription(answer, onSetSessionDescriptionSuccess,
test.fail);
pc1.setRemoteDescription(answer, onSetSessionDescriptionSuccess,
test.fail);
answersCount++;
if(answersCount == 3) {
// SetTimeout used because creating the three answers will very fast
// and test will success and the vm will be closed before establishing
// the calls.
setTimeout(function() {
test.done();
}, 5000);
}
}
function onSetSessionDescriptionSuccess() {
test.log("Set session description success.");
}
}
}
registerBotTest('threeBotsVideoConference/android+android+chrome',
testTwoWayVideoStreaming, ['android-chrome', 'android-chrome',
'chrome']);
registerBotTest('threeBotsVideoConference/chrome-chrome-chrome',
testTwoWayVideoStreaming, ['chrome', 'chrome', 'chrome']);
registerBotTest('threeBotsVideoConference/android-android-android',
testTwoWayVideoStreaming, ['android-chrome', 'android-chrome',
'android-chrome']);

View File

@ -0,0 +1,112 @@
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//
// A two way video and audio flowing test between bot 1 and bot 2.
// The test succeeds after collecting stats for 10 seconds from both bots
// and then write these stats to a file.
//
// Note: the source of the video and audio stream is getUserMedia().
function testTwoWayVideoStreaming(test, bot1, bot2) {
var report = test.createStatisticsReport("two_way_video_streaming");
var statsCollector;
test.wait([
createPeerConnectionWithLocalStream.bind(bot1),
createPeerConnectionWithLocalStream.bind(bot2)],
onPeerConnectionCreated);
function createPeerConnectionWithLocalStream(done) {
this.getUserMedia({video:true, audio:true},
onUserMediaSuccess.bind(this), test.fail);
function onUserMediaSuccess(stream) {
test.log("User has granted access to local media.");
test.createTurnConfig(onTurnConfig.bind(this), test.fail);
function onTurnConfig(config) {
this.createPeerConnection(config, addAndShowStream.bind(this),
test.fail);
};
function addAndShowStream(pc) {
pc.addStream(stream);
this.showStream(stream.id, true, true);
done(pc);
}
}
}
function onPeerConnectionCreated(pc1, pc2) {
test.log("RTC Peers created.");
pc1.addEventListener('addstream', onAddStream.bind(bot1));
pc2.addEventListener('addstream', onAddStream.bind(bot2));
pc1.addEventListener('icecandidate', onIceCandidate.bind(pc2));
pc2.addEventListener('icecandidate', onIceCandidate.bind(pc1));
createOfferAndAnswer(pc1, pc2);
}
function onAddStream(event) {
test.log("On Add stream.");
this.showStream(event.stream.id, true, false);
}
function onIceCandidate(event) {
if(event.candidate) {
test.log(event.candidate.candidate);
this.addIceCandidate(event.candidate,
onAddIceCandidateSuccess, test.fail);
};
function onAddIceCandidateSuccess() {
test.log("Candidate added successfully");
};
}
function createOfferAndAnswer(pc1, pc2) {
test.log("Creating offer.");
pc1.createOffer(gotOffer, test.fail);
function gotOffer(offer) {
test.log("Got offer");
pc1.setLocalDescription(offer, onSetSessionDescriptionSuccess, test.fail);
pc2.setRemoteDescription(offer, onSetSessionDescriptionSuccess,
test.fail);
test.log("Creating answer");
pc2.createAnswer(gotAnswer, test.fail);
}
function gotAnswer(answer) {
test.log("Got answer");
pc2.setLocalDescription(answer, onSetSessionDescriptionSuccess,
test.fail);
pc1.setRemoteDescription(answer, onSetSessionDescriptionSuccess,
test.fail);
collectStats();
}
function onSetSessionDescriptionSuccess() {
test.log("Set session description success.");
}
function collectStats() {
report.collectStatsFromPeerConnection("bot1", pc1);
report.collectStatsFromPeerConnection("bot2", pc2);
setTimeout(function() {
report.finish(test.done);
}, 10000);
}
}
}
registerBotTest('testTwoWayVideo/android-android',
testTwoWayVideoStreaming, ['android-chrome', 'android-chrome']);
registerBotTest('testTwoWayVideo/chrome-chrome',
testTwoWayVideoStreaming, ['chrome', 'chrome']);

View File

@ -0,0 +1,103 @@
// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//
// A unidirectional video and audio flowing test from bot 1 to bot 2.
// The test succeeds after collecting stats for 10 seconds from both bots
// and then write these stats to a file.
//
// Note: the source of the video and audio stream is getUserMedia().
function testOneWayVideo(test, bot1, bot2) {
var report = test.createStatisticsReport("webrtc_video_streaming");
test.wait([
createPeerConnection.bind(bot1),
createPeerConnection.bind(bot2) ],
onPeerConnectionCreated);
function createPeerConnection(done) {
test.createTurnConfig(onTurnConfig.bind(this), test.fail);
function onTurnConfig(config) {
this.createPeerConnection(config, done, test.fail);
};
}
function onPeerConnectionCreated(pc1, pc2) {
test.log("RTC Peers created.");
pc1.addEventListener('addstream', test.fail);
pc2.addEventListener('addstream', onAddStream);
pc1.addEventListener('icecandidate', onIceCandidate.bind(pc2));
pc2.addEventListener('icecandidate', onIceCandidate.bind(pc1));
bot1.getUserMedia({video:true, audio:true}, onUserMediaSuccess, test.fail);
function onUserMediaSuccess(stream) {
test.log("User has granted access to local media.");
pc1.addStream(stream);
bot1.showStream(stream.id, true, true);
createOfferAndAnswer(pc1, pc2);
}
}
function onAddStream(event) {
test.log("On Add stream.");
bot2.showStream(event.stream.id, true, false);
}
function onIceCandidate(event) {
if(event.candidate) {
test.log(event.candidate.candidate);
this.addIceCandidate(event.candidate,
onAddIceCandidateSuccess, test.fail);
}
function onAddIceCandidateSuccess() {
test.log("Candidate added successfully");
}
}
function createOfferAndAnswer(pc1, pc2) {
test.log("Creating offer.");
pc1.createOffer(gotOffer, test.fail);
function gotOffer(offer) {
test.log("Got offer");
pc1.setLocalDescription(offer, onSetSessionDescriptionSuccess, test.fail);
pc2.setRemoteDescription(offer, onSetSessionDescriptionSuccess,
test.fail);
test.log("Creating answer");
pc2.createAnswer(gotAnswer, test.fail);
}
function gotAnswer(answer) {
test.log("Got answer");
pc2.setLocalDescription(answer, onSetSessionDescriptionSuccess,
test.fail);
pc1.setRemoteDescription(answer, onSetSessionDescriptionSuccess,
test.fail);
collectStats();
}
function onSetSessionDescriptionSuccess() {
test.log("Set session description success.");
}
function collectStats() {
report.collectStatsFromPeerConnection("bot1", pc1);
report.collectStatsFromPeerConnection("bot2", pc2);
setTimeout(function() {
report.finish(test.done);
}, 10000);
}
}
}
registerBotTest('testOneWayVideo/chrome-chrome',
testOneWayVideo, ['chrome', 'chrome']);