Split apart MCP, API manager, exports
This commit is contained in:
parent
455c06ab91
commit
def91ffe53
|
@ -0,0 +1,43 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function lib(path) { return require("./lib/" + path); }
|
||||||
|
|
||||||
|
var Config = lib("config"),
|
||||||
|
MCP = lib("mcp"),
|
||||||
|
API = lib("api");
|
||||||
|
|
||||||
|
var exports = module.exports = {};
|
||||||
|
|
||||||
|
exports.MCP = lib("mcp");
|
||||||
|
exports.Robot = lib("robot");
|
||||||
|
exports.Driver = lib("driver");
|
||||||
|
exports.Adaptor = lib("adaptor");
|
||||||
|
|
||||||
|
exports.Utils = lib("utils");
|
||||||
|
exports.Logger = lib("logger");
|
||||||
|
|
||||||
|
exports.IO = {
|
||||||
|
DigitalPin: lib("io/digital-pin"),
|
||||||
|
Utils: lib("io/utils")
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.robot = MCP.create;
|
||||||
|
exports.start = MCP.start;
|
||||||
|
exports.halt = MCP.halt;
|
||||||
|
|
||||||
|
exports.api = API.create;
|
||||||
|
|
||||||
|
exports.config = Config.update;
|
||||||
|
|
||||||
|
process.on("SIGINT", function() {
|
||||||
|
exports.halt(function() {
|
||||||
|
process.kill(process.pid);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
var io = { input: process.stdin, output: process.stdout },
|
||||||
|
quit = process.emit.bind(process, "SIGINT");
|
||||||
|
|
||||||
|
require("readline").createInterface(io).on("SIGINT", quit);
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var MCP = require("./mcp"),
|
||||||
|
Logger = require("./logger"),
|
||||||
|
_ = require("./utils/helpers");
|
||||||
|
|
||||||
|
var api = module.exports = {};
|
||||||
|
|
||||||
|
api.instances = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new API instance
|
||||||
|
*
|
||||||
|
* @param {String} [Server] which API plugin to use (e.g. "http" loads
|
||||||
|
* cylon-api-http)
|
||||||
|
* @param {Object} opts options for the new API instance
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
api.create = function create(Server, opts) {
|
||||||
|
// if only passed options (or nothing), assume HTTP server
|
||||||
|
if (Server == null || _.isObject(Server) && !_.isFunction(Server)) {
|
||||||
|
opts = Server;
|
||||||
|
Server = "http";
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = opts || {};
|
||||||
|
|
||||||
|
if (_.isString(Server)) {
|
||||||
|
var req = "cylon-api-" + Server;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Server = require(req);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.code !== "MODULE_NOT_FOUND") {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
[
|
||||||
|
"Cannot find the " + req + " API module.",
|
||||||
|
"You may be able to install it: `npm install " + req + "`"
|
||||||
|
].forEach(_.arity(Logger.fatal, 1));
|
||||||
|
|
||||||
|
throw new Error("Missing API plugin - cannot proceed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.mcp = MCP;
|
||||||
|
|
||||||
|
var instance = new Server(opts);
|
||||||
|
api.instances.push(instance);
|
||||||
|
instance.start();
|
||||||
|
};
|
|
@ -7,13 +7,14 @@ var config = module.exports = {},
|
||||||
|
|
||||||
// default data
|
// default data
|
||||||
config.logging = {};
|
config.logging = {};
|
||||||
|
config.haltTimeout = 3000;
|
||||||
config.testMode = false;
|
config.testMode = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the Config, and triggers handler callbacks
|
* Updates the Config, and triggers handler callbacks
|
||||||
*
|
*
|
||||||
* @param {Object} data new configuration information to set
|
* @param {Object} data new configuration information to set
|
||||||
* @return {void}
|
* @return {Object} the updated configuration
|
||||||
*/
|
*/
|
||||||
config.update = function update(data) {
|
config.update = function update(data) {
|
||||||
var forbidden = ["update", "subscribe", "unsubscribe"];
|
var forbidden = ["update", "subscribe", "unsubscribe"];
|
||||||
|
@ -23,12 +24,14 @@ config.update = function update(data) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!Object.keys(data).length) {
|
if (!Object.keys(data).length) {
|
||||||
return;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
_.extend(config, data);
|
_.extend(config, data);
|
||||||
|
|
||||||
callbacks.forEach(function(callback) { callback(data); });
|
callbacks.forEach(function(callback) { callback(data); });
|
||||||
|
|
||||||
|
return config;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
164
lib/cylon.js
164
lib/cylon.js
|
@ -1,164 +0,0 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var Logger = require("./logger"),
|
|
||||||
Robot = require("./robot"),
|
|
||||||
Config = require("./config"),
|
|
||||||
Utils = require("./utils"),
|
|
||||||
_ = require("./utils/helpers");
|
|
||||||
|
|
||||||
var EventEmitter = require("events").EventEmitter;
|
|
||||||
|
|
||||||
var Cylon = module.exports = new EventEmitter();
|
|
||||||
|
|
||||||
Cylon.Logger = Logger;
|
|
||||||
Cylon.Driver = require("./driver");
|
|
||||||
Cylon.Adaptor = require("./adaptor");
|
|
||||||
Cylon.Utils = Utils;
|
|
||||||
|
|
||||||
Cylon.IO = {
|
|
||||||
DigitalPin: require("./io/digital-pin"),
|
|
||||||
Utils: require("./io/utils")
|
|
||||||
};
|
|
||||||
|
|
||||||
Cylon.apiInstances = [];
|
|
||||||
|
|
||||||
Cylon.robots = {};
|
|
||||||
Cylon.commands = {};
|
|
||||||
|
|
||||||
Cylon.events = [ "robot_added", "robot_removed" ];
|
|
||||||
|
|
||||||
// Public: Creates a new Robot
|
|
||||||
//
|
|
||||||
// opts - hash of Robot attributes
|
|
||||||
//
|
|
||||||
// Returns a shiny new Robot
|
|
||||||
// Examples:
|
|
||||||
// Cylon.robot
|
|
||||||
// connection: { name: "arduino", adaptor: "firmata" }
|
|
||||||
// device: { name: "led", driver: "led", pin: 13 }
|
|
||||||
//
|
|
||||||
// work: (me) ->
|
|
||||||
// me.led.toggle()
|
|
||||||
Cylon.robot = function robot(opts) {
|
|
||||||
opts = opts || {};
|
|
||||||
|
|
||||||
// check if a robot with the same name exists already
|
|
||||||
if (opts.name && this.robots[opts.name]) {
|
|
||||||
var original = opts.name;
|
|
||||||
opts.name = Utils.makeUnique(original, Object.keys(this.robots));
|
|
||||||
|
|
||||||
var str = "Robot names must be unique. Renaming '";
|
|
||||||
str += original + "' to '" + opts.name + "'";
|
|
||||||
|
|
||||||
Logger.warn(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
var bot = new Robot(opts);
|
|
||||||
this.robots[bot.name] = bot;
|
|
||||||
this.emit("robot_added", bot.name);
|
|
||||||
|
|
||||||
return bot;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Public: Initializes an API instance based on provided options.
|
|
||||||
//
|
|
||||||
// Returns nothing
|
|
||||||
Cylon.api = function api(Server, opts) {
|
|
||||||
// if only passed options (or nothing), assume HTTP server
|
|
||||||
if (Server == null || _.isObject(Server) && !_.isFunction(Server)) {
|
|
||||||
opts = Server;
|
|
||||||
Server = "http";
|
|
||||||
}
|
|
||||||
|
|
||||||
opts = opts || {};
|
|
||||||
|
|
||||||
if (_.isString(Server)) {
|
|
||||||
var req = "cylon-api-" + Server;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Server = require(req);
|
|
||||||
} catch (e) {
|
|
||||||
if (e.code !== "MODULE_NOT_FOUND") {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
[
|
|
||||||
"Cannot find the " + req + " API module.",
|
|
||||||
"You may be able to install it: `npm install " + req + "`"
|
|
||||||
].forEach(_.arity(Logger.fatal, 1));
|
|
||||||
|
|
||||||
throw new Error("Missing API plugin - cannot proceed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
opts.mcp = this;
|
|
||||||
var instance = new Server(opts);
|
|
||||||
this.apiInstances.push(instance);
|
|
||||||
instance.start();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Public: Starts up the API and the robots
|
|
||||||
//
|
|
||||||
// Returns nothing
|
|
||||||
Cylon.start = function start() {
|
|
||||||
var starters = _.pluck(this.robots, "start");
|
|
||||||
|
|
||||||
_.parallel(starters, function() {
|
|
||||||
var mode = Utils.fetch(Config, "workMode", "async");
|
|
||||||
|
|
||||||
if (mode === "sync") {
|
|
||||||
_.invoke(this.robots, "startWork");
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Public: Sets the internal configuration, based on passed options
|
|
||||||
//
|
|
||||||
// opts - object containing configuration key/value pairs
|
|
||||||
//
|
|
||||||
// Returns the current config
|
|
||||||
Cylon.config = function(opts) {
|
|
||||||
if (_.isObject(opts)) { Config.update(opts); }
|
|
||||||
return Config;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Public: Halts the API and the robots
|
|
||||||
//
|
|
||||||
// callback - callback to be triggered when Cylon is ready to shutdown
|
|
||||||
//
|
|
||||||
// Returns nothing
|
|
||||||
Cylon.halt = function halt(callback) {
|
|
||||||
callback = callback || function() {};
|
|
||||||
|
|
||||||
var fns = _.pluck(this.robots, "halt");
|
|
||||||
|
|
||||||
// if robots can"t shut down quickly enough, forcefully self-terminate
|
|
||||||
var timeout = Config.haltTimeout || 3000;
|
|
||||||
Utils.after(timeout, callback);
|
|
||||||
|
|
||||||
_.parallel(fns, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Cylon.toJSON = function() {
|
|
||||||
return {
|
|
||||||
robots: _.invoke(this.robots, "toJSON"),
|
|
||||||
commands: Object.keys(this.commands),
|
|
||||||
events: this.events
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
if (process.platform === "win32") {
|
|
||||||
var readline = require("readline");
|
|
||||||
|
|
||||||
var io = { input: process.stdin, output: process.stdout };
|
|
||||||
|
|
||||||
readline.createInterface(io).on("SIGINT", function() {
|
|
||||||
process.emit("SIGINT");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
process.on("SIGINT", function() {
|
|
||||||
Cylon.halt(function() {
|
|
||||||
process.kill(process.pid);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var EventEmitter = require("events").EventEmitter;
|
||||||
|
|
||||||
|
var Config = require("./config"),
|
||||||
|
Logger = require("./logger"),
|
||||||
|
Utils = require("./utils"),
|
||||||
|
Robot = require("./robot"),
|
||||||
|
_ = require("./utils/helpers");
|
||||||
|
|
||||||
|
var mcp = module.exports = new EventEmitter();
|
||||||
|
|
||||||
|
mcp.robots = {};
|
||||||
|
mcp.commands = {};
|
||||||
|
mcp.events = [ "robot_added", "robot_removed" ];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Robot with the provided options.
|
||||||
|
*
|
||||||
|
* @param {Object} opts robot options
|
||||||
|
* @return {Robot} the new robot
|
||||||
|
*/
|
||||||
|
mcp.create = function create(opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
|
||||||
|
// check if a robot with the same name exists already
|
||||||
|
if (opts.name && mcp.robots[opts.name]) {
|
||||||
|
var original = opts.name;
|
||||||
|
opts.name = Utils.makeUnique(original, Object.keys(mcp.robots));
|
||||||
|
|
||||||
|
var str = "Robot names must be unique. Renaming '";
|
||||||
|
str += original + "' to '" + opts.name + "'";
|
||||||
|
|
||||||
|
Logger.warn(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
var bot = new Robot(opts);
|
||||||
|
mcp.robots[bot.name] = bot;
|
||||||
|
mcp.emit("robot_added", bot.name);
|
||||||
|
|
||||||
|
return bot;
|
||||||
|
};
|
||||||
|
|
||||||
|
mcp.start = function start(callback) {
|
||||||
|
var fns = _.pluck(mcp.robots, "start");
|
||||||
|
|
||||||
|
_.parallel(fns, function() {
|
||||||
|
var mode = Utils.fetch(Config, "workMode", "async");
|
||||||
|
if (mode === "sync") { _.invoke(mcp.robots, "startWork"); }
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Halts all MCP robots.
|
||||||
|
*
|
||||||
|
* @param {Function} callback function to call when done halting robots
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
mcp.halt = function halt(callback) {
|
||||||
|
callback = callback || function() {};
|
||||||
|
|
||||||
|
var timeout = setTimeout(callback, Config.haltTimeout || 3000);
|
||||||
|
|
||||||
|
_.parallel(_.pluck(mcp.robots, "halt"), function() {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes MCP robots, commands, and events into a JSON-serializable Object.
|
||||||
|
*
|
||||||
|
* @return {Object} a serializable representation of the MCP
|
||||||
|
*/
|
||||||
|
mcp.toJSON = function() {
|
||||||
|
return {
|
||||||
|
robots: _.invoke(mcp.robots, "toJSON"),
|
||||||
|
commands: Object.keys(mcp.commands),
|
||||||
|
events: mcp.events
|
||||||
|
};
|
||||||
|
};
|
|
@ -211,6 +211,8 @@ function parallel(functions, done) {
|
||||||
results = [],
|
results = [],
|
||||||
error;
|
error;
|
||||||
|
|
||||||
|
if (typeof done !== "function") { done = function() {}; }
|
||||||
|
|
||||||
function callback(err, result) {
|
function callback(err, result) {
|
||||||
if (error) {
|
if (error) {
|
||||||
return;
|
return;
|
||||||
|
@ -243,6 +245,8 @@ function series(functions, done) {
|
||||||
var results = [],
|
var results = [],
|
||||||
error;
|
error;
|
||||||
|
|
||||||
|
if (typeof done !== "function") { done = function() {}; }
|
||||||
|
|
||||||
function callback(err, result) {
|
function callback(err, result) {
|
||||||
if (err || error) {
|
if (err || error) {
|
||||||
error = err;
|
error = err;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cylon",
|
"name": "cylon",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "lib/cylon.js",
|
|
||||||
"description": "A JavaScript robotics framework for Node.js",
|
"description": "A JavaScript robotics framework for Node.js",
|
||||||
"homepage": "http://cylonjs.com",
|
"homepage": "http://cylonjs.com",
|
||||||
"bugs": "https://github.com/hybridgroup/cylon/issues",
|
"bugs": "https://github.com/hybridgroup/cylon/issues",
|
||||||
|
@ -37,7 +36,8 @@
|
||||||
"hardware": {
|
"hardware": {
|
||||||
"*": false,
|
"*": false,
|
||||||
"./": false,
|
"./": false,
|
||||||
"./lib": true
|
"./lib": true,
|
||||||
|
"index.js": true
|
||||||
},
|
},
|
||||||
|
|
||||||
"engines" : {
|
"engines" : {
|
||||||
|
|
|
@ -26,7 +26,7 @@ global.lib = function(module) {
|
||||||
return require(path.normalize("./../lib/" + module));
|
return require(path.normalize("./../lib/" + module));
|
||||||
};
|
};
|
||||||
|
|
||||||
var Cylon = lib("cylon");
|
var Cylon = require("./../");
|
||||||
|
|
||||||
Cylon.config({
|
Cylon.config({
|
||||||
mode: "manual",
|
mode: "manual",
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var API = lib("api"),
|
||||||
|
MCP = lib("mcp");
|
||||||
|
|
||||||
|
describe("API", function() {
|
||||||
|
describe("#create", function() {
|
||||||
|
afterEach(function() {
|
||||||
|
API.instances = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
context("with a provided API server and opts", function() {
|
||||||
|
var Server, opts, instance;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
instance = { start: spy() };
|
||||||
|
opts = { https: false };
|
||||||
|
Server = stub().returns(instance);
|
||||||
|
|
||||||
|
API.create(Server, opts);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates an API instance", function() {
|
||||||
|
expect(Server).to.be.calledWithNew;
|
||||||
|
expect(Server).to.be.calledWith(opts);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes MCP through to the instance as opts.mcp", function() {
|
||||||
|
expect(opts.mcp).to.be.eql(MCP);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("stores the API instance in @instances", function() {
|
||||||
|
expect(API.instances).to.be.eql([instance]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("tells the API instance to start", function() {
|
||||||
|
expect(instance.start).to.be.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,186 +1,78 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var Cylon = lib("cylon"),
|
var Cylon = lib("../index");
|
||||||
Robot = lib("robot");
|
|
||||||
|
|
||||||
var Logger = lib("logger"),
|
var MCP = lib("mcp"),
|
||||||
Adaptor = lib("adaptor"),
|
API = lib("api"),
|
||||||
|
Robot = lib("robot"),
|
||||||
Driver = lib("driver"),
|
Driver = lib("driver"),
|
||||||
Config = lib("config");
|
Adaptor = lib("adaptor"),
|
||||||
|
Utils = lib("utils"),
|
||||||
|
Config = lib("config"),
|
||||||
|
Logger = lib("logger");
|
||||||
|
|
||||||
|
var IO = {
|
||||||
|
DigitalPin: lib("io/digital-pin"),
|
||||||
|
Utils: lib("io/utils")
|
||||||
|
};
|
||||||
|
|
||||||
describe("Cylon", function() {
|
describe("Cylon", function() {
|
||||||
describe("exports", function() {
|
it("exports the MCP as Cylon.MCP", function() {
|
||||||
it("sets Logger to the Logger module", function() {
|
expect(Cylon.MCP).to.be.eql(MCP);
|
||||||
expect(Cylon.Logger).to.be.eql(Logger);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it("sets Adaptor to the Adaptor module", function() {
|
it("exports the Robot as Cylon.Robot", function() {
|
||||||
expect(Cylon.Adaptor).to.be.eql(Adaptor);
|
expect(Cylon.Robot).to.be.eql(Robot);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sets Driver to the Driver module", function() {
|
it("exports the Driver as Cylon.Driver", function() {
|
||||||
expect(Cylon.Driver).to.be.eql(Driver);
|
expect(Cylon.Driver).to.be.eql(Driver);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sets @apiInstances to an empty array by default", function() {
|
it("exports the Adaptor as Cylon.Adaptor", function() {
|
||||||
expect(Cylon.apiInstances).to.be.eql([]);
|
expect(Cylon.Adaptor).to.be.eql(Adaptor);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sets @robots to an empty object by default", function() {
|
it("exports the Utils as Cylon.Utils", function() {
|
||||||
expect(Cylon.robots).to.be.eql({});
|
expect(Cylon.Utils).to.be.eql(Utils);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sets @robots to an empty object by default", function() {
|
|
||||||
expect(Cylon.commands).to.be.eql({});
|
it("exports the Logger as Cylon.Logger", function() {
|
||||||
});
|
expect(Cylon.Logger).to.be.eql(Logger);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("exports the IO DigitalPin and Utils as Cylon.IO", function() {
|
||||||
|
expect(Cylon.IO).to.be.eql(IO);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#robot", function() {
|
describe("#robot", function() {
|
||||||
afterEach(function() {
|
it("proxies to MCP.create", function() {
|
||||||
Cylon.robots = {};
|
expect(Cylon.robot).to.be.eql(MCP.create);
|
||||||
});
|
|
||||||
|
|
||||||
it("uses passed options to create a new Robot", function() {
|
|
||||||
var opts = { name: "Ultron" };
|
|
||||||
var robot = Cylon.robot(opts);
|
|
||||||
|
|
||||||
expect(robot.toString()).to.be.eql("[Robot name='Ultron']");
|
|
||||||
expect(Cylon.robots.Ultron).to.be.eql(robot);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("avoids duplicating names", function() {
|
|
||||||
Cylon.robot({ name: "Ultron" });
|
|
||||||
Cylon.robot({ name: "Ultron" });
|
|
||||||
|
|
||||||
var bots = Object.keys(Cylon.robots);
|
|
||||||
expect(bots).to.be.eql(["Ultron", "Ultron-1"]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#api", function() {
|
|
||||||
afterEach(function() {
|
|
||||||
Cylon.apiInstances = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
context("with a provided API server and opts", function() {
|
|
||||||
var API, opts, instance;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
instance = { start: spy() };
|
|
||||||
opts = { https: false };
|
|
||||||
API = stub().returns(instance);
|
|
||||||
|
|
||||||
Cylon.api(API, opts);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("creates an API instance", function() {
|
|
||||||
expect(API).to.be.calledWithNew;
|
|
||||||
expect(API).to.be.calledWith(opts);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("passes Cylon through to the instance as opts.mcp", function() {
|
|
||||||
expect(opts.mcp).to.be.eql(Cylon);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("stores the API instance in @apiInstances", function() {
|
|
||||||
expect(Cylon.apiInstances).to.be.eql([instance]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("tells the API instance to start", function() {
|
|
||||||
expect(instance.start).to.be.called;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#start", function() {
|
describe("#start", function() {
|
||||||
it("calls #start() on all robots", function() {
|
it("proxies to MCP.start", function() {
|
||||||
var bot1 = { start: spy() },
|
expect(Cylon.start).to.be.eql(MCP.start);
|
||||||
bot2 = { start: spy() };
|
|
||||||
|
|
||||||
Cylon.robots = {
|
|
||||||
bot1: bot1,
|
|
||||||
bot2: bot2
|
|
||||||
};
|
|
||||||
|
|
||||||
Cylon.start();
|
|
||||||
|
|
||||||
expect(bot1.start).to.be.called;
|
|
||||||
expect(bot2.start).to.be.called;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#config", function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
delete Config.a;
|
|
||||||
delete Config.b;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets config variables", function() {
|
|
||||||
Cylon.config({ a: 1, b: 2 });
|
|
||||||
expect(Config.a).to.be.eql(1);
|
|
||||||
expect(Config.b).to.be.eql(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates existing config", function() {
|
|
||||||
Cylon.config({ a: 1, b: 2 });
|
|
||||||
Cylon.config({ a: 3 });
|
|
||||||
expect(Config.a).to.be.eql(3);
|
|
||||||
expect(Config.b).to.be.eql(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns updated config", function() {
|
|
||||||
var config = Cylon.config({ a: 1, b: 2 });
|
|
||||||
expect(Config).to.be.eql(config);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("doesn't ignores non-object arguments", function() {
|
|
||||||
var config = Cylon.config({ a: 1, b: 2 });
|
|
||||||
Cylon.config(["a", 1, "b", 2]);
|
|
||||||
Cylon.config("hello world");
|
|
||||||
expect(Config).to.be.eql(config);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#halt", function() {
|
describe("#halt", function() {
|
||||||
it("calls #halt() on all robots", function() {
|
it("proxies to MCP.halt", function() {
|
||||||
var bot1 = { halt: spy() },
|
expect(Cylon.halt).to.be.eql(MCP.halt);
|
||||||
bot2 = { halt: spy() };
|
|
||||||
|
|
||||||
Cylon.robots = {
|
|
||||||
bot1: bot1,
|
|
||||||
bot2: bot2
|
|
||||||
};
|
|
||||||
|
|
||||||
Cylon.halt();
|
|
||||||
|
|
||||||
expect(bot1.halt).to.be.called;
|
|
||||||
expect(bot2.halt).to.be.called;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#toJSON", function() {
|
describe("#api", function() {
|
||||||
var json, bot1, bot2;
|
it("proxies to API.create", function() {
|
||||||
|
expect(Cylon.api).to.be.eql(API.create);
|
||||||
beforeEach(function() {
|
|
||||||
bot1 = new Robot();
|
|
||||||
bot2 = new Robot();
|
|
||||||
|
|
||||||
Cylon.robots = { bot1: bot1, bot2: bot2 };
|
|
||||||
Cylon.commands.echo = function(arg) { return arg; };
|
|
||||||
|
|
||||||
json = Cylon.toJSON();
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("contains all robots the MCP knows about", function() {
|
describe("#config", function() {
|
||||||
expect(json.robots).to.be.eql([bot1.toJSON(), bot2.toJSON()]);
|
it("proxies to Config.update", function() {
|
||||||
});
|
expect(Cylon.config).to.be.eql(Config.update);
|
||||||
|
|
||||||
it("contains an array of MCP commands", function() {
|
|
||||||
expect(json.commands).to.be.eql(["echo"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("contains an array of MCP events", function() {
|
|
||||||
expect(json.events).to.be.eql(["robot_added", "robot_removed"]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var MCP = lib("mcp"),
|
||||||
|
Robot = lib("robot");
|
||||||
|
|
||||||
|
describe("MCP", function() {
|
||||||
|
it("contains a collection of robots", function() {
|
||||||
|
expect(MCP.robots).to.be.eql({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("contains a collection of commands", function() {
|
||||||
|
expect(MCP.commands).to.be.eql({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("contains a collection of events", function() {
|
||||||
|
expect(MCP.events).to.be.eql(["robot_added", "robot_removed"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#create", function() {
|
||||||
|
afterEach(function() {
|
||||||
|
MCP.robots = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses passed options to create a new Robot", function() {
|
||||||
|
var opts = { name: "Ultron" };
|
||||||
|
var robot = MCP.create(opts);
|
||||||
|
|
||||||
|
expect(robot.toString()).to.be.eql("[Robot name='Ultron']");
|
||||||
|
expect(MCP.robots.Ultron).to.be.eql(robot);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("avoids duplicating names", function() {
|
||||||
|
MCP.create({ name: "Ultron" });
|
||||||
|
MCP.create({ name: "Ultron" });
|
||||||
|
|
||||||
|
var bots = Object.keys(MCP.robots);
|
||||||
|
expect(bots).to.be.eql(["Ultron", "Ultron-1"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#start", function() {
|
||||||
|
it("calls #start() on all robots", function() {
|
||||||
|
var bot1 = { start: spy() },
|
||||||
|
bot2 = { start: spy() };
|
||||||
|
|
||||||
|
MCP.robots = {
|
||||||
|
bot1: bot1,
|
||||||
|
bot2: bot2
|
||||||
|
};
|
||||||
|
|
||||||
|
MCP.start();
|
||||||
|
|
||||||
|
expect(bot1.start).to.be.called;
|
||||||
|
expect(bot2.start).to.be.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#halt", function() {
|
||||||
|
it("calls #halt() on all robots", function() {
|
||||||
|
var bot1 = { halt: spy() },
|
||||||
|
bot2 = { halt: spy() };
|
||||||
|
|
||||||
|
MCP.robots = {
|
||||||
|
bot1: bot1,
|
||||||
|
bot2: bot2
|
||||||
|
};
|
||||||
|
|
||||||
|
MCP.halt();
|
||||||
|
|
||||||
|
expect(bot1.halt).to.be.called;
|
||||||
|
expect(bot2.halt).to.be.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#toJSON", function() {
|
||||||
|
var json, bot1, bot2;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
bot1 = new Robot();
|
||||||
|
bot2 = new Robot();
|
||||||
|
|
||||||
|
MCP.robots = { bot1: bot1, bot2: bot2 };
|
||||||
|
MCP.commands.echo = function(arg) { return arg; };
|
||||||
|
|
||||||
|
json = MCP.toJSON();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("contains all robots the MCP knows about", function() {
|
||||||
|
expect(json.robots).to.be.eql([bot1.toJSON(), bot2.toJSON()]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("contains an array of MCP commands", function() {
|
||||||
|
expect(json.commands).to.be.eql(["echo"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("contains an array of MCP events", function() {
|
||||||
|
expect(json.events).to.be.eql(["robot_added", "robot_removed"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue