From e128f2af13eff70a0a21e3d30d343bb6a181af0f Mon Sep 17 00:00:00 2001 From: Rick Waldron Date: Fri, 17 Jul 2015 12:31:48 -0400 Subject: [PATCH] Fix call to undefined logging function in usb_connection. - Make USBConnection testable --- lib/usb_connection.js | 42 ++++++---- test/unit/usb_connection.js | 163 ++++++++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+), 18 deletions(-) create mode 100644 test/unit/usb_connection.js diff --git a/lib/usb_connection.js b/lib/usb_connection.js index 5d94c802..e3cd33b5 100644 --- a/lib/usb_connection.js +++ b/lib/usb_connection.js @@ -17,17 +17,19 @@ var Daemon = require('./usb/usb_daemon'); var TESSEL_VID = 0x1209; var TESSEL_PID = 0x7551; -function USBConnection(device) { +var USB = {}; + +USB.Connection = function(device) { Duplex.call(this); this.device = device; this.connectionType = 'USB'; this.epIn = undefined; this.epOut = undefined; -} +}; -util.inherits(USBConnection, Duplex); +util.inherits(USB.Connection, Duplex); -USBConnection.prototype.exec = function(command, callback) { +USB.Connection.prototype.exec = function(command, callback) { var self = this; // Execute the command @@ -53,7 +55,7 @@ USBConnection.prototype.exec = function(command, callback) { }); }; -USBConnection.prototype._write = function(chunk, enc, callback) { +USB.Connection.prototype._write = function(chunk, enc, callback) { if (this.closed) { callback(new Error('Connection was already closed...')); @@ -62,13 +64,13 @@ USBConnection.prototype._write = function(chunk, enc, callback) { } }; -USBConnection.prototype._read = function() { +USB.Connection.prototype._read = function() { if (this.closed) { return this.push(null); } }; -USBConnection.prototype._receiveMessages = function() { +USB.Connection.prototype._receiveMessages = function() { var self = this; // Default transfer size var transferSize = 4096; @@ -85,14 +87,14 @@ USBConnection.prototype._receiveMessages = function() { return; } else { // Otherwise print the error - logs.error('Error reading USB message endpoint:', e); + logs.err('Error reading USB message endpoint:', e); // Return a non-zero return code process.exit(-5); } }); }; -USBConnection.prototype.open = function() { +USB.Connection.prototype.open = function() { var self = this; return new Promise(function(resolve, reject) { // Try to open connection @@ -143,7 +145,7 @@ USBConnection.prototype.open = function() { }); }; -USBConnection.prototype.end = function(callback) { +USB.Connection.prototype.end = function(callback) { var self = this; // Tell the USB daemon to end all processes active // on account of this connection @@ -155,7 +157,7 @@ USBConnection.prototype.end = function(callback) { }); }; -USBConnection.prototype._close = function(callback) { +USB.Connection.prototype._close = function(callback) { var self = this; if (self.closed) { @@ -171,7 +173,7 @@ USBConnection.prototype._close = function(callback) { }.bind(self)); }; -USBConnection.prototype._processArgsForTransport = function(command) { +USB.Connection.prototype._processArgsForTransport = function(command) { if (!Array.isArray(command)) { return; @@ -196,7 +198,7 @@ var scanner; function startScan() { if (scanner === undefined) { - scanner = new Scanner(); + scanner = new USB.Scanner(); setImmediate(scanner.start.bind(scanner)); } @@ -213,11 +215,11 @@ function stopScan() { return scanner; } -function Scanner() {} +USB.Scanner = function() {}; -util.inherits(Scanner, EventEmitter); +util.inherits(USB.Scanner, EventEmitter); -Scanner.prototype.start = function() { +USB.Scanner.prototype.start = function() { var self = this; usb.getDeviceList().forEach(deviceInspector); @@ -226,15 +228,19 @@ Scanner.prototype.start = function() { function deviceInspector(device) { if ((device.deviceDescriptor.idVendor === TESSEL_VID) && (device.deviceDescriptor.idProduct === TESSEL_PID)) { - var connection = new USBConnection(device); + var connection = new USB.Connection(device); self.emit('connection', connection); } } }; -Scanner.prototype.stop = function() { +USB.Scanner.prototype.stop = function() { usb.removeAllListeners('attach'); }; module.exports.startScan = startScan; module.exports.stopScan = stopScan; + +if (global.IS_TEST_ENV) { + module.exports.USB = USB; +} diff --git a/test/unit/usb_connection.js b/test/unit/usb_connection.js new file mode 100644 index 00000000..2c0f543c --- /dev/null +++ b/test/unit/usb_connection.js @@ -0,0 +1,163 @@ +var sinon = require('sinon'); +var Emitter = require('events').EventEmitter; +var Duplex = require('stream').Duplex; +var USB = require('../../lib/usb_connection').USB; +var Daemon = require('../../lib/usb/usb_daemon'); +var logs = require('../../lib/logs'); + +exports['USB.Connection'] = { + setUp: function(done) { + this.usbConnection = new USB.Connection({}); + done(); + }, + + tearDown: function(done) { + done(); + }, + + duplexSubclass: function(test) { + test.expect(1); + test.ok(this.usbConnection instanceof Duplex); + test.done(); + }, + + connectionType: function(test) { + test.expect(1); + test.equal(this.usbConnection.connectionType, 'USB'); + test.done(); + }, +}; + +exports['USB.Connection.prototype.exec'] = { + setUp: function(done) { + this.sandbox = sinon.sandbox.create(); + this.openProcess = this.sandbox.stub(Daemon, 'openProcess'); + this.usbConnection = new USB.Connection({}); + done(); + }, + + tearDown: function(done) { + this.sandbox.restore(); + done(); + }, + + openProcess: function(test) { + test.expect(1); + + this.usbConnection.exec(['some -command 1']); + + test.equal(this.openProcess.callCount, 1); + + // TODO: add more specific tests for handling the opened process + test.done(); + }, +}; + +exports['USB.Connection.prototype._write'] = { + setUp: function(done) { + this.sandbox = sinon.sandbox.create(); + this.openProcess = this.sandbox.stub(Daemon, 'openProcess'); + this.usbConnection = new USB.Connection({}); + this.usbConnection.epOut = new Emitter(); + this.usbConnection.epOut.transfer = this.sandbox.spy(); + done(); + }, + + tearDown: function(done) { + this.sandbox.restore(); + done(); + }, + + transfer: function(test) { + test.expect(3); + + var spy = this.sandbox.spy(); + + this.usbConnection._write(1, 2, spy); + + test.equal(this.usbConnection.epOut.transfer.callCount, 1); + test.equal(this.usbConnection.epOut.transfer.lastCall.args[0], 1); + test.equal(this.usbConnection.epOut.transfer.lastCall.args[1], spy); + + test.done(); + }, + + transferClosed: function(test) { + test.expect(2); + + var spy = this.sandbox.spy(); + + this.usbConnection.closed = true; + this.usbConnection._write(1, 2, spy); + + test.equal(this.usbConnection.epOut.transfer.callCount, 0); + test.equal(spy.callCount, 1); + + test.done(); + }, +}; + +exports['USB.Connection.prototype._receiveMessages'] = { + setUp: function(done) { + this.sandbox = sinon.sandbox.create(); + this.err = this.sandbox.stub(logs, 'err'); + this.processExit = this.sandbox.stub(process, 'exit'); + this.usbConnection = new USB.Connection({}); + this.usbConnection.epIn = new Emitter(); + this.usbConnection.epIn.startPoll = this.sandbox.spy(); + done(); + }, + + tearDown: function(done) { + this.sandbox.restore(); + done(); + }, + + startPolling: function(test) { + test.expect(3); + + this.usbConnection._receiveMessages(); + + test.equal(this.usbConnection.epIn.startPoll.callCount, 1); + test.equal(this.usbConnection.epIn.startPoll.lastCall.args[0], 2); + test.equal(this.usbConnection.epIn.startPoll.lastCall.args[1], 4096); + test.done(); + }, + + pushReceivedData: function(test) { + test.expect(1); + + this.usbConnection._receiveMessages(); + + this.usbConnection.epIn.emit('data', new Buffer([0xff, 0xff])); + test.equal(this.usbConnection._readableState.length, 2); + test.done(); + }, + + errorWhenStillOpen: function(test) { + test.expect(2); + + this.usbConnection._receiveMessages(); + + this.usbConnection.epIn.emit('error', 'oh no!'); + + test.equal(this.err.callCount, 1); + test.equal(this.processExit.callCount, 1); + test.done(); + }, + + errorWhenClosed: function(test) { + test.expect(2); + + this.usbConnection._receiveMessages(); + this.usbConnection.closed = true; + this.usbConnection.epIn.emit('error', 'oh no!'); + + // The immediate return prevents these from being + // called from in the error handler + test.equal(this.err.callCount, 0); + test.equal(this.processExit.callCount, 0); + test.done(); + }, + +};