diff --git a/lib/tessel/deploy.js b/lib/tessel/deploy.js index bfb30b75..5ce7267a 100644 --- a/lib/tessel/deploy.js +++ b/lib/tessel/deploy.js @@ -168,28 +168,17 @@ actions.findProject = function(opts) { }); }; -actions.sendBundle = function(t, filepath, opts) { +actions.sendBundle = function(tessel, filepath, opts) { return new Promise(function(resolve, reject) { // Execute the remote untar process command - return t.connection.exec(commands.untarStdin(filepath)) + return tessel.connection.exec(commands.untarStdin(filepath)) // Once the process starts running .then(function(remoteProcess) { - var error = ''; - actions.findProject(opts).then(function(project) { opts.target = path.resolve(process.cwd(), project.pushdir); opts.resolvedEntryPoint = project.entryPoint; - // Collect error data as received - remoteProcess.stderr.on('data', function(data) { - error += data.toString(); - }); - actions.tarBundle(opts).then(function(bundle) { - if (error) { - return reject(error); - } - // RAM or Flash for log var memtype; if (opts.push) { @@ -199,13 +188,12 @@ actions.sendBundle = function(t, filepath, opts) { } // Log write - logs.info('Writing %s to %s on %s (%d kB)...', project.entryPoint, memtype, t.name, bundle.length / 1000); + logs.info('Writing %s to %s on %s (%d kB)...', project.entryPoint, memtype, tessel.name, bundle.length / 1000); - // Wait for the transfer to finish... - remoteProcess.once('close', function() { + tessel.forward(remoteProcess).then(function() { logs.info('Deployed.'); resolve(project.entryPoint); - }); + }).catch(reject); // Write the code bundle to the hardware remoteProcess.stdin.end(bundle); @@ -315,12 +303,12 @@ actions.writeToFile = function(t) { }); }; -actions.startPushedScript = function(t, entryPoint) { +actions.startPushedScript = function(tessel, entryPoint) { return new Promise(function(resolve) { // Once it has been written, run the script with Node - return t.connection.exec(commands.startPushedScript()) + return tessel.connection.exec(commands.startPushedScript()) .then(function(remoteProcess) { - remoteProcess.once('close', function() { + return tessel.forward(remoteProcess).then(function() { logs.info('Running %s...', entryPoint); return resolve(); }); diff --git a/lib/tessel/provision.js b/lib/tessel/provision.js index 0c41d9fc..eb13c9de 100644 --- a/lib/tessel/provision.js +++ b/lib/tessel/provision.js @@ -166,30 +166,16 @@ function copyKey(tessel, authFile, pubKey) { return new Promise(function(resolve, reject) { // Open up stdin to the authorized_keys file return tessel.connection.exec(commands.appendStdinToFile(authFile)) - .then(function(remoteProc) { - // Var to concat error data - var errBuf = ''; - // If error data comes in - remoteProc.stderr.on('data', function(err) { - // Save it - errBuf += err.toString(); - }); - // Handle the end of the stream - remoteProc.once('close', function() { - // If errors were logged - if (errBuf.length) { - // Report the failure - return reject(errBuf); - } else { - // Everything worked as expected - logs.info('Tessel authenticated with public key.'); - // End the process - return resolve(); - } - }); + .then(function(remoteProcess) { + tessel.forward(remoteProcess).then(function() { + // Everything worked as expected + logs.info('Tessel authenticated with public key.'); + // End the process + return resolve(); + }).catch(reject); // Send the key - remoteProc.stdin.end(pubKey); + remoteProcess.stdin.end(pubKey); }); }); } diff --git a/lib/tessel/tessel.js b/lib/tessel/tessel.js index b914e7e4..f648348a 100644 --- a/lib/tessel/tessel.js +++ b/lib/tessel/tessel.js @@ -69,40 +69,38 @@ Object.defineProperty(Tessel.prototype, 'connection', { } }); +Tessel.prototype.forward = function(remote) { + var error = ''; + var received = new Buffer(0); + + remote.stderr.on('data', function(buffer) { + error += buffer.toString(); + }); + + remote.stdout.on('data', function(buffer) { + received = Buffer.concat([received, buffer]); + }); + + return new Promise(function(resolve, reject) { + remote.once('close', function() { + if (error) { + return reject(new Error(error)); + } else { + return resolve(received); + } + }); + }); +}; + Tessel.prototype.simpleExec = function(command) { var self = this; return new Promise(function(resolve, reject) { // Stop processes and delete everything in the folder return self.connection.exec(command) .then(function(remoteProcess) { - - // Buffer to store incoming error data - var errBuf = new Buffer(0); - // If we receive error data - remoteProcess.stderr.on('data', function(e) { - // Concatenate the data - errBuf = Buffer.concat([errBuf, e]); - }); - - // Buffer to store incoming stdout data - var dataBuf = new Buffer(0); - // If we receive stdout data - remoteProcess.stdout.on('data', function(d) { - // Concatenate the data - dataBuf = Buffer.concat([dataBuf, d]); - }); - - // Once the process completes - remoteProcess.once('close', function() { - // Check if an error occurred - if (errBuf.length) { - return reject(new Error(errBuf.toString())); - } - // Assume it worked if there was no error - else { - resolve(dataBuf.toString()); - } - }); + return self.forward(remoteProcess).then(function(received) { + return resolve(received.toString()); + }).catch(reject); }); }); }; diff --git a/lib/tessel/wifi.js b/lib/tessel/wifi.js index ae38ae9f..50e1fca7 100644 --- a/lib/tessel/wifi.js +++ b/lib/tessel/wifi.js @@ -95,7 +95,7 @@ Tessel.prototype.connectToNetwork = function(opts) { return self.connection.exec(commands.reconnectWifi()) .then(function(remoteProcess) { // Once the wifi restart process closes - remoteProcess.once('close', function() { + return self.forward(remoteProcess).then(function() { logs.info('Credentials set!'); // End the connection return self.connection.end() diff --git a/test/unit/constructor.js b/test/unit/constructor.js new file mode 100644 index 00000000..795462a7 --- /dev/null +++ b/test/unit/constructor.js @@ -0,0 +1,157 @@ +var sinon = require('sinon'); +var Tessel = require('../../lib/tessel/tessel'); +var commands = require('../../lib/tessel/commands'); +var logs = require('../../lib/logs'); +var RemoteProcessSimulator = require('../common/remote-process-simulator'); +var TesselSimulator = require('../common/tessel-simulator'); + +exports['Tessel'] = { + setUp: function(done) { + done(); + }, + + tearDown: function(done) { + done(); + }, + + construction: function(test) { + test.expect(14); + + var usb = new Tessel({ + connectionType: 'USB' + }); + + var lan = new Tessel({ + connectionType: 'LAN' + }); + + test.deepEqual(usb.usbConnection, { connectionType: 'USB'}); + test.deepEqual(usb.connection, { connectionType: 'USB'}); + test.equal(usb.lanConnection, undefined); + + test.deepEqual(lan.lanConnection, { connectionType: 'LAN'}); + test.deepEqual(lan.connection, { connectionType: 'LAN'}); + test.equal(lan.usbConnection, undefined); + + var usbOwnProperties = Object.getOwnPropertyNames(usb); + var lanOwnProperties = Object.getOwnPropertyNames(lan); + + test.ok(usbOwnProperties.indexOf('addConnection') !== -1); + test.ok(usbOwnProperties.indexOf('closed') !== -1); + test.ok(usbOwnProperties.indexOf('name') !== -1); + test.ok(usbOwnProperties.indexOf('serialNumber') !== -1); + + test.ok(lanOwnProperties.indexOf('addConnection') !== -1); + test.ok(lanOwnProperties.indexOf('closed') !== -1); + test.ok(lanOwnProperties.indexOf('name') !== -1); + test.ok(lanOwnProperties.indexOf('serialNumber') !== -1); + + test.done(); + }, +}; + +exports['Tessel.prototype.forward'] = { + setUp: function(done) { + this.rps = new RemoteProcessSimulator(); + this.tessel = new Tessel({ + connectionType: 'USB' + }); + + done(); + }, + + tearDown: function(done) { + done(); + }, + + error: function(test) { + test.expect(1); + + this.tessel.forward(this.rps).catch(function(error) { + test.equal(error.message, 'Some Error'); + test.done(); + }); + + this.rps.stderr.emit('data', new Buffer('Some Error')); + this.rps.emit('close'); + }, + + dataReceived: function(test) { + test.expect(1); + + this.tessel.forward(this.rps).then(function(received) { + test.equal(received.toString(), 'Some Data'); + test.done(); + }); + + this.rps.stdout.emit('data', new Buffer('Some Data')); + this.rps.emit('close'); + }, +}; + +exports['Tessel.prototype.simpleExec'] = { + setUp: function(done) { + this.sandbox = sinon.sandbox.create(); + + this.rps = new RemoteProcessSimulator(); + this.tessel = new TesselSimulator(); + + this.forward = this.sandbox.spy(this.tessel, 'forward'); + this.connectionExec = this.sandbox.stub(this.tessel.connection, 'exec', function() { + return Promise.resolve(this.rps); + }.bind(this)); + + done(); + }, + + tearDown: function(done) { + this.sandbox.restore(); + done(); + }, + + callsConnectionExecWithCommand: function(test) { + test.expect(2); + + this.tessel.simpleExec('some command').then(function() { + test.equal(this.connectionExec.callCount, 1); + test.equal(this.forward.callCount, 1); + test.done(); + }.bind(this)); + + setImmediate(function() { + this.rps.emit('close'); + }.bind(this)); + }, + + callsConnectionExecWithCommandAndErrors: function(test) { + test.expect(3); + + this.tessel.simpleExec('some command').catch(function(error) { + test.equal(error.message, 'Some Error'); + test.equal(this.connectionExec.callCount, 1); + test.equal(this.forward.callCount, 1); + test.done(); + }.bind(this)); + + setImmediate(function() { + this.rps.stderr.emit('data', new Buffer('Some Error')); + this.rps.emit('close'); + }.bind(this)); + }, + + callsConnectionExecWithCommandAndDataReceived: function(test) { + test.expect(3); + + this.tessel.simpleExec('some command').then(function(received) { + test.equal(received, 'Some Data'); + test.equal(this.connectionExec.callCount, 1); + test.equal(this.forward.callCount, 1); + test.done(); + }.bind(this)); + + setImmediate(function() { + this.rps.stdout.emit('data', new Buffer('Some Data')); + this.rps.emit('close'); + }.bind(this)); + }, +};