From a9382e93c3dd2c537c3f947bb4833b8e31402ab0 Mon Sep 17 00:00:00 2001 From: HipsterBrown Date: Thu, 1 Sep 2016 15:07:09 -0400 Subject: [PATCH 1/5] feat(adhoc): adds adhoc command, configuration --- bin/tessel-2.js | 40 ++++++++++++++++++++++++++++++++++++++ lib/tessel/access-point.js | 9 ++++++--- lib/tessel/commands.js | 4 ++-- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/bin/tessel-2.js b/bin/tessel-2.js index 1a0f22d0..43cecfc2 100755 --- a/bin/tessel-2.js +++ b/bin/tessel-2.js @@ -503,6 +503,46 @@ makeCommand('version') makeCommand('ap') .callback(options => { + options.mode = 'ap'; + log.level(options.loglevel); + + if (options.on || options.off) { + if (options.on) { + callControllerWith('enableAccessPoint', options); + } else { + callControllerWith('disableAccessPoint', options); + } + } else if (options.ssid) { + callControllerWith('createAccessPoint', options); + } else { + callControllerWith('getAccessPointInfo', options); + } + }) + .option('ssid', { + abbr: 'n', + help: 'Name of the network.' + }) + .option('password', { + abbr: 'p', + help: 'Password to access network.' + }) + .option('security', { + abbr: 's', + help: 'Encryption to use on network (i.e. wep, psk, psk2, wpa, wpa2).' + }) + .option('off', { + flag: true, + help: 'Disable the access point' + }) + .option('on', { + flag: true, + help: 'Enable the access point' + }) + .help('Configure the Tessel as an access point'); + +makeCommand('adhoc') + .callback(options => { + options.mode = 'adhoc'; log.level(options.loglevel); if (options.on || options.off) { diff --git a/lib/tessel/access-point.js b/lib/tessel/access-point.js index 4f1d93d8..ebc03df7 100644 --- a/lib/tessel/access-point.js +++ b/lib/tessel/access-point.js @@ -84,7 +84,7 @@ Tessel.prototype.getAccessPointInfo = function() { info = { ssid: values[0], - key: values[1], + key: values[2] !== 'none' ? values[1] : null, // the password for a previous configuration could still exist, so omit this info if the encryption is 'none' encryption: values[2], disabled: values[3] }; @@ -104,6 +104,7 @@ Tessel.prototype.createAccessPoint = function(opts) { var ssid = opts.ssid; var password = opts.pass; var security = opts.security; + var mode = opts.mode; var status = 'Created Access Point successfully. '; @@ -125,6 +126,8 @@ Tessel.prototype.createAccessPoint = function(opts) { var setAccessPointSecurity = () => this.simpleExec(commands.setAccessPointSecurity(security)); + var setAccessPointMode = () => this.simpleExec(commands.setAccessPointMode(mode)); + var turnAccessPointOn = () => this.simpleExec(commands.turnAccessPointOn()); var commitAndClosePromise = () => { @@ -143,6 +146,7 @@ Tessel.prototype.createAccessPoint = function(opts) { }; return setup() + .then(setAccessPointMode) .then(turnAccessPointOn) .then(commitAndClosePromise); }; @@ -160,8 +164,7 @@ Tessel.prototype.createAccessPoint = function(opts) { var setAccessPoint = () => { return this.simpleExec(commands.setAccessPoint()) .then(() => this.simpleExec(commands.setAccessPointDevice())) - .then(() => this.simpleExec(commands.setAccessPointNetwork())) - .then(() => this.simpleExec(commands.setAccessPointMode())); + .then(() => this.simpleExec(commands.setAccessPointNetwork())); }; var setLanNetwork = () => { diff --git a/lib/tessel/commands.js b/lib/tessel/commands.js index 7920056f..13398a47 100644 --- a/lib/tessel/commands.js +++ b/lib/tessel/commands.js @@ -149,8 +149,8 @@ module.exports.setAccessPointDevice = function() { module.exports.setAccessPointNetwork = function() { return ['uci', 'set', 'wireless.@wifi-iface[1].network=lan']; }; -module.exports.setAccessPointMode = function() { - return ['uci', 'set', 'wireless.@wifi-iface[1].mode=ap']; +module.exports.setAccessPointMode = function(mode) { + return ['uci', 'set', `wireless.@wifi-iface[1].mode=${mode}`]; }; module.exports.setAccessPointSSID = function(ssid) { return ['uci', 'set', 'wireless.@wifi-iface[1].ssid=' + ssid]; From 7ad24447f75e12f744972b75a5d677f3fa81c69b Mon Sep 17 00:00:00 2001 From: HipsterBrown Date: Thu, 1 Sep 2016 17:03:05 -0400 Subject: [PATCH 2/5] fix(ap): use 'password' option instead of 'pass' --- bin/tessel-2.js | 8 ++++---- lib/tessel/access-point.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/tessel-2.js b/bin/tessel-2.js index 43cecfc2..43b6ffe8 100755 --- a/bin/tessel-2.js +++ b/bin/tessel-2.js @@ -567,17 +567,17 @@ makeCommand('adhoc') }) .option('security', { abbr: 's', - help: 'Encryption to use on network (i.e. wep, psk, psk2, wpa, wpa2).' + help: 'Encryption used on network (i.e. wep, psk, psk2, wpa, wpa2).' }) .option('off', { flag: true, - help: 'Disable the access point' + help: 'Disable the adhoc network' }) .option('on', { flag: true, - help: 'Enable the access point' + help: 'Enable the adhoc network' }) - .help('Configure the Tessel as an access point'); + .help('Configure the Tessel as an adhoc network'); makeCommand('root') .callback(options => { diff --git a/lib/tessel/access-point.js b/lib/tessel/access-point.js index ebc03df7..385e26dc 100644 --- a/lib/tessel/access-point.js +++ b/lib/tessel/access-point.js @@ -102,7 +102,7 @@ Tessel.prototype.getAccessPointInfo = function() { Tessel.prototype.createAccessPoint = function(opts) { var ssid = opts.ssid; - var password = opts.pass; + var password = opts.password; var security = opts.security; var mode = opts.mode; var status = 'Created Access Point successfully. '; From 4a6b260b68a7b33c35db9e7e8d271ecdba9b344d Mon Sep 17 00:00:00 2001 From: HipsterBrown Date: Thu, 1 Sep 2016 17:03:40 -0400 Subject: [PATCH 3/5] test(ap): use 'password', set mode for 'adhoc' --- test/unit/access-point.js | 100 ++++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 15 deletions(-) diff --git a/test/unit/access-point.js b/test/unit/access-point.js index 99d6857b..5c37787b 100644 --- a/test/unit/access-point.js +++ b/test/unit/access-point.js @@ -46,11 +46,12 @@ exports['Tessel.prototype.createAccessPoint'] = { }, newAccessPoint: function(test) { - test.expect(20); + test.expect(21); var creds = { ssid: 'test', - pass: 'test-password', - security: 'psk2' + password: 'test-password', + security: 'psk2', + mode: 'ap' }; // Immediately close any opened connections @@ -94,8 +95,71 @@ exports['Tessel.prototype.createAccessPoint'] = { test.equal(this.reconnectDnsmasq.callCount, 1); test.equal(this.reconnectDhcp.callCount, 1); test.ok(this.setAccessPointSSID.lastCall.calledWith(creds.ssid)); - test.ok(this.setAccessPointPassword.lastCall.calledWith(creds.pass)); + test.ok(this.setAccessPointPassword.lastCall.calledWith(creds.password)); test.ok(this.setAccessPointSecurity.lastCall.calledWith(creds.security)); + test.ok(this.setAccessPointMode.lastCall.calledWith(creds.mode)); + + test.done(); + }) + .catch(error => { + test.ok(false, error.toString()); + test.done(); + }); + }, + + newAdhoc: function(test) { + test.expect(21); + var creds = { + ssid: 'test', + password: 'test-password', + security: 'psk2', + mode: 'adhoc' + }; + + // Immediately close any opened connections + this.tessel._rps.on('control', () => { + setImmediate(() => { + this.tessel._rps.emit('close'); + }); + }); + + // tessel.receive is called twice but needs to be rejected the first time + var count = 0; + this.sandbox.stub(this.tessel, 'receive', function(remoteProcess, callback) { + if (typeof callback !== 'function') { + callback = function() {}; + } + if (count === 0) { + count++; + return callback(new Error('uci: Entry not found')); + } else { + return callback(null, new Buffer(0)); + } + }); + + this.tessel.createAccessPoint(creds) + .then(() => { + test.equal(this.getAccessPoint.callCount, 1); + test.equal(this.setAccessPoint.callCount, 1); + test.equal(this.setAccessPointDevice.callCount, 1); + test.equal(this.setAccessPointNetwork.callCount, 1); + test.equal(this.setAccessPointMode.callCount, 1); + test.equal(this.setAccessPointSSID.callCount, 1); + test.equal(this.setAccessPointPassword.callCount, 1); + test.equal(this.setAccessPointSecurity.callCount, 1); + test.equal(this.setLanNetwork.callCount, 1); + test.equal(this.setLanNetworkIfname.callCount, 1); + test.equal(this.setLanNetworkProto.callCount, 1); + test.equal(this.setLanNetworkIP.callCount, 1); + test.equal(this.setLanNetworkNetmask.callCount, 1); + test.equal(this.commitNetwork.callCount, 1); + test.equal(this.reconnectWifi.callCount, 1); + test.equal(this.reconnectDnsmasq.callCount, 1); + test.equal(this.reconnectDhcp.callCount, 1); + test.ok(this.setAccessPointSSID.lastCall.calledWith(creds.ssid)); + test.ok(this.setAccessPointPassword.lastCall.calledWith(creds.password)); + test.ok(this.setAccessPointSecurity.lastCall.calledWith(creds.security)); + test.ok(this.setAccessPointMode.lastCall.calledWith(creds.mode)); test.done(); }) @@ -106,11 +170,12 @@ exports['Tessel.prototype.createAccessPoint'] = { }, noPasswordNoSecurity: function(test) { - test.expect(9); + test.expect(10); var creds = { ssid: 'test', - pass: undefined, - security: undefined + password: undefined, + security: undefined, + mode: 'ap' }; // Test is expecting two closes...; @@ -126,6 +191,7 @@ exports['Tessel.prototype.createAccessPoint'] = { test.equal(this.setAccessPointSSID.callCount, 1); test.equal(this.setAccessPointPassword.callCount, 0); test.equal(this.setAccessPointSecurity.callCount, 1); + test.equal(this.setAccessPointMode.callCount, 1); test.equal(this.reconnectWifi.callCount, 1); test.equal(this.reconnectDnsmasq.callCount, 1); test.equal(this.reconnectDhcp.callCount, 1); @@ -140,11 +206,12 @@ exports['Tessel.prototype.createAccessPoint'] = { }, properCredentials: function(test) { - test.expect(10); + test.expect(11); var creds = { ssid: 'test', - pass: 'test-password', - security: 'psk2' + password: 'test-password', + security: 'psk2', + mode: 'ap' }; // Test is expecting two closes...; @@ -160,11 +227,12 @@ exports['Tessel.prototype.createAccessPoint'] = { test.equal(this.setAccessPointSSID.callCount, 1); test.equal(this.setAccessPointPassword.callCount, 1); test.equal(this.setAccessPointSecurity.callCount, 1); + test.equal(this.setAccessPointMode.callCount, 1); test.equal(this.reconnectWifi.callCount, 1); test.equal(this.reconnectDnsmasq.callCount, 1); test.equal(this.reconnectDhcp.callCount, 1); test.ok(this.setAccessPointSSID.lastCall.calledWith(creds.ssid)); - test.ok(this.setAccessPointPassword.lastCall.calledWith(creds.pass)); + test.ok(this.setAccessPointPassword.lastCall.calledWith(creds.password)); test.ok(this.setAccessPointSecurity.lastCall.calledWith(creds.security)); test.done(); }) @@ -175,11 +243,12 @@ exports['Tessel.prototype.createAccessPoint'] = { }, passwordNoSecurity: function(test) { - test.expect(10); + test.expect(11); var creds = { ssid: 'test', - pass: 'test-password', - security: undefined + password: 'test-password', + security: undefined, + mode: 'ap' }; // Test is expecting two closes...; @@ -195,11 +264,12 @@ exports['Tessel.prototype.createAccessPoint'] = { test.equal(this.setAccessPointSSID.callCount, 1); test.equal(this.setAccessPointPassword.callCount, 1); test.equal(this.setAccessPointSecurity.callCount, 1); + test.equal(this.setAccessPointMode.callCount, 1); test.equal(this.reconnectWifi.callCount, 1); test.equal(this.reconnectDnsmasq.callCount, 1); test.equal(this.reconnectDhcp.callCount, 1); test.ok(this.setAccessPointSSID.lastCall.calledWith(creds.ssid)); - test.ok(this.setAccessPointPassword.lastCall.calledWith(creds.pass)); + test.ok(this.setAccessPointPassword.lastCall.calledWith(creds.password)); test.ok(this.setAccessPointSecurity.lastCall.calledWith('psk2')); test.done(); }) From 60c67ef2d7a78161272fbd273e6ad4887d6c4285 Mon Sep 17 00:00:00 2001 From: HipsterBrown Date: Sun, 9 Oct 2016 13:46:45 -0400 Subject: [PATCH 4/5] feat(access-point): adds ability to set ap/adhoc IP address --- bin/tessel-2.js | 6 +++++ lib/controller.js | 11 +++++++-- lib/tessel/access-point.js | 48 +++++++++++++++++++++++++++++--------- lib/tessel/commands.js | 7 ++++-- lib/tessel/wifi.js | 17 +++++++++++++- 5 files changed, 73 insertions(+), 16 deletions(-) diff --git a/bin/tessel-2.js b/bin/tessel-2.js index 43b6ffe8..f2667340 100755 --- a/bin/tessel-2.js +++ b/bin/tessel-2.js @@ -530,6 +530,9 @@ makeCommand('ap') abbr: 's', help: 'Encryption to use on network (i.e. wep, psk, psk2, wpa, wpa2).' }) + .option('ip', { + help: 'Set the IP address of the network, i.e. "192.168.1.101"' + }) .option('off', { flag: true, help: 'Disable the access point' @@ -569,6 +572,9 @@ makeCommand('adhoc') abbr: 's', help: 'Encryption used on network (i.e. wep, psk, psk2, wpa, wpa2).' }) + .option('ip', { + help: 'Set the IP address of the network, i.e. "192.168.1.101"' + }) .option('off', { flag: true, help: 'Disable the adhoc network' diff --git a/lib/controller.js b/lib/controller.js index d3588389..7ee01e5a 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -720,9 +720,11 @@ controller.setWiFiState = function(opts) { controller.createAccessPoint = function(opts) { opts.authorized = true; var ssid = opts.ssid; + var ip = opts.ip; var password = opts.password; var security = opts.security; var securityOptions = ['none', 'wep', 'psk', 'psk2']; + var ipRegex = /^([0-1]?[0-9]?[0-9]|[2][0-4][0-9]|25[0-5])\.([0-1]?[0-9]?[0-9]|[2][0-4][0-9]|25[0-5])\.([0-1]?[0-9]?[0-9]|[2][0-4][0-9]|25[0-5])\.([0-1]?[0-9]?[0-9]|[2][0-4][0-9]|25[0-5])$/; return new Promise((resolve, reject) => { if (!ssid) { @@ -763,6 +765,11 @@ controller.createAccessPoint = function(opts) { return reject('Invalid passphrase: WPA/WPA2-PSK passkeys must be 8-63 ASCII characters, or 64 hexadecimal digits.'); } } + + if (ip && ipRegex.test(ip) === false) { + return reject(`Invalid IP address: the IP must 4 sets of numbers between 0 and 255, separate by a '.', like '192.168.1.101' or '255.255.255.255'`); + } + resolve(); }) .then(() => { @@ -774,12 +781,12 @@ controller.createAccessPoint = function(opts) { controller.enableAccessPoint = function(opts) { opts.authorized = true; - return controller.standardTesselCommand(opts, (tessel) => tessel.enableAccessPoint()); + return controller.standardTesselCommand(opts, (tessel) => tessel.enableAccessPoint(opts)); }; controller.disableAccessPoint = function(opts) { opts.authorized = true; - return controller.standardTesselCommand(opts, (tessel) => tessel.disableAccessPoint()); + return controller.standardTesselCommand(opts, (tessel) => tessel.disableAccessPoint(opts)); }; controller.getAccessPointInfo = function(opts) { diff --git a/lib/tessel/access-point.js b/lib/tessel/access-point.js index 385e26dc..60ebbc4c 100644 --- a/lib/tessel/access-point.js +++ b/lib/tessel/access-point.js @@ -9,6 +9,7 @@ var commands = require('./commands'); var log = require('../log'); var Tessel = require('./tessel'); +var defaultIP = '192.168.1.101'; function commitAndClose(tessel, status, resolve, reject) { @@ -27,8 +28,8 @@ function commitAndClose(tessel, status, resolve, reject) { .catch(reject); } -Tessel.prototype.enableAccessPoint = function() { - var status = 'Access Point successfully enabled.'; +Tessel.prototype.enableAccessPoint = function(opts) { + var status = `${opts.mode === 'ap' ? 'Access Point' : 'Adhoc Network'} successfully enabled.`; return new Promise((resolve, reject) => { return this.getAccessPointInfo() @@ -59,8 +60,8 @@ Tessel.prototype.enableAccessPoint = function() { }); }; -Tessel.prototype.disableAccessPoint = function() { - var status = 'Access Point successfully disabled.'; +Tessel.prototype.disableAccessPoint = function(opts) { + var status = `${opts.mode === 'ap' ? 'Access Point' : 'Adhoc Network'} successfully disabled.`; return new Promise((resolve, reject) => { return this.simpleExec(commands.turnAccessPointOff()) @@ -105,7 +106,8 @@ Tessel.prototype.createAccessPoint = function(opts) { var password = opts.password; var security = opts.security; var mode = opts.mode; - var status = 'Created Access Point successfully. '; + var ip = opts.ip; + var status = `Created ${mode === 'ap' ? 'Access Point' : 'Adhoc Network'} successfully. `; var setupAccessPoint = () => { @@ -113,11 +115,15 @@ Tessel.prototype.createAccessPoint = function(opts) { security = 'psk2'; } + if (!ip) { + ip = defaultIP; + } + if (password && security) { - status += `SSID: ${ssid}, password ${password}, security mode: ${security}`; + status += `SSID: ${ssid}, password ${password}, security mode: ${security}, IP address: ${ip}`; } else if (!password && !security) { security = 'none'; - status += `SSID: ${ssid}`; + status += `SSID: ${ssid}, IP address: ${ip}`; } var setSSID = () => this.simpleExec(commands.setAccessPointSSID(ssid)); @@ -128,6 +134,8 @@ Tessel.prototype.createAccessPoint = function(opts) { var setAccessPointMode = () => this.simpleExec(commands.setAccessPointMode(mode)); + var setAccessPointIP = () => this.simpleExec(commands.setLanNetworkIP(ip)); + var turnAccessPointOn = () => this.simpleExec(commands.turnAccessPointOn()); var commitAndClosePromise = () => { @@ -147,19 +155,38 @@ Tessel.prototype.createAccessPoint = function(opts) { return setup() .then(setAccessPointMode) + .then(setAccessPointIP) .then(turnAccessPointOn) .then(commitAndClosePromise); }; - return this.simpleExec(commands.getAccessPoint()) + return this.simpleExec(commands.getWifiSettings()) + .then((settings) => { + var regexDisabled = /disabled='(\d)'/; + var disabledMatcher = settings.match(regexDisabled); + + // because an adhoc network both connects and emits, + // wifi cannot be enabled to when creating an adhoc network + if (mode === 'adhoc' && disabledMatcher[1] === '0') { + throw new Error(`Tessel must have wifi disabled before creating an adhoc network. Please run 't2 wifi --off' and try again.`); + } + + return true; + }) + + .then(this.simpleExec(commands.getAccessPoint())) .then(() => { // When an AP exists, change the status to // reflect an "update" vs. "create": - status = 'Updated Access Point successfully. '; + status = `Updated ${mode === 'ap' ? 'Access Point' : 'Adhoc Network'} successfully. `; return setupAccessPoint(); }) - .catch(() => { + .catch((error) => { + // this error was thrown when checking on the wifi status above + if (error && error.message.includes('adhoc')) { + return Promise.reject(error); + } var setAccessPoint = () => { return this.simpleExec(commands.setAccessPoint()) @@ -171,7 +198,6 @@ Tessel.prototype.createAccessPoint = function(opts) { return this.simpleExec(commands.setLanNetwork()) .then(() => this.simpleExec(commands.setLanNetworkIfname())) .then(() => this.simpleExec(commands.setLanNetworkProto())) - .then(() => this.simpleExec(commands.setLanNetworkIP())) .then(() => this.simpleExec(commands.setLanNetworkNetmask())); }; diff --git a/lib/tessel/commands.js b/lib/tessel/commands.js index 13398a47..174daccd 100644 --- a/lib/tessel/commands.js +++ b/lib/tessel/commands.js @@ -125,8 +125,8 @@ module.exports.setLanNetworkIfname = function() { module.exports.setLanNetworkProto = function() { return ['uci', 'set', 'network.lan.proto=static']; }; -module.exports.setLanNetworkIP = function() { - return ['uci', 'set', 'network.lan.ipaddr=192.168.1.101']; +module.exports.setLanNetworkIP = function(ip) { + return ['uci', 'set', `network.lan.ipaddr=${ip}`]; }; module.exports.setLanNetworkNetmask = function() { return ['uci', 'set', 'network.lan.netmask=255.255.255.0']; @@ -134,6 +134,9 @@ module.exports.setLanNetworkNetmask = function() { module.exports.commitNetwork = function() { return ['uci', 'commit', 'network']; }; +module.exports.getWifiSettings = function() { + return ['uci', 'show', 'wireless.@wifi-iface[0]']; +}; module.exports.getAccessPoint = function() { return ['uci', 'get', 'wireless.@wifi-iface[1]']; }; diff --git a/lib/tessel/wifi.js b/lib/tessel/wifi.js index c425965e..298eda04 100644 --- a/lib/tessel/wifi.js +++ b/lib/tessel/wifi.js @@ -149,7 +149,22 @@ Tessel.prototype.connectToNetwork = function(opts) { }; - return setup() + return this.simpleExec(commands.getAccessPointConfig()) + .then((config) => { + var regexDisabled = /disabled='(\d)'/; + var regexMode = /mode='(.+)'/; + var disabledMatcher = config.match(regexDisabled); + var modeMatcher = config.match(regexMode); + + // because an adhoc network both connects and emits, + // adhoc cannot be enabled to when connecting to a wifi network + if (modeMatcher[1] === 'adhoc' && disabledMatcher[1] === '0') { + throw new Error(`Tessel must have adhoc disabled before connecting to a wifi network. Please run 't2 adhoc --off' and try again.`); + } + + return true; + }) + .then(setup) .then(turnWifiOn) .then(logStatus); }; From 65d657094bf331cb32ee7c0b2e95ddce005b2ab6 Mon Sep 17 00:00:00 2001 From: HipsterBrown Date: Sun, 9 Oct 2016 16:39:43 -0400 Subject: [PATCH 5/5] test(access-point/wifi): adds ability to set ap/adhoc IP address --- test/unit/access-point.js | 58 ++++++++++++++++++--- test/unit/controller.js | 30 +++++++++++ test/unit/wifi.js | 105 +++++++++++++++++++++++++++++++++++++- 3 files changed, 184 insertions(+), 9 deletions(-) diff --git a/test/unit/access-point.js b/test/unit/access-point.js index 5c37787b..7e4b3e4f 100644 --- a/test/unit/access-point.js +++ b/test/unit/access-point.js @@ -27,6 +27,7 @@ exports['Tessel.prototype.createAccessPoint'] = { this.reconnectWifi = this.sandbox.spy(commands, 'reconnectWifi'); this.reconnectDnsmasq = this.sandbox.spy(commands, 'reconnectDnsmasq'); this.reconnectDhcp = this.sandbox.spy(commands, 'reconnectDhcp'); + this.getWifiSettings = this.sandbox.spy(commands, 'getWifiSettings'); this.tessel = TesselSimulator(); // These are needed because the sheer number of commands run within @@ -46,7 +47,7 @@ exports['Tessel.prototype.createAccessPoint'] = { }, newAccessPoint: function(test) { - test.expect(21); + test.expect(23); var creds = { ssid: 'test', password: 'test-password', @@ -94,10 +95,12 @@ exports['Tessel.prototype.createAccessPoint'] = { test.equal(this.reconnectWifi.callCount, 1); test.equal(this.reconnectDnsmasq.callCount, 1); test.equal(this.reconnectDhcp.callCount, 1); + test.equal(this.getWifiSettings.callCount, 1); test.ok(this.setAccessPointSSID.lastCall.calledWith(creds.ssid)); test.ok(this.setAccessPointPassword.lastCall.calledWith(creds.password)); test.ok(this.setAccessPointSecurity.lastCall.calledWith(creds.security)); test.ok(this.setAccessPointMode.lastCall.calledWith(creds.mode)); + test.ok(this.setLanNetworkIP.lastCall.calledWith('192.168.1.101')); test.done(); }) @@ -108,7 +111,7 @@ exports['Tessel.prototype.createAccessPoint'] = { }, newAdhoc: function(test) { - test.expect(21); + test.expect(23); var creds = { ssid: 'test', password: 'test-password', @@ -156,10 +159,12 @@ exports['Tessel.prototype.createAccessPoint'] = { test.equal(this.reconnectWifi.callCount, 1); test.equal(this.reconnectDnsmasq.callCount, 1); test.equal(this.reconnectDhcp.callCount, 1); + test.equal(this.getWifiSettings.callCount, 1); test.ok(this.setAccessPointSSID.lastCall.calledWith(creds.ssid)); test.ok(this.setAccessPointPassword.lastCall.calledWith(creds.password)); test.ok(this.setAccessPointSecurity.lastCall.calledWith(creds.security)); test.ok(this.setAccessPointMode.lastCall.calledWith(creds.mode)); + test.ok(this.setLanNetworkIP.lastCall.calledWith('192.168.1.101')); test.done(); }) @@ -206,12 +211,13 @@ exports['Tessel.prototype.createAccessPoint'] = { }, properCredentials: function(test) { - test.expect(11); + test.expect(13); var creds = { ssid: 'test', password: 'test-password', security: 'psk2', - mode: 'ap' + mode: 'ap', + ip: '192.168.1.113' }; // Test is expecting two closes...; @@ -228,12 +234,14 @@ exports['Tessel.prototype.createAccessPoint'] = { test.equal(this.setAccessPointPassword.callCount, 1); test.equal(this.setAccessPointSecurity.callCount, 1); test.equal(this.setAccessPointMode.callCount, 1); + test.equal(this.setLanNetworkIP.callCount, 1); test.equal(this.reconnectWifi.callCount, 1); test.equal(this.reconnectDnsmasq.callCount, 1); test.equal(this.reconnectDhcp.callCount, 1); test.ok(this.setAccessPointSSID.lastCall.calledWith(creds.ssid)); test.ok(this.setAccessPointPassword.lastCall.calledWith(creds.password)); test.ok(this.setAccessPointSecurity.lastCall.calledWith(creds.security)); + test.ok(this.setLanNetworkIP.lastCall.calledWith(creds.ip)); test.done(); }) .catch(error => { @@ -277,6 +285,42 @@ exports['Tessel.prototype.createAccessPoint'] = { test.ok(false, error.toString()); test.done(); }); + }, + + createAdhocWhenWifiEnabled: function(test) { + test.expect(2); + var creds = { + ssid: 'test', + mode: 'adhoc' + }; + + // Test is expecting two closes...; + this.tessel._rps.on('control', (command) => { + if (command.toString() === 'uci show wireless.@wifi-iface[0]') { + var info = new Buffer(tags.stripIndent ` + wireless.cfg053579.disabled='0'`); + + setImmediate(() => { + this.tessel._rps.stdout.emit('data', info); + this.tessel._rps.emit('close'); + }); + } else { + setImmediate(() => { + this.tessel._rps.emit('close'); + }); + } + }); + + this.tessel.createAccessPoint(creds) + .then(() => { + test.fail('Creating an adhoc network should fail while wifi is enabled.'); + test.done(); + }) + .catch(error => { + test.equal(this.getWifiSettings.callCount, 1); + test.ok(error.toString()); + test.done(); + }); } }; @@ -346,7 +390,7 @@ exports['Tessel.prototype.enableAccessPoint'] = { } }); - this.tessel.enableAccessPoint() + this.tessel.enableAccessPoint({mode: 'ap'}) .then(() => { test.equal(this.turnAccessPointOn.callCount, 1); test.equal(this.reconnectWifi.callCount, 1); @@ -398,7 +442,7 @@ exports['Tessel.prototype.enableAccessPoint'] = { } }); - this.tessel.enableAccessPoint() + this.tessel.enableAccessPoint({mode: 'ap'}) .then(() => { test.fail('Should not pass'); test.done(); @@ -444,7 +488,7 @@ exports['Tessel.prototype.disableAccessPoint'] = { }); }); - this.tessel.disableAccessPoint() + this.tessel.disableAccessPoint({mode: 'ap'}) .then(() => { test.equal(this.turnAccessPointOff.callCount, 1); test.equal(this.reconnectWifi.callCount, 1); diff --git a/test/unit/controller.js b/test/unit/controller.js index 707df0ab..d49c1f42 100644 --- a/test/unit/controller.js +++ b/test/unit/controller.js @@ -1156,6 +1156,36 @@ exports['controller.createAccessPoint'] = { test.done(); }); }, + + invalidAccessPointIPAddress: function(test) { + test.expect(1); + + controller.createAccessPoint({ + ssid: 'test', + ip: '999.9999.9' + }) + .catch(error => { + test.ok(error); + test.done(); + }); + }, + + validAccessPointIPAddress: function(test) { + test.expect(1); + + controller.createAccessPoint({ + ssid: 'test', + ip: '192.168.1.113' + }) + .then((settings) => { + test.ok(settings); + test.done(); + }) + .catch((error) => { + test.fail(error.toString()); + test.done(); + }); + }, }; exports['controller.root'] = { diff --git a/test/unit/wifi.js b/test/unit/wifi.js index 483c5087..1cf86227 100644 --- a/test/unit/wifi.js +++ b/test/unit/wifi.js @@ -232,6 +232,7 @@ module.exports['Tessel.prototype.connectToNetwork'] = { this.commitWirelessCredentials = this.sandbox.spy(commands, 'commitWirelessCredentials'); this.reconnectWifi = this.sandbox.spy(commands, 'reconnectWifi'); this.getWifiInfo = this.sandbox.spy(commands, 'getWifiInfo'); + this.getAccessPointConfig = this.sandbox.spy(commands, 'getAccessPointConfig'); this.tessel = TesselSimulator(); done(); @@ -242,7 +243,7 @@ module.exports['Tessel.prototype.connectToNetwork'] = { done(); }, noPassword: function(test) { - test.expect(8); + test.expect(9); var creds = { ssid: 'tank', password: undefined @@ -257,6 +258,15 @@ module.exports['Tessel.prototype.connectToNetwork'] = { this.tessel._rps.stdout.emit('data', new Buffer('signal')); this.tessel._rps.emit('close'); }); + } else if (command.toString() === 'uci show wireless.@wifi-iface[1]') { + var info = new Buffer(tags.stripIndent ` + wireless.cfg053579.mode='ap' + wireless.cfg053579.disabled='1'`); + + setImmediate(() => { + this.tessel._rps.stdout.emit('data', info); + this.tessel._rps.emit('close'); + }); } else { setImmediate(() => { // Remove any listeners on stdout so we don't break anything when we write to it @@ -273,9 +283,10 @@ module.exports['Tessel.prototype.connectToNetwork'] = { test.equal(this.setNetworkEncryption.callCount, 1); test.equal(this.commitWirelessCredentials.callCount, 1); test.equal(this.reconnectWifi.callCount, 1); + test.equal(this.getWifiInfo.callCount, 1); + test.equal(this.getAccessPointConfig.callCount, 1); test.ok(this.setNetworkSSID.lastCall.calledWith(creds.ssid)); test.ok(this.setNetworkEncryption.lastCall.calledWith('none')); - test.ok(this.getWifiInfo.callCount, 1); test.done(); }) .catch(error => { @@ -299,6 +310,15 @@ module.exports['Tessel.prototype.connectToNetwork'] = { this.tessel._rps.stdout.emit('data', new Buffer('signal')); this.tessel._rps.emit('close'); }); + } else if (command.toString() === 'uci show wireless.@wifi-iface[1]') { + var info = new Buffer(tags.stripIndent ` + wireless.cfg053579.mode='ap' + wireless.cfg053579.disabled='1'`); + + setImmediate(() => { + this.tessel._rps.stdout.emit('data', info); + this.tessel._rps.emit('close'); + }); } else { setImmediate(() => { // Remove any listeners on stdout so we don't break anything when we write to it @@ -344,6 +364,15 @@ module.exports['Tessel.prototype.connectToNetwork'] = { this.tessel._rps.stdout.emit('data', new Buffer('signal')); this.tessel._rps.emit('close'); }); + } else if (command.toString() === 'uci show wireless.@wifi-iface[1]') { + var info = new Buffer(tags.stripIndent ` + wireless.cfg053579.mode='ap' + wireless.cfg053579.disabled='1'`); + + setImmediate(() => { + this.tessel._rps.stdout.emit('data', info); + this.tessel._rps.emit('close'); + }); } else { setImmediate(() => { // Remove any listeners on stdout so we don't break anything when we write to it @@ -388,6 +417,15 @@ module.exports['Tessel.prototype.connectToNetwork'] = { this.tessel._rps.stderr.emit('data', new Buffer('Unable to connect to the network.')); this.tessel._rps.emit('close'); }); + } else if (command.toString() === 'uci show wireless.@wifi-iface[1]') { + var info = new Buffer(tags.stripIndent ` + wireless.cfg053579.mode='ap' + wireless.cfg053579.disabled='1'`); + + setImmediate(() => { + this.tessel._rps.stdout.emit('data', info); + this.tessel._rps.emit('close'); + }); } else { setImmediate(() => { // Remove any listeners on stderr so we don't break anything when we write to it @@ -435,6 +473,15 @@ module.exports['Tessel.prototype.connectToNetwork'] = { this.tessel._rps.stderr.removeAllListeners(); this.tessel._rps.emit('close'); }); + } else if (command.toString() === 'uci show wireless.@wifi-iface[1]') { + var info = new Buffer(tags.stripIndent ` + wireless.cfg053579.mode='ap' + wireless.cfg053579.disabled='1'`); + + setImmediate(() => { + this.tessel._rps.stdout.emit('data', info); + this.tessel._rps.emit('close'); + }); } else { setImmediate(() => { // Remove any listeners on stderr so we don't break anything when we write to it @@ -464,6 +511,51 @@ module.exports['Tessel.prototype.connectToNetwork'] = { test.done(); }); }, + connectFailsWhileAdhocEnabled: function(test) { + test.expect(2); + var creds = { + ssid: 'tank', + password: 'not_gonna_work' + }; + // Test is expecting several closes...; + this.tessel._rps.on('control', (command) => { + if (command.toString() === 'ubus call iwinfo info {"device":"wlan0"}') { + // Write to stdout so it completes as expected + // Wrap in setImmediate to make sure listener is set up before emitting + setImmediate(() => { + this.tessel._rps.stdout.emit('data', new Buffer('signal')); + this.tessel._rps.emit('close'); + }); + } else if (command.toString() === 'uci show wireless.@wifi-iface[1]') { + var info = new Buffer(tags.stripIndent ` + wireless.cfg053579.mode='adhoc' + wireless.cfg053579.disabled='0'`); + + setImmediate(() => { + this.tessel._rps.stdout.emit('data', info); + this.tessel._rps.emit('close'); + }); + } else { + setImmediate(() => { + // Remove any listeners on stderr so we don't break anything when we write to it + this.tessel._rps.stdout.removeAllListeners(); + this.tessel._rps.stderr.removeAllListeners(); + this.tessel._rps.emit('close'); + }); + } + }); + + this.tessel.connectToNetwork(creds) + .then(function() { + test.ok(false, 'Test should have rejected with an error.'); + test.done(); + }) + .catch((error) => { + test.equal(error.message.includes('Tessel must have adhoc disabled'), true); + test.equal(this.getAccessPointConfig.callCount, 1); + test.done(); + }); + }, // Sometimes the keyword for success (signal) is not in the first batch // of stdout data batchedResponse: function(test) { @@ -484,6 +576,15 @@ module.exports['Tessel.prototype.connectToNetwork'] = { this.tessel._rps.stdout.emit('data', new Buffer('signal')); this.tessel._rps.emit('close'); }); + } else if (command.toString() === 'uci show wireless.@wifi-iface[1]') { + var info = new Buffer(tags.stripIndent ` + wireless.cfg053579.mode='ap' + wireless.cfg053579.disabled='1'`); + + setImmediate(() => { + this.tessel._rps.stdout.emit('data', info); + this.tessel._rps.emit('close'); + }); } else { setImmediate(() => { // Remove any listeners on stderr so we don't break anything when we write to it