529 lines
13 KiB
JavaScript
529 lines
13 KiB
JavaScript
/* eslint no-sync: 0 */
|
|
"use strict";
|
|
|
|
var fs = require("fs");
|
|
|
|
var DigitalPin = lib("io/digital-pin"),
|
|
Utils = lib("utils");
|
|
|
|
describe("Cylon.IO.DigitalPin", function() {
|
|
var pin = new DigitalPin({ pin: "4", mode: "w" });
|
|
|
|
describe("constructor", function() {
|
|
it("sets @pinNum to the pin number passed in opts", function() {
|
|
expect(pin.pinNum).to.be.eql("4");
|
|
});
|
|
|
|
it("sets @status to 'low' by default", function() {
|
|
expect(pin.status).to.be.eql("low");
|
|
});
|
|
|
|
it("sets @ready to false by default", function() {
|
|
expect(pin.ready).to.be.false;
|
|
});
|
|
|
|
it("sets @mode to the mode passed in opts", function() {
|
|
expect(pin.mode).to.be.eql("w");
|
|
});
|
|
});
|
|
|
|
describe("#connect", function() {
|
|
var path = "/sys/class/gpio/gpio4";
|
|
|
|
context("if the GPIO file for the pin exists", function() {
|
|
beforeEach(function() {
|
|
stub(fs, "exists").callsArgWith(1, true);
|
|
stub(pin, "_openPin");
|
|
});
|
|
|
|
afterEach(function() {
|
|
fs.exists.restore();
|
|
pin._openPin.restore();
|
|
});
|
|
|
|
it("opens the pin", function() {
|
|
pin.connect(pin.mode);
|
|
expect(fs.exists).to.be.calledWith(path);
|
|
expect(pin._openPin).to.be.called;
|
|
});
|
|
});
|
|
|
|
context("if the GPIO file for the pin doesn't exist", function() {
|
|
beforeEach(function() {
|
|
stub(fs, "exists").callsArgWith(1, false);
|
|
stub(pin, "_createGPIOPin");
|
|
});
|
|
|
|
afterEach(function() {
|
|
fs.exists.restore();
|
|
pin._createGPIOPin.restore();
|
|
});
|
|
|
|
it("creates a new GPIO pin", function() {
|
|
pin.connect(pin.mode);
|
|
expect(fs.exists).to.be.calledWith(path);
|
|
expect(pin._createGPIOPin).to.be.called;
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("#close", function() {
|
|
var path = "/sys/class/gpio/unexport";
|
|
|
|
beforeEach(function() {
|
|
stub(fs, "writeFile").callsArgWith(2, false);
|
|
stub(pin, "_closeCallback");
|
|
});
|
|
|
|
afterEach(function() {
|
|
fs.writeFile.restore();
|
|
pin._closeCallback.restore();
|
|
});
|
|
|
|
it("writes to the GPIO unexport path with the pin's value", function() {
|
|
pin.close();
|
|
expect(fs.writeFile).to.be.calledWith(path, "4");
|
|
});
|
|
|
|
it("calls the closeCallback", function() {
|
|
pin.close();
|
|
expect(pin._closeCallback).to.be.calledWith(false);
|
|
});
|
|
});
|
|
|
|
describe("#closeSync", function() {
|
|
var path = "/sys/class/gpio/unexport";
|
|
|
|
beforeEach(function() {
|
|
stub(fs, "writeFileSync");
|
|
stub(pin, "_closeCallback");
|
|
});
|
|
|
|
afterEach(function() {
|
|
fs.writeFileSync.restore();
|
|
pin._closeCallback.restore();
|
|
});
|
|
|
|
it("writes to the GPIO unexport path with the pin's value", function() {
|
|
pin.closeSync();
|
|
expect(fs.writeFileSync).to.be.calledWith(path, "4");
|
|
});
|
|
|
|
it("calls the closeCallback", function() {
|
|
pin.closeSync();
|
|
expect(pin._closeCallback).to.be.calledWith(false);
|
|
});
|
|
});
|
|
|
|
describe("#digitalWrite", function() {
|
|
var path = "/sys/class/gpio/gpio4/value";
|
|
|
|
context("if pin mode isn't 'w'", function() {
|
|
beforeEach(function() {
|
|
stub(fs, "writeFile");
|
|
stub(pin, "_setMode");
|
|
});
|
|
|
|
afterEach(function() {
|
|
fs.writeFile.restore();
|
|
pin._setMode.restore();
|
|
});
|
|
|
|
it("sets the pin mode to 'w'", function() {
|
|
pin.mode = "r";
|
|
pin.digitalWrite(1);
|
|
expect(pin._setMode).to.be.calledWith("w");
|
|
});
|
|
});
|
|
|
|
context("when successful", function() {
|
|
beforeEach(function() {
|
|
pin.mode = "w";
|
|
stub(fs, "writeFile").callsArgWith(2, null);
|
|
stub(pin, "emit");
|
|
});
|
|
|
|
afterEach(function() {
|
|
fs.writeFile.restore();
|
|
pin.emit.restore();
|
|
});
|
|
|
|
it("emits a digitalWrite event with the written value", function() {
|
|
pin.digitalWrite(1);
|
|
expect(fs.writeFile).to.be.calledWith(path, 1);
|
|
expect(pin.emit).to.be.calledWith("digitalWrite", 1);
|
|
});
|
|
|
|
it("returns the passed value", function() {
|
|
expect(pin.digitalWrite(1)).to.be.eql(1);
|
|
});
|
|
|
|
it("changes the pin's @status", function() {
|
|
pin.status = "low";
|
|
pin.digitalWrite(1);
|
|
expect(pin.status).to.be.eql("high");
|
|
});
|
|
});
|
|
|
|
context("when there is an error", function() {
|
|
beforeEach(function() {
|
|
pin.mode = "w";
|
|
stub(fs, "writeFile").callsArgWith(2, true);
|
|
stub(pin, "emit");
|
|
});
|
|
|
|
afterEach(function() {
|
|
fs.writeFile.restore();
|
|
pin.emit.restore();
|
|
});
|
|
|
|
it("emits an error message", function() {
|
|
pin.digitalWrite(1);
|
|
expect(pin.emit).to.be.calledWith("error");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("#digitalRead", function() {
|
|
beforeEach(function() {
|
|
this.clock = sinon.useFakeTimers();
|
|
});
|
|
|
|
afterEach(function() {
|
|
this.clock.restore();
|
|
});
|
|
|
|
context("if the mode isn't 'r'", function() {
|
|
beforeEach(function() {
|
|
stub(Utils, "every");
|
|
stub(pin, "_setMode");
|
|
});
|
|
|
|
afterEach(function() {
|
|
Utils.every.restore();
|
|
pin._setMode.restore();
|
|
});
|
|
|
|
it("sets the pin mode to 'r'", function() {
|
|
pin.mode = "w";
|
|
pin.digitalRead(500);
|
|
expect(pin._setMode).to.be.calledWith("r");
|
|
});
|
|
});
|
|
|
|
context("when successful", function() {
|
|
beforeEach(function() {
|
|
stub(fs, "readFile").callsArgWith(1, null, 1);
|
|
stub(pin, "emit");
|
|
});
|
|
|
|
afterEach(function() {
|
|
fs.readFile.restore();
|
|
pin.emit.restore();
|
|
});
|
|
|
|
it("requests the pin value on the specified interval", function() {
|
|
pin.digitalRead(500);
|
|
this.clock.tick(510);
|
|
|
|
expect(fs.readFile).to.be.calledOnce;
|
|
|
|
this.clock.tick(500);
|
|
expect(fs.readFile).to.be.calledTwice;
|
|
});
|
|
|
|
it("emits a 'digitalRead' event with the data recieved", function() {
|
|
pin.digitalRead(500);
|
|
this.clock.tick(510);
|
|
|
|
expect(pin.emit).to.be.calledWith("digitalRead", 1);
|
|
});
|
|
});
|
|
|
|
context("when an error occurs", function() {
|
|
beforeEach(function() {
|
|
stub(fs, "readFile").callsArgWith(1, true, null);
|
|
stub(pin, "emit");
|
|
});
|
|
|
|
afterEach(function() {
|
|
fs.readFile.restore();
|
|
pin.emit.restore();
|
|
});
|
|
|
|
it("emits an error message", function() {
|
|
pin.digitalRead(500);
|
|
this.clock.tick(500);
|
|
|
|
expect(pin.emit).to.be.calledWith("error");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("#setHigh", function() {
|
|
beforeEach(function() {
|
|
stub(pin, "digitalWrite");
|
|
});
|
|
|
|
afterEach(function() {
|
|
pin.digitalWrite.restore();
|
|
});
|
|
|
|
it("calls #digitalWrite with a value of 1", function() {
|
|
pin.setHigh();
|
|
expect(pin.digitalWrite).to.be.calledWith(1);
|
|
});
|
|
});
|
|
|
|
describe("#setLow", function() {
|
|
beforeEach(function() {
|
|
stub(pin, "digitalWrite");
|
|
});
|
|
|
|
afterEach(function() {
|
|
pin.digitalWrite.restore();
|
|
});
|
|
|
|
it("calls #digitalWrite with a value of 0", function() {
|
|
pin.setLow();
|
|
expect(pin.digitalWrite).to.be.calledWith(0);
|
|
});
|
|
});
|
|
|
|
describe("#toggle", function() {
|
|
context("when @status is 'high'", function() {
|
|
beforeEach(function() {
|
|
stub(pin, "setLow");
|
|
pin.status = "high";
|
|
});
|
|
|
|
afterEach(function() {
|
|
pin.setLow.restore();
|
|
});
|
|
|
|
it("calls #setLow", function() {
|
|
pin.toggle();
|
|
expect(pin.setLow).to.be.called;
|
|
});
|
|
});
|
|
|
|
context("when @status is 'low'", function() {
|
|
beforeEach(function() {
|
|
stub(pin, "setHigh");
|
|
pin.status = "low";
|
|
});
|
|
|
|
afterEach(function() {
|
|
pin.setHigh.restore();
|
|
});
|
|
|
|
it("calls #setHigh", function() {
|
|
pin.toggle();
|
|
expect(pin.setHigh).to.be.called;
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("#_createGPIOPin", function() {
|
|
var path = "/sys/class/gpio/export";
|
|
|
|
context("when successful", function() {
|
|
beforeEach(function() {
|
|
stub(fs, "writeFile").callsArgWith(2, null);
|
|
stub(pin, "_openPin");
|
|
});
|
|
|
|
afterEach(function() {
|
|
fs.writeFile.restore();
|
|
pin._openPin.restore();
|
|
});
|
|
|
|
it("writes the pin number to the GPIO export path", function() {
|
|
pin._createGPIOPin();
|
|
expect(fs.writeFile).to.be.calledWith(path, "4");
|
|
});
|
|
|
|
it("calls #_openPin", function() {
|
|
pin._createGPIOPin();
|
|
expect(pin._openPin).to.be.called;
|
|
});
|
|
});
|
|
|
|
context("when an error occurs", function() {
|
|
beforeEach(function() {
|
|
stub(fs, "writeFile").callsArgWith(2, true);
|
|
stub(pin, "emit");
|
|
});
|
|
|
|
afterEach(function() {
|
|
fs.writeFile.restore();
|
|
pin.emit.restore();
|
|
});
|
|
|
|
it("emits an error", function() {
|
|
pin._createGPIOPin();
|
|
expect(pin.emit).to.be.calledWith("error");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("#_openPin", function() {
|
|
beforeEach(function() {
|
|
stub(pin, "_setMode");
|
|
stub(pin, "emit");
|
|
});
|
|
|
|
afterEach(function() {
|
|
pin._setMode.restore();
|
|
pin.emit.restore();
|
|
});
|
|
|
|
it("sets the pin's mode", function() {
|
|
pin._openPin();
|
|
expect(pin._setMode).to.be.calledWith(pin.mode);
|
|
});
|
|
|
|
it("emits the 'open' event", function() {
|
|
pin._openPin();
|
|
expect(pin.emit).to.be.calledWith("open");
|
|
});
|
|
});
|
|
|
|
describe("_closeCallback", function() {
|
|
context("if there is an error", function() {
|
|
beforeEach(function() {
|
|
stub(pin, "emit");
|
|
pin._closeCallback(true);
|
|
});
|
|
|
|
afterEach(function() {
|
|
pin.emit.restore();
|
|
});
|
|
|
|
it("emits an error", function() {
|
|
expect(pin.emit).to.be.calledWith("error");
|
|
});
|
|
});
|
|
|
|
context("if there is no error", function() {
|
|
beforeEach(function() {
|
|
stub(pin, "emit");
|
|
pin._closeCallback(false);
|
|
});
|
|
|
|
afterEach(function() {
|
|
pin.emit.restore();
|
|
});
|
|
|
|
it("emits a 'close' event with the pin number", function() {
|
|
expect(pin.emit).to.be.calledWith("close", "4");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("#_setMode", function() {
|
|
var path = "/sys/class/gpio/gpio4/direction";
|
|
|
|
beforeEach(function() {
|
|
stub(fs, "writeFile").callsArgWith(2, "error");
|
|
stub(pin, "_setModeCallback");
|
|
});
|
|
|
|
afterEach(function() {
|
|
fs.writeFile.restore();
|
|
pin._setModeCallback.restore();
|
|
});
|
|
|
|
context("when mode is 'w'", function() {
|
|
it("writes to the pin's direction path with 'out'", function() {
|
|
pin._setMode("w");
|
|
expect(fs.writeFile).to.be.calledWith(path, "out");
|
|
});
|
|
|
|
it("calls #_setModeCallback with any error message", function() {
|
|
pin._setMode("w", true);
|
|
expect(pin._setModeCallback).to.be.calledWith("error", true);
|
|
});
|
|
});
|
|
|
|
context("when mode is 'r'", function() {
|
|
it("writes to the pin's direction path with 'in'", function() {
|
|
pin._setMode("r");
|
|
expect(fs.writeFile).to.be.calledWith(path, "in");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("#_setModeCallback", function() {
|
|
beforeEach(function() {
|
|
stub(pin, "emit");
|
|
});
|
|
|
|
afterEach(function() {
|
|
pin.emit.restore();
|
|
});
|
|
|
|
context("when successful", function() {
|
|
it("sets @ready to true", function() {
|
|
pin.ready = false;
|
|
pin._setModeCallback(false);
|
|
expect(pin.ready).to.be.eql(true);
|
|
});
|
|
|
|
context("when emitConnect is true", function() {
|
|
it("emits a 'connect' event with the pin's mode", function() {
|
|
pin._setModeCallback(false, true);
|
|
expect(pin.emit).to.be.calledWith("connect", pin.mode);
|
|
});
|
|
});
|
|
});
|
|
|
|
context("when passed an error", function() {
|
|
it("emits an error", function() {
|
|
pin._setModeCallback(true);
|
|
expect(pin.emit).to.be.calledWith("error");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("#_pinPath", function() {
|
|
var path = "/sys/class/gpio/gpio4";
|
|
|
|
it("returns the path to the GPIO pin", function() {
|
|
expect(pin._pinPath()).to.be.eql(path);
|
|
});
|
|
});
|
|
|
|
describe("#_directionPath", function() {
|
|
var path = "/sys/class/gpio/gpio4/direction";
|
|
|
|
it("returns the path to the GPIO pin's direction file", function() {
|
|
expect(pin._directionPath()).to.be.eql(path);
|
|
});
|
|
});
|
|
|
|
describe("#_valuePath", function() {
|
|
var path = "/sys/class/gpio/gpio4/value";
|
|
|
|
it("returns the path to the GPIO pin's value file", function() {
|
|
expect(pin._valuePath()).to.be.eql(path);
|
|
});
|
|
});
|
|
|
|
describe("#_exportPath", function() {
|
|
var path = "/sys/class/gpio/export";
|
|
|
|
it("returns the GPIO export path", function() {
|
|
expect(pin._exportPath()).to.be.eql(path);
|
|
});
|
|
});
|
|
|
|
describe("#_unexportPath", function() {
|
|
var path = "/sys/class/gpio/unexport";
|
|
|
|
it("returns the GPIO unexport path", function() {
|
|
expect(pin._unexportPath()).to.be.eql(path);
|
|
});
|
|
});
|
|
});
|