blob: e8f8b9b2f2ba48c844457a942ba63bfa387e041a [file] [log] [blame]
andresp@webrtc.org468516c2014-09-02 10:52:54 +00001// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the LICENSE file in the root of the source
5// tree. An additional intellectual property rights grant can be found
6// in the file PATENTS. All contributing project authors may
7// be found in the AUTHORS file in the root of the source tree.
8//
9// botmanager.js module allows a test to spawn bots that expose an RPC API
10// to be controlled by tests.
11var http = require('http');
12var child = require('child_process');
13var Browserify = require('browserify');
14var Dnode = require('dnode');
15var Express = require('express');
16var WebSocketServer = require('ws').Server;
17var WebSocketStream = require('websocket-stream');
18
19// BotManager runs a HttpServer that serves bots assets and and WebSocketServer
20// that listens to incoming connections. Once a connection is available it
21// connects it to bots pending endpoints.
22//
23// TODO(andresp): There should be a way to control which bot was spawned
24// and what bot instance it gets connected to.
25BotManager = function () {
26 this.webSocketServer_ = null;
27 this.bots_ = [];
28 this.pendingConnections_ = [];
29}
30
houssainy@google.comc77e4d62014-09-08 10:36:11 +000031BotManager.BotTypes = {
32 CHROME : 'chrome',
33};
34
andresp@webrtc.org468516c2014-09-02 10:52:54 +000035BotManager.prototype = {
houssainy@google.comc77e4d62014-09-08 10:36:11 +000036 createBot_: function (name, botType, callback) {
37 switch(botType) {
38 case BotManager.BotTypes.CHROME:
39 return new BrowserBot(name, callback);
40 default:
41 console.log('Error: Type ' + botType + ' not supported by rtc-Bot!');
42 process.exit(1);
43 }
44 },
45
46 spawnNewBot: function (name, botType, callback) {
andresp@webrtc.org468516c2014-09-02 10:52:54 +000047 this.startWebSocketServer_();
houssainy@google.comc77e4d62014-09-08 10:36:11 +000048 var bot = this.createBot_(name, botType, callback);
andresp@webrtc.org468516c2014-09-02 10:52:54 +000049 this.bots_.push(bot);
50 this.pendingConnections_.push(bot.onBotConnected.bind(bot));
51 },
52
53 startWebSocketServer_: function () {
54 if (this.webSocketServer_) return;
55
56 this.app_ = new Express();
57
houssainy@google.com38ef6642014-09-04 13:44:47 +000058 this.app_.use('/bot/api.js',
andresp@webrtc.org468516c2014-09-02 10:52:54 +000059 this.serveBrowserifyFile_.bind(this,
houssainy@google.com38ef6642014-09-04 13:44:47 +000060 __dirname + '/bot/api.js'));
andresp@webrtc.org468516c2014-09-02 10:52:54 +000061
houssainy@google.com38ef6642014-09-04 13:44:47 +000062 this.app_.use('/bot/', Express.static(__dirname + '/bot'));
andresp@webrtc.org468516c2014-09-02 10:52:54 +000063
64 this.server_ = http.createServer(this.app_);
65
66 this.webSocketServer_ = new WebSocketServer({ server: this.server_ });
67 this.webSocketServer_.on('connection', this.onConnection_.bind(this));
68
69 this.server_.listen(8080);
70 },
71
72 onConnection_: function (ws) {
73 var callback = this.pendingConnections_.shift();
74 callback(new WebSocketStream(ws));
75 },
76
77 serveBrowserifyFile_: function (file, request, result) {
78 // TODO(andresp): Cache browserify result for future serves.
79 var browserify = new Browserify();
80 browserify.add(file);
81 browserify.bundle().pipe(result);
82 }
83}
84
85// A basic bot waits for onBotConnected to be called with a stream to the actual
86// endpoint with the bot. Once that stream is available it establishes a dnode
87// connection and calls the callback with the other endpoint interface so the
88// test can interact with it.
89Bot = function (name, callback) {
90 this.name_ = name;
91 this.onbotready_ = callback;
92}
93
94Bot.prototype = {
95 log: function (msg) {
96 console.log("bot:" + this.name_ + " > " + msg);
97 },
98
99 name: function () { return this.name_; },
100
101 onBotConnected: function (stream) {
102 this.log('Connected');
103 this.stream_ = stream;
104 this.dnode_ = new Dnode();
105 this.dnode_.on('remote', this.onRemoteFromDnode_.bind(this));
106 this.dnode_.pipe(this.stream_).pipe(this.dnode_);
107 },
108
109 onRemoteFromDnode_: function (remote) {
110 this.onbotready_(remote);
111 }
112}
113
houssainy@google.com38ef6642014-09-04 13:44:47 +0000114// BrowserBot spawns a process to open "http://localhost:8080/bot/".
andresp@webrtc.org468516c2014-09-02 10:52:54 +0000115//
116// That page once loaded, connects to the websocket server run by BotManager
117// and exposes the bot api.
118BrowserBot = function (name, callback) {
119 Bot.call(this, name, callback);
120 this.spawnBotProcess_();
121}
122
123BrowserBot.prototype = {
124 spawnBotProcess_: function () {
125 this.log('Spawning browser');
houssainy@google.com38ef6642014-09-04 13:44:47 +0000126 child.exec('google-chrome "http://localhost:8080/bot/"');
andresp@webrtc.org468516c2014-09-02 10:52:54 +0000127 },
128
129 __proto__: Bot.prototype
130}
131
houssainy@google.comc77e4d62014-09-08 10:36:11 +0000132AndroidDeviceManager = function () {
133 this.connectedDevices_ = [];
134}
135
136AndroidDeviceManager.prototype = {
137 getNewDevice: function (callback) {
138 this.listDevices_(function (devices) {
139 for (var i = 0; i < devices.length; i++) {
140 if (!this.connectedDevices_[devices[i]]) {
141 this.connectedDevices_[devices[i]] = devices[i];
142 callback(this.connectedDevices_[devices[i]]);
143 return;
144 }
145 }
146 if (devices.length == 0) {
147 console.log('Error: No connected devices!');
148 } else {
149 console.log('Error: There is no enough connected devices.');
150 }
151 process.exit(1);
152 }.bind(this));
153 },
154
155 listDevices_: function (callback) {
156 child.exec('adb devices' , function (error, stdout, stderr) {
157 var devices = [];
158 if (error || stderr) {
159 console.log('' + (error || stderr));
160 }
161 if (stdout) {
162 // The first line is "List of devices attached"
163 // and the following lines:
164 // <serial number> <device/emulator>
165 var tempList = ('' + stdout).split("\n").slice(1);
166 for (var i = 0; i < tempList.length; i++) {
167 if (tempList[i] == "") {
168 continue;
169 }
170 devices.push(tempList[i].split("\t")[0]);
171 }
172 }
173 callback(devices);
174 });
175 },
176}
andresp@webrtc.org468516c2014-09-02 10:52:54 +0000177module.exports = BotManager;