johnny-five-electron
Version:
Temporary fork to support Electron (to be deprecated)
981 lines (755 loc) • 22.2 kB
JavaScript
var MockFirmata = require("./util/mock-firmata");
var five = require("../lib/johnny-five.js");
var lcdChars = require("../lib/lcd-chars.js");
var sinon = require("sinon");
var util = require("util");
var Board = five.Board;
var LCD = five.LCD;
function newBoard() {
var io = new MockFirmata();
var board = new Board({
io: io,
debug: false,
repl: false
});
io.emit("connect");
io.emit("ready");
return board;
}
function restore(target) {
for (var prop in target) {
if (Array.isArray(target[prop])) {
continue;
}
if (target[prop] != null && typeof target[prop].restore === "function") {
target[prop].restore();
}
if (typeof target[prop] === "object") {
restore(target[prop]);
}
}
}
exports["LCD.Characters"] = {
setUp: function(done) {
done();
},
tearDown: function(done) {
Board.purge();
restore(this);
done();
},
exists: function(test) {
test.expect(1);
test.deepEqual(LCD.Characters, lcdChars);
test.done();
},
};
exports["LCD"] = {
setUp: function(done) {
this.board = newBoard();
this.digitalWrite = sinon.spy(MockFirmata.prototype, "digitalWrite");
this.lcd = new LCD({
pins: [7, 8, 9, 10, 11, 12],
board: this.board
});
this.proto = [{
name: "autoscroll"
}, {
name: "blink"
}, {
name: "clear"
}, {
name: "command"
}, {
name: "createChar"
}, {
name: "cursor"
}, {
name: "hilo"
}, {
name: "home"
}, {
name: "noAutoscroll"
}, {
name: "noBlink"
}, {
name: "noCursor"
}, {
name: "on"
}, {
name: "off"
}, {
name: "print"
}, {
name: "setCursor"
}, {
name: "useChar"
}, {
name: "write"
}, {
name: "send"
}];
this.instance = [{
name: "bitMode"
}, {
name: "cols"
}, {
name: "dots"
}, {
name: "id"
}, {
name: "lines"
}, {
name: "pins"
}, {
name: "rows"
}];
done();
},
tearDown: function(done) {
Board.purge();
restore(this);
done();
},
shape: function(test) {
test.expect(this.proto.length + this.instance.length);
this.proto.forEach(function(method) {
test.equal(typeof this.lcd[method.name], "function");
}, this);
this.instance.forEach(function(property) {
test.notEqual(typeof this.lcd[property.name], "undefined");
}, this);
test.done();
},
send: function(test) {
test.expect(4);
this.digitalWrite.reset();
this.lcd.send(10);
test.deepEqual(this.digitalWrite.args[0], [12, 1]);
test.deepEqual(this.digitalWrite.args[1], [11, 0]);
test.deepEqual(this.digitalWrite.args[2], [10, 1]);
test.deepEqual(this.digitalWrite.args[3], [9, 0]);
test.done();
},
command: function(test) {
test.expect(10);
var wbStub = sinon.stub(this.lcd, "send");
this.lcd.command(15);
test.ok(wbStub.calledTwice);
test.deepEqual(wbStub.args[0], [0]);
test.deepEqual(wbStub.args[1], [15]);
wbStub.reset();
this.lcd.command(32);
test.ok(wbStub.calledTwice);
test.deepEqual(wbStub.args[0], [2]);
test.deepEqual(wbStub.args[1], [32]);
this.lcd.bitMode = 8;
wbStub.reset();
this.lcd.command(15);
test.ok(wbStub.calledOnce);
test.deepEqual(wbStub.args[0], [15]);
wbStub.reset();
this.lcd.command(32);
test.ok(wbStub.calledOnce);
test.deepEqual(wbStub.args[0], [32]);
test.done();
},
write: function(test) {
test.expect(3);
var cSpy = sinon.spy(this.lcd, "command");
var hiloSpy = sinon.spy(this.lcd, "hilo");
this.lcd.write(42);
test.ok(hiloSpy.calledOn(this.lcd));
test.ok(cSpy.calledOnce);
test.deepEqual(cSpy.args[0], [0x40, 42]);
hiloSpy.restore();
test.done();
},
cursor: function(test) {
test.expect(6);
var scSpy = sinon.stub(this.lcd, "setCursor");
var cSpy = sinon.stub(this.lcd, "command");
this.lcd.cursor();
test.ok(!scSpy.called);
test.ok(cSpy.calledOnce);
test.ok(cSpy.firstCall.args[0] & this.lcd.OP.CURSORON, "command not called with this.lcd.OP.CURSORON bit high");
cSpy.reset();
this.lcd.cursor(1, 1);
test.ok(scSpy.calledOnce);
test.deepEqual(scSpy.args[0], [1, 1]);
test.ok(!cSpy.called);
test.done();
},
noCursor: function(test) {
test.expect(2);
var cSpy = sinon.stub(this.lcd, "command");
this.lcd.noCursor();
test.ok(cSpy.calledOnce);
test.ok(0 === (cSpy.firstCall.args[0] & this.lcd.OP.CURSORON), "command not called with this.lcd.OP.CURSORON bit low");
test.done();
},
createChar: function(test) {
test.expect(143);
// Numbers capped to 7, direct addresses, proper commands
var cSpy = sinon.spy(this.lcd, "command");
var charMap = [0, 1, 2, 3, 4, 5, 6, 7];
for (var num = 0; num <= 8; ++num) {
cSpy.reset();
test.strictEqual(this.lcd.createChar(num, charMap), num & 7, "Incorrect returned address");
test.strictEqual(cSpy.callCount, 9, "Improper command call count");
test.ok(cSpy.firstCall.calledWith(this.lcd.OP.DATA | ((num > 7 ? num & 7 : num) << 3)),
"DATA mask is incorrect");
for (var i = 0, l = charMap.length; i < l; ++i) {
test.ok(cSpy.getCall(i + 1).calledWith(0x40, charMap[i]), "CharMap call #" + (i + 1) + " incorrect");
}
}
// Named-based: rotating addresses (from this.lcd.OP.MEMORYLIMIT -1 down), ignores existing name
["foo", "bar", "baz", "bar"].forEach(function(name, index) {
cSpy.reset();
var addr = this.lcd.OP.MEMORYLIMIT - (1 + index % this.lcd.OP.MEMORYLIMIT);
test.strictEqual(this.lcd.createChar(name, charMap), addr, "Incorrect returned address");
test.strictEqual(cSpy.callCount, 9, "Improper command call count");
test.ok(cSpy.firstCall.calledWith(this.lcd.OP.DATA | (addr << 3)),
"DATA mask is incorrect");
for (var i = 0, l = charMap.length; i < l; ++i) {
test.ok(cSpy.getCall(i + 1).calledWith(0x40, charMap[i]), "CharMap call #" + (i + 1) + " incorrect");
}
}, this);
test.done();
},
useChar: function(test) {
test.expect(3);
var ccSpy = sinon.spy(this.lcd, "createChar");
this.lcd.useChar("heart");
test.ok(ccSpy.firstCall.calledWith("heart"));
ccSpy.reset();
this.lcd.useChar("heart");
test.strictEqual(ccSpy.callCount, 0, "createChar should not have been called on an existing name");
test.equal(this.lcd, this.lcd.useChar("heart"));
test.done();
},
printRegularTexts: function(test) {
// No test.expect() as these are a bit cumbersome/coupled to obtain
var sentences = ["hello world", "", " ", " hello ", " hello "];
var cSpy = sinon.spy(this.lcd, "command");
sentences.forEach(function(text) {
var comparison = text;
cSpy.reset();
this.lcd.print(text);
test.strictEqual(cSpy.callCount, comparison.length, "Unexpected amount of #command calls");
for (var i = 0, l = comparison.length; i < l; ++i) {
test.strictEqual(cSpy.getCall(i).args[1], comparison.charCodeAt(i),
"Unexpected byte #" + i + " on " + util.inspect(text) + " (comparing with " +
util.inspect(comparison) + ")");
}
}, this);
test.done();
},
printSpecialTexts: function(test) {
// No test.expect() as these are a bit cumbersome/coupled to obtain
// These assume this.lcd.OP.MEMORYLIMIT is 8, for readability
var sentences = [
[":heart:", "\07"],
[":heart: JS", "\07 JS"],
[":heart:JS", "\07JS"],
["JS :heart:", "JS \07"],
["JS:heart:", "JS\07"],
["I :heart: JS", "I \07 JS"],
["I:heart:JS", "I\07JS"],
["I :heart: JS :smile:", "I \07 JS \06"],
["I:heart:JS :smile:", "I\07JS \06"],
["I :heart::heart::heart: JS :smile: !", "I \07\07\07 JS \06 !"],
["I :heart: :unknown: symbols", "I \07 :unknown: symbols"]
];
sentences.forEach(function(pair) {
var text = pair[0],
comparison = pair[1];
(text.match(/:\w+?:/g) || []).forEach(function(match) {
if (":unknown:" !== match) {
this.lcd.useChar(match.slice(1, -1));
}
}, this);
var cSpy = sinon.spy(this.lcd, "command");
this.lcd.print(text);
test.strictEqual(cSpy.callCount, comparison.length,
"Unexpected amount of #command calls for " + util.inspect(text));
var i, output = "";
for (i = 0; i < cSpy.callCount; ++i) {
output += String.fromCharCode(cSpy.getCall(i).args[1]);
}
for (i = 0; i < cSpy.callCount; ++i) {
test.strictEqual(cSpy.getCall(i).args[1], comparison.charCodeAt(i),
"Unexpected byte #" + i + " on " + util.inspect(text) +
" (comparing " + util.inspect(output) + " with " +
util.inspect(comparison) + ")");
}
cSpy.restore();
}, this);
test.done();
}
// TODO: Remaining tests: clear, home, display/noDisplay, blink/noBlink, setCursor, pulse, autoscroll/noAutoscroll
};
exports["LCD - I2C (JHD1313M1)"] = {
// TODO: Move all stubs and spies into setup
setUp: function(done) {
this.board = newBoard();
this.i2cConfig = sinon.spy(MockFirmata.prototype, "i2cConfig");
this.i2cWrite = sinon.spy(MockFirmata.prototype, "i2cWrite");
done();
},
tearDown: function(done) {
Board.purge();
restore(this);
done();
},
fwdOptionsToi2cConfig: function(test) {
test.expect(3);
this.i2cConfig.reset();
new LCD({
controller: "JHD1313M1",
address: 0xff,
bus: "i2c-1",
board: this.board
});
var forwarded = this.i2cConfig.lastCall.args[0];
test.equal(this.i2cConfig.callCount, 1);
test.equal(forwarded.address, 0xff);
test.equal(forwarded.bus, "i2c-1");
test.done();
},
initialization: function(test) {
test.expect(1);
// TODO:
// This needs to more thoroughly test
// the expected initialization for the
// specified device.
//
new LCD({
controller: "JHD1313M1",
board: this.board
});
test.equal(this.i2cWrite.callCount, 14);
test.done();
},
command: function(test) {
test.expect(2);
var lcd = new LCD({
controller: "JHD1313M1",
board: this.board
});
lcd.command(15);
test.equal(this.i2cWrite.called, 1);
test.deepEqual(this.i2cWrite.lastCall.args, [ 62, [ 128, 15 ] ]);
test.done();
},
send: function(test) {
test.expect(2);
var lcd = new LCD({
controller: "JHD1313M1",
board: this.board
});
lcd.send(15);
test.equal(this.i2cWrite.called, 1);
test.deepEqual(this.i2cWrite.lastCall.args, [ 62, [ 64, 15 ] ]);
test.done();
}
};
exports["LCD - I2C (LCD2004)"] = {
setUp: function(done) {
this.board = newBoard();
this.i2cConfig = sinon.spy(MockFirmata.prototype, "i2cConfig");
this.i2cWrite = sinon.spy(MockFirmata.prototype, "i2cWrite");
done();
},
tearDown: function(done) {
Board.purge();
restore(this);
done();
},
fwdOptionsToi2cConfig: function(test) {
test.expect(3);
this.i2cConfig.reset();
new LCD({
controller: "LCD2004",
address: 0xff,
bus: "i2c-1",
board: this.board
});
var forwarded = this.i2cConfig.lastCall.args[0];
test.equal(this.i2cConfig.callCount, 1);
test.equal(forwarded.address, 0xff);
test.equal(forwarded.bus, "i2c-1");
test.done();
},
initialization: function(test) {
test.expect(1);
// TODO:
// This needs to more thoroughly test
// the expected initialization for the
// specified device.
//
new LCD({
controller: "LCD2004",
board: this.board
});
test.equal(this.i2cWrite.callCount, 30);
test.done();
},
command: function(test) {
test.expect(7);
var lcd = new LCD({
controller: "LCD2004",
board: this.board
});
var send = sinon.spy(lcd, "send");
var writeBits = sinon.spy(lcd, "writeBits");
var pulse = sinon.spy(lcd, "pulse");
// Prevent inclusion of initialization-related writes.
this.i2cWrite.reset();
lcd.command(15);
test.equal(send.callCount, 1);
test.deepEqual(send.getCall(0).args, [0, 15]);
test.equal(writeBits.callCount, 2);
test.ok(pulse.called);
test.equal(pulse.callCount, 2);
test.equal(this.i2cWrite.callCount, 4);
test.deepEqual(this.i2cWrite.args, [ [ 39, 12 ], [ 39, 8 ], [ 39, 252 ], [ 39, 248 ] ]);
test.done();
},
send: function(test) {
test.expect(6);
var lcd = new LCD({
controller: "LCD2004",
board: this.board
});
var send = sinon.spy(lcd, "send");
var writeBits = sinon.spy(lcd, "writeBits");
var pulse = sinon.spy(lcd, "pulse");
// Prevent inclusion of initialization-related writes.
this.i2cWrite.reset();
lcd.send(0, 15);
test.equal(send.callCount, 1);
test.deepEqual(send.getCall(0).args, [0, 15]);
test.equal(writeBits.callCount, 2);
test.equal(pulse.callCount, 2);
test.equal(this.i2cWrite.callCount, 4);
test.deepEqual(this.i2cWrite.args, [ [ 39, 12 ], [ 39, 8 ], [ 39, 252 ], [ 39, 248 ] ]);
test.done();
}
};
exports["LCD - I2C (LCM1602)"] = {
setUp: function(done) {
this.board = newBoard();
this.i2cConfig = sinon.spy(MockFirmata.prototype, "i2cConfig");
this.i2cWrite = sinon.spy(MockFirmata.prototype, "i2cWrite");
done();
},
tearDown: function(done) {
Board.purge();
restore(this);
done();
},
fwdOptionsToi2cConfig: function(test) {
test.expect(3);
this.i2cConfig.reset();
new LCD({
controller: "LCM1602",
address: 0xff,
bus: "i2c-1",
board: this.board
});
var forwarded = this.i2cConfig.lastCall.args[0];
test.equal(this.i2cConfig.callCount, 1);
test.equal(forwarded.address, 0xff);
test.equal(forwarded.bus, "i2c-1");
test.done();
},
initialization: function(test) {
test.expect(1);
// TODO:
// This needs to more thoroughly test
// the expected initialization for the
// specified device.
//
new LCD({
controller: "LCM1602",
board: this.board
});
test.equal(this.i2cWrite.callCount, 30);
test.done();
},
command: function(test) {
test.expect(6);
var lcd = new LCD({
controller: "LCM1602",
board: this.board
});
var send = sinon.spy(lcd, "send");
var writeBits = sinon.spy(lcd, "writeBits");
var pulse = sinon.spy(lcd, "pulse");
// Prevent inclusion of initialization-related writes.
this.i2cWrite.reset();
lcd.command(15);
test.equal(send.callCount, 1);
test.deepEqual(send.getCall(0).args, [0, 15]);
test.equal(writeBits.callCount, 2);
test.equal(pulse.callCount, 2);
test.equal(this.i2cWrite.callCount, 4);
test.deepEqual(this.i2cWrite.args, [ [ 39, 12 ], [ 39, 8 ], [ 39, 252 ], [ 39, 248 ] ]);
test.done();
},
send: function(test) {
test.expect(6);
var lcd = new LCD({
controller: "LCM1602",
board: this.board
});
var send = sinon.spy(lcd, "send");
var writeBits = sinon.spy(lcd, "writeBits");
var pulse = sinon.spy(lcd, "pulse");
// Prevent inclusion of initialization-related writes.
this.i2cWrite.reset();
lcd.send(0, 15);
test.equal(send.callCount, 1);
test.deepEqual(send.getCall(0).args, [0, 15]);
test.equal(writeBits.callCount, 2);
test.equal(pulse.callCount, 2);
test.equal(this.i2cWrite.callCount, 4);
test.deepEqual(this.i2cWrite.args, [ [ 39, 12 ], [ 39, 8 ], [ 39, 252 ], [ 39, 248 ] ]);
test.done();
}
};
exports["LCD - PCF8574"] = {
setUp: function(done) {
this.board = newBoard();
this.i2cConfig = sinon.spy(MockFirmata.prototype, "i2cConfig");
this.i2cWrite = sinon.spy(MockFirmata.prototype, "i2cWrite");
done();
},
tearDown: function(done) {
Board.purge();
restore(this);
done();
},
fwdOptionsToi2cConfig: function(test) {
test.expect(3);
this.i2cConfig.reset();
new LCD({
controller: "PCF8574",
address: 0xff,
bus: "i2c-1",
board: this.board
});
var forwarded = this.i2cConfig.lastCall.args[0];
test.equal(this.i2cConfig.callCount, 1);
test.equal(forwarded.address, 0xff);
test.equal(forwarded.bus, "i2c-1");
test.done();
},
initialization: function(test) {
test.expect(2);
// TODO:
// This needs to more thoroughly test
// the expected initialization for the
// specified device.
//
new LCD({
controller: "PCF8574",
board: this.board
});
test.equal(this.i2cWrite.callCount, 30);
// This is the expected write sequence.
// If this changes, the controller will not function.
var sequence = [
[ 39, 0 ],
[ 39, 48 ],
[ 39, 52 ],
[ 39, 48 ],
[ 39, 48 ],
[ 39, 52 ],
[ 39, 48 ],
[ 39, 48 ],
[ 39, 52 ],
[ 39, 48 ],
[ 39, 32 ],
[ 39, 36 ],
[ 39, 32 ],
[ 39, 36 ],
[ 39, 32 ],
[ 39, 132 ],
[ 39, 128 ],
[ 39, 4 ],
[ 39, 0 ],
[ 39, 196 ],
[ 39, 192 ],
[ 39, 4 ],
[ 39, 0 ],
[ 39, 20 ],
[ 39, 16 ],
[ 39, 4 ],
[ 39, 0 ],
[ 39, 100 ],
[ 39, 96 ],
[ 39, 8 ]
];
test.deepEqual(this.i2cWrite.args, sequence);
test.done();
},
command: function(test) {
test.expect(6);
var lcd = new LCD({
controller: "PCF8574",
board: this.board
});
var send = sinon.spy(lcd, "send");
var writeBits = sinon.spy(lcd, "writeBits");
var pulse = sinon.spy(lcd, "pulse");
// Prevent inclusion of initialization-related writes.
this.i2cWrite.reset();
lcd.command(15);
test.equal(send.callCount, 1);
test.deepEqual(send.getCall(0).args, [0, 15]);
test.equal(writeBits.callCount, 2);
test.equal(pulse.callCount, 2);
test.equal(this.i2cWrite.callCount, 4);
test.deepEqual(this.i2cWrite.args, [ [ 39, 12 ], [ 39, 8 ], [ 39, 252 ], [ 39, 248 ] ]);
test.done();
},
send: function(test) {
test.expect(6);
var lcd = new LCD({
controller: "PCF8574",
board: this.board
});
var send = sinon.spy(lcd, "send");
var writeBits = sinon.spy(lcd, "writeBits");
var pulse = sinon.spy(lcd, "pulse");
// Prevent inclusion of initialization-related writes.
this.i2cWrite.reset();
lcd.send(0, 15);
test.equal(send.callCount, 1);
test.deepEqual(send.getCall(0).args, [0, 15]);
test.equal(writeBits.callCount, 2);
test.equal(pulse.callCount, 2);
test.equal(this.i2cWrite.callCount, 4);
test.deepEqual(this.i2cWrite.args, [ [ 39, 12 ], [ 39, 8 ], [ 39, 252 ], [ 39, 248 ] ]);
test.done();
}
};
exports["LCD - MJKDZ"] = {
setUp: function(done) {
this.board = newBoard();
this.i2cConfig = sinon.spy(MockFirmata.prototype, "i2cConfig");
this.i2cWrite = sinon.spy(MockFirmata.prototype, "i2cWrite");
done();
},
tearDown: function(done) {
Board.purge();
restore(this);
done();
},
fwdOptionsToi2cConfig: function(test) {
test.expect(3);
this.i2cConfig.reset();
new LCD({
controller: "MJKDZ",
address: 0xff,
bus: "i2c-1",
board: this.board
});
var forwarded = this.i2cConfig.lastCall.args[0];
test.equal(this.i2cConfig.callCount, 1);
test.equal(forwarded.address, 0xff);
test.equal(forwarded.bus, "i2c-1");
test.done();
},
initialization: function(test) {
test.expect(2);
// TODO:
// This needs to more thoroughly test
// the expected initialization for the
// specified device.
//
new LCD({
controller: "MJKDZ",
board: this.board
});
test.equal(this.i2cWrite.callCount, 30);
// This is the expected write sequence.
// If this changes, the controller will not function.
var sequence = [
[ 39, 0 ],
[ 39, 3 ],
[ 39, 19 ],
[ 39, 3 ],
[ 39, 3 ],
[ 39, 19 ],
[ 39, 3 ],
[ 39, 3 ],
[ 39, 19 ],
[ 39, 3 ],
[ 39, 2 ],
[ 39, 18 ],
[ 39, 2 ],
[ 39, 18 ],
[ 39, 2 ],
[ 39, 24 ],
[ 39, 8 ],
[ 39, 16 ],
[ 39, 0 ],
[ 39, 28 ],
[ 39, 12 ],
[ 39, 16 ],
[ 39, 0 ],
[ 39, 17 ],
[ 39, 1 ],
[ 39, 16 ],
[ 39, 0 ],
[ 39, 22 ],
[ 39, 6 ],
[ 39, 8 ]
];
test.deepEqual(this.i2cWrite.args, sequence);
test.done();
},
command: function(test) {
test.expect(6);
var lcd = new LCD({
controller: "MJKDZ",
board: this.board
});
var send = sinon.spy(lcd, "send");
var writeBits = sinon.spy(lcd, "writeBits");
var pulse = sinon.spy(lcd, "pulse");
// Prevent inclusion of initialization-related writes.
this.i2cWrite.reset();
lcd.command(15);
test.equal(send.callCount, 1);
test.deepEqual(send.getCall(0).args, [0, 15]);
test.equal(writeBits.callCount, 2);
test.equal(pulse.callCount, 2);
test.equal(this.i2cWrite.callCount, 4);
test.deepEqual(this.i2cWrite.args, [ [ 39, 24 ], [ 39, 8 ], [ 39, 31 ], [ 39, 15 ] ]);
test.done();
},
send: function(test) {
test.expect(6);
var lcd = new LCD({
controller: "MJKDZ",
board: this.board
});
var send = sinon.spy(lcd, "send");
var writeBits = sinon.spy(lcd, "writeBits");
var pulse = sinon.spy(lcd, "pulse");
// Prevent inclusion of initialization-related writes.
this.i2cWrite.reset();
lcd.send(0, 15);
test.equal(send.callCount, 1);
test.deepEqual(send.getCall(0).args, [0, 15]);
test.equal(writeBits.callCount, 2);
test.equal(pulse.callCount, 2);
test.equal(this.i2cWrite.callCount, 4);
test.deepEqual(this.i2cWrite.args, [ [ 39, 24 ], [ 39, 8 ], [ 39, 31 ], [ 39, 15 ] ]);
test.done();
}
};