From 1cfdfcddc6d633f9bc8c9fbc7dd040dc92b510e9 Mon Sep 17 00:00:00 2001 From: David Antler Date: Fri, 20 May 2016 16:44:39 -0700 Subject: [PATCH] Add JavaScript mraaStub project Signed-off-by: David Antler Signed-off-by: Brendan Le Foll --- jsstub/Gruntfile.js | 17 +++ jsstub/README.md | 52 +++++++++ jsstub/index.js | 223 +++++++++++++++++++++++++++++++++++++++ jsstub/package.json | 34 ++++++ jsstub/test/index.js | 32 ++++++ jsstub/test/lightbulb.js | 32 ++++++ 6 files changed, 390 insertions(+) create mode 100644 jsstub/Gruntfile.js create mode 100644 jsstub/README.md create mode 100644 jsstub/index.js create mode 100644 jsstub/package.json create mode 100644 jsstub/test/index.js create mode 100644 jsstub/test/lightbulb.js diff --git a/jsstub/Gruntfile.js b/jsstub/Gruntfile.js new file mode 100644 index 0000000..acd3276 --- /dev/null +++ b/jsstub/Gruntfile.js @@ -0,0 +1,17 @@ +module.exports = function(grunt) { + grunt.loadNpmTasks('grunt-mocha-test'); + // Project configuration. + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + mochaTest: { + test: { + options: { + reporter: 'spec' + }, + src: ['test/*.js'] + } + } + }); + + grunt.registerTask('test', ['mochaTest:test']); +}; diff --git a/jsstub/README.md b/jsstub/README.md new file mode 100644 index 0000000..2184d6e --- /dev/null +++ b/jsstub/README.md @@ -0,0 +1,52 @@ +mraaStub - JavaScript simulation and stubs for mraa +==================== + +This project enables simulation of a device which might be accessed via mraa. +Currently this library supports I2c, SPI, and GPIO. This project provides +several benefits: + +1. Prevent crashes in nodejs applications using mraa on unsuported or + misconfigured hardware. +2. Enable basic simulation of mraa-accessible devices for unit testing. + +## Examples + +The following example is based on an imaginary 'light bulb' device abstraction, +which exposes a value of brightness over a mraa-provided interface. Please see +the `test/index.js` file for an example. + +## Installation + +mraaStub is not yet in npm so has to be installed from git. In the future +you'll be able to install `mraaStub` from npm like this: + +``` +npm install mraaStub +``` + +Since we often switch between a mraaStub and the real mraa library, we +suggest creating an `index.js` file inside a `lib/mraaSwitcher` folder. + +```js +/* index.js - file for switching between mraa and mraaStub + */ + +// Define the conditions under which the mraaStub should be loaded +var platform = require('os').platform(); +var m; + +if (platform === 'win32') { + m = require('mraaStub'); +} else { + m = require('mraa'); +} + +module.exports = m; +``` + +You can add this to your project in its own `lib/mraaSwitcher/index.js` file +and use `require('../mraaSwitcher')` everywhere! + +## License + +See [COPYING file](../COPYING) in the root of this repository. diff --git a/jsstub/index.js b/jsstub/index.js new file mode 100644 index 0000000..ccf8520 --- /dev/null +++ b/jsstub/index.js @@ -0,0 +1,223 @@ +/** + * @fileoverview This file implements a fake mraa stub which enables testing + * as well as the ability to run on Windows. + */ + +var m; +var winston = require('winston'); +var logger = new winston.Logger({ + transports: [ + new winston.transports.Console({ + level: 'error', + handleExceptions: false, + json: false, + colorize: true}) + ], + exitOnError: false + }); + +/** + * @class mraaStub + * @classdesc This class is designed to stub out libmraa so we can run + * test code on unsupported platforms (specifically Windows). + */ +var mraaStub = function() { + var verison = '0.0.1'; + var self = this; + self.EDGE_BOTH = 1; + self.EDGE_NONE = 2; + self.EDGE_RISING = 3; + self.EDGE_FALLING = 4; + self.DIR_IN = 1; + self.DIR_OUT = 2; + + self.getVersion = function() { + return "mraaStub " + version; + }; + + // Stub out GPIO + function Gpio(num) { + this.num = num; + this._callback = null; + this._dir = null; + this._isr_mode = self.EDGE_NONE; + } + + var logGpio = false; + Gpio.prototype._callIsr = function() { + if(this.isr_mode === self.EDGE_NONE) { + logger.log('info',"Could not call ISR. Not set up for triggering"); + } + this._callback(); + }; + + Gpio.prototype.isr = function(mode, handler){ + if(logGpio) { + logger.log('info',"GPIO " + this.num + " isr stub invoked."); + } + this._isr_mode = self.EDGE_NONE; + this._callback = handler; + }; + + Gpio.prototype.dir = function(d) { + if(logGpio) { + logger.log('info',"GPIO " + this.num + " dir stub invoked."); + } + this._dir = d; + }; + Gpio.prototype.write = function(z) { + if(logGpio) { + logger.log('logger',"GPIO " + this.num + " write stub invoked."); + } + if(this._dir !== self.DIR_OUT) { + logger.log('info',"GPIO " + this.num + " write called without DIR_OUT set."); + } + }; + Gpio.prototype.read = function() { + if(logGpio) { + logger.log('info',"GPIO " + this.num + " read stub invoked."); + } + return 0; + }; + + // Stub out SPI + function Spi(num) { + var self = this; + this.num = num; + this._buffer = new Buffer(29); + this._buffer.fill(0); + this._loopback = false; + } + + Spi.prototype._setOutput = function(buf) { + this._buffer = buf; + }; + + Spi.prototype._enableLoopback = function(x) { + if(x === true) { + this._loopback = true; + } else { + this._loopback = false; + } + }; + + Spi.prototype.write = function(b) { + logger.log('info',"SPI write stub invoked."); + if(this._loopback === true) { + return b; + } + return new Buffer(this._buffer); + }; + + Spi.prototype.frequency = function(f) { + logger.log('info',"SPI frequency stub invoked."); + return f; + }; + + Spi.prototype.lsbmode = function(t) { + logger.log('info',"SPI lsbmode stub invoked."); + }; + + Spi.prototype.mode = function(x) { + logger.log('info',"SPI mode stub invoked."); + }; + + function I2c(num) { + this._num = num; + this._regMapInitialized = false; + } + + /* This function sets an internal register map for the I2c device. + */ + I2c.prototype._setRegisterMapInternal = function(buffer) { + this._regMapInitialized = true; + this._buffer = buffer; + }; + + I2c.prototype.frequency = function(freq) { + // Do nothing. We don't care. + }; + + I2c.prototype.address = function(address) { + var self = this; + self.address = address; + }; + + I2c.prototype.readReg = function(regAddr) { + if(!this._regMapInitialized) { + logger.log('error', "Need to set reg map"); + } + if(!this.address) { + logger.log('error', "Need to set address"); + } + + return this._buffer.readUInt8(regAddr); + }; + + I2c.prototype.readWordReg = function(regAddr) { + if(!this._regMapInitialized) { + logger.log('error', "Need to set reg map"); + } + if(!this.address) { + logger.log('error', "Need to set address"); + } + + return this._buffer.readUInt16LE(regAddr); + }; + + I2c.prototype.readBytesReg = function(regAddr, len) { + if(!this._regMapInitialized) { + logger.log('error', "Need to set reg map"); + } + if(!this.address) { + logger.log('error', "Need to set address"); + } + + return this._buffer.slice(regAddr,regAddr+len); + }; + + I2c.prototype.write = function(buf) { + if(!this._regMapInitialized) { + logger.log('error', "Need to set reg map"); + } + if(!this.address) { + logger.log('error', "Need to set address"); + } + + var regAddr = buf[0]; + var newBuf = buf.slice(1); + newBuf.copy(this._buffer, regAddr); + }; + + I2c.prototype.writeReg = function(regAddr, data) { + if(!this._regMapInitialized) { + logger.log('error', "Need to set reg map"); + } + if(!this.address) { + logger.log('error', "Need to set address"); + } + + this._buffer.writeUInt8(regAddr,data); + }; + + I2c.prototype.writeWordReg = function(regAddr, dataWord) { + if(!this._regMapInitialized) { + logger.log('error', "Need to set reg map"); + } + if(!this.address) { + logger.log('error', "Need to set address"); + } + + this._buffer.writeUInt16LE(regAddr,data); + }; + + // Export our stubs + self.Gpio = Gpio; + self.Spi = Spi; + self.I2c = I2c; + +}; + +m = new mraaStub(); + +module.exports = m; diff --git a/jsstub/package.json b/jsstub/package.json new file mode 100644 index 0000000..121261b --- /dev/null +++ b/jsstub/package.json @@ -0,0 +1,34 @@ +{ + "name": "mraaStub", + "version": "0.0.1", + "description": "Enables simulation of mraa interfaces for testing purposes", + "main": "index.js", + "scripts": { + "test": "grunt test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/intel-iot-devkit/mraa.git" + }, + "keywords": [ + "mraa", + "iot", + "intel", + "libmraa", + "test", + "galileo", + "edison" + ], + "author": "David A Antler ", + "license": "MIT", + "bugs": { + "url": "https://github.com/intel-iot-devkit/mraa/issues" + }, + "homepage": "https://github.com/intel-iot-devkit/mraa#readme", + "devDependencies": { + "expect.js": "^0.3.1", + "grunt": "^1.0.1", + "grunt-mocha-test": "^0.12.7", + "mocha": "^2.4.5" + } +} diff --git a/jsstub/test/index.js b/jsstub/test/index.js new file mode 100644 index 0000000..47d7657 --- /dev/null +++ b/jsstub/test/index.js @@ -0,0 +1,32 @@ +var expect = require('expect.js'); +var m = require('../index'); +var LightBulb = require('./lightbulb'); + +describe('LightBulb', function() { + + /** Model the internal data of LightBulb as a buffer */ + var bufferFullBrightness = new Buffer( + [ 'N', // Four bytes allocated for name + 'a', + 'm', + 'e', + 95 // One byte allocated for brightness. Stuff in '95' value! + ]); + + it('getBrightness() function should return 95', function() { + + // Create a fake I2c bus based on the 'full brightness' data model + var testI2cBus = new m.I2c(0); + testI2cBus._setRegisterMapInternal(bufferFullBrightness); + + // Create a new LightBulb that opens the testI2cBus, instead of a real + // mraa I2c bus. + var lightBulbI2c = new LightBulb(testI2cBus); + + // presumably getBrightness will gather data from I2C and get '95' + var brightness = lightBulbI2c.getBrightness(); + + expect(brightness).to.be(95); + }) +}); + diff --git a/jsstub/test/lightbulb.js b/jsstub/test/lightbulb.js new file mode 100644 index 0000000..f05ba57 --- /dev/null +++ b/jsstub/test/lightbulb.js @@ -0,0 +1,32 @@ +/** +* @fileoverview Implementation of a LightBulb class abstraction +* +*/ + +module.exports = (function() { + "use strict"; + var m = require('../index'); + + /** + * Constructor for a new LightBulb + * @class LightBulb + * + * @classdesc This class abstracts access to the control and data registers + * on an imaginary lightbulb. + * @param {Object} A libmraa I2c object, initialized + */ + function LightBulb (i2cInterface) { + var self = this; + self._i2cInterface = i2cInterface; + + self.getBrightness = function() { + // Presume our brightness data is one byte at offset 4 + return self._i2cInterface.readReg(4); + } + + return self; + } + + return LightBulb; +})(); +