UNPKG

inventoresed

Version:

Z-Wave driver written entirely in JavaScript/TypeScript

221 lines (189 loc) 5.4 kB
import { wait } from "alcalzone-shared/async"; import { PassThrough } from "stream"; import { MessageHeaders } from "./MessageHeaders"; import { createAndOpenMockedZWaveSerialPort } from "./MockSerialPort"; import type { MockPortBinding } from "./SerialPortBindingMock"; import type { ZWaveSerialPort } from "./ZWaveSerialPort"; async function waitForData(port: { once: (event: "data", callback: (data: any) => void) => any; }): Promise< MessageHeaders.ACK | MessageHeaders.NAK | MessageHeaders.CAN | Buffer > { return new Promise((resolve) => { port.once("data", resolve); }); } describe("ZWaveSerialPort", () => { let port: ZWaveSerialPort; let binding: MockPortBinding; beforeEach(async () => { ({ port, binding } = await createAndOpenMockedZWaveSerialPort( "/dev/zwavetest", )); }); afterEach(async () => { port.removeAllListeners(); if (port.isOpen) await port.close(); }); it("isOpen returns true after opening", () => { expect(port.isOpen).toBeTrue(); }); it("isOpen returns false after closing", async () => { await port.close(); expect(port.isOpen).toBeFalse(); }); it("passes written data through unchanged", async () => { const buffers = [ Buffer.from([1, 2, 3]), Buffer.from("abcdef1234567890", "hex"), ]; for (const buffer of buffers) { await port.writeAsync(buffer); expect(binding.lastWrite).toEqual(buffer); } }); it("write rejects if the port is not open", async () => { await port.close(); await expect( port.writeAsync(Buffer.from([MessageHeaders.ACK])), ).toReject(); }); it("emit an event for each single-byte message that was read", async () => { binding.emitData(Buffer.from([MessageHeaders.ACK])); let data = await waitForData(port); expect(data).toEqual(MessageHeaders.ACK); binding.emitData(Buffer.from([MessageHeaders.CAN])); data = await waitForData(port); expect(data).toEqual(MessageHeaders.CAN); binding.emitData(Buffer.from([MessageHeaders.NAK])); data = await waitForData(port); expect(data).toEqual(MessageHeaders.NAK); }); it("emits a series of events when multiple single-byte messages are received", (done) => { binding.emitData( Buffer.from([ MessageHeaders.ACK, MessageHeaders.CAN, MessageHeaders.NAK, ]), ); let count = 0; port.on("data", (data) => { count++; if (count === 1) expect(data).toBe(MessageHeaders.ACK); if (count === 2) expect(data).toBe(MessageHeaders.CAN); if (count === 3) expect(data).toBe(MessageHeaders.NAK); if (count === 3) done(); }); }); it("skips all invalid/unexpected data", (done) => { binding.emitData( Buffer.from([ MessageHeaders.ACK, MessageHeaders.CAN, 0xff, 0xfe, 0xfd, 0xfa, MessageHeaders.ACK, ]), ); let count = 0; port.on("data", (data) => { count++; if (count === 1) expect(data).toBe(MessageHeaders.ACK); if (count === 2) expect(data).toBe(MessageHeaders.CAN); if (count === 3) expect(data).toBe(MessageHeaders.ACK); if (count === 3) done(); }); }); it("skips all invalid/unexpected data (test 2)", (done) => { binding.emitData( Buffer.from([ MessageHeaders.ACK, MessageHeaders.CAN, 0xff, 0xfe, 0xfd, 0xfa, ]), ); setTimeout(() => { binding.emitData(Buffer.from([MessageHeaders.NAK])); }, 10); let count = 0; port.on("data", (data) => { count++; if (count === 1) expect(data).toBe(MessageHeaders.ACK); if (count === 2) expect(data).toBe(MessageHeaders.CAN); if (count === 3) expect(data).toBe(MessageHeaders.NAK); if (count === 3) done(); }); }); it("emits a buffer when a message is received", async () => { const data = Buffer.from([ MessageHeaders.SOF, 0x05, // remaining length 0xff, 0xff, 0xff, 0xff, 0xff, ]); binding.emitData(data); const received = await waitForData(port); expect(received).toEqual(data); }); it("may be consumed with an async iterator", async () => { const data = Buffer.from([ MessageHeaders.ACK, MessageHeaders.CAN, 0xff, 0xfe, 0xfd, 0xfa, MessageHeaders.ACK, ]); binding.emitData(data); let count = 0; for await (const msg of port) { count++; if (count === 1) expect(msg).toBe(MessageHeaders.ACK); if (count === 2) expect(msg).toBe(MessageHeaders.CAN); if (count === 3) expect(msg).toBe(MessageHeaders.ACK); if (count === 3) break; } }); it("can be piped into", (done) => { const passThrough = new PassThrough(); passThrough.pipe(port); const data = Buffer.from([1, 2, 3, 4, 5]); passThrough.write(data, (err) => { expect(err).toBeFalsy(); // I see no better way of forcing the write to bubble through the streams setTimeout(() => { expect(binding.lastWrite).toEqual(data); done(); }, 1); }); }); it("can be piped to a reader", async () => { const stream = new PassThrough(); port.pipe(stream); const expected = Buffer.from([0x01, 0x03, 0xff, 0xff, 0xff]); binding.emitData(expected); const data = await waitForData(stream); expect(data).toEqual(expected); }); it("can be unpiped again", async () => { const stream = new PassThrough(); const spy = jest.fn(); stream.on("data", spy); port.pipe(stream); port.unpipe(); const expected = Buffer.from([0x01, 0x03, 0xff, 0xff, 0xff]); binding.emitData(expected); await wait(1); expect(spy).not.toBeCalled(); }); });