Skip to content

Commit

Permalink
Add JavaScript mraaStub project
Browse files Browse the repository at this point in the history
Signed-off-by: David Antler <[email protected]>
Signed-off-by: Brendan Le Foll <[email protected]>
  • Loading branch information
dantler authored and arfoll committed May 24, 2016
1 parent a321d67 commit 1cfdfcd
Show file tree
Hide file tree
Showing 6 changed files with 390 additions and 0 deletions.
17 changes: 17 additions & 0 deletions jsstub/Gruntfile.js
Original file line number Diff line number Diff line change
@@ -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']);
};
52 changes: 52 additions & 0 deletions jsstub/README.md
Original file line number Diff line number Diff line change
@@ -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.
223 changes: 223 additions & 0 deletions jsstub/index.js
Original file line number Diff line number Diff line change
@@ -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;
34 changes: 34 additions & 0 deletions jsstub/package.json
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>",
"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"
}
}
32 changes: 32 additions & 0 deletions jsstub/test/index.js
Original file line number Diff line number Diff line change
@@ -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);
})
});

32 changes: 32 additions & 0 deletions jsstub/test/lightbulb.js
Original file line number Diff line number Diff line change
@@ -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;
})();

0 comments on commit 1cfdfcd

Please sign in to comment.