blob: 4201237330ee9e6bae74b39a5d4f1bc3cc64a7b5 [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
31BotManager.prototype = {
32 spawnNewBot: function (name, callback) {
33 this.startWebSocketServer_();
34 var bot = new BrowserBot(name, callback);
35 this.bots_.push(bot);
36 this.pendingConnections_.push(bot.onBotConnected.bind(bot));
37 },
38
39 startWebSocketServer_: function () {
40 if (this.webSocketServer_) return;
41
42 this.app_ = new Express();
43
44 this.app_.use('/bot/browser/api.js',
45 this.serveBrowserifyFile_.bind(this,
46 __dirname + '/bot/browser/api.js'));
47
48 this.app_.use('/bot/browser/', Express.static(__dirname + '/bot/browser'));
49
50 this.server_ = http.createServer(this.app_);
51
52 this.webSocketServer_ = new WebSocketServer({ server: this.server_ });
53 this.webSocketServer_.on('connection', this.onConnection_.bind(this));
54
55 this.server_.listen(8080);
56 },
57
58 onConnection_: function (ws) {
59 var callback = this.pendingConnections_.shift();
60 callback(new WebSocketStream(ws));
61 },
62
63 serveBrowserifyFile_: function (file, request, result) {
64 // TODO(andresp): Cache browserify result for future serves.
65 var browserify = new Browserify();
66 browserify.add(file);
67 browserify.bundle().pipe(result);
68 }
69}
70
71// A basic bot waits for onBotConnected to be called with a stream to the actual
72// endpoint with the bot. Once that stream is available it establishes a dnode
73// connection and calls the callback with the other endpoint interface so the
74// test can interact with it.
75Bot = function (name, callback) {
76 this.name_ = name;
77 this.onbotready_ = callback;
78}
79
80Bot.prototype = {
81 log: function (msg) {
82 console.log("bot:" + this.name_ + " > " + msg);
83 },
84
85 name: function () { return this.name_; },
86
87 onBotConnected: function (stream) {
88 this.log('Connected');
89 this.stream_ = stream;
90 this.dnode_ = new Dnode();
91 this.dnode_.on('remote', this.onRemoteFromDnode_.bind(this));
92 this.dnode_.pipe(this.stream_).pipe(this.dnode_);
93 },
94
95 onRemoteFromDnode_: function (remote) {
96 this.onbotready_(remote);
97 }
98}
99
100// BrowserBot spawns a process to open "http://localhost:8080/bot/browser/".
101//
102// That page once loaded, connects to the websocket server run by BotManager
103// and exposes the bot api.
104BrowserBot = function (name, callback) {
105 Bot.call(this, name, callback);
106 this.spawnBotProcess_();
107}
108
109BrowserBot.prototype = {
110 spawnBotProcess_: function () {
111 this.log('Spawning browser');
112 child.exec('google-chrome "http://localhost:8080/bot/browser/"');
113 },
114
115 __proto__: Bot.prototype
116}
117
118module.exports = BotManager;