UNPKG

zigbee-herdsman

Version:

An open source ZigBee gateway solution with node.js.

181 lines (163 loc) 7.14 kB
import {FrameType} from "../../../src/adapter/ezsp/driver/frame"; import {Parser} from "../../../src/adapter/ezsp/driver/parser"; import {SerialDriver} from "../../../src/adapter/ezsp/driver/uart"; import {Writer} from "../../../src/adapter/ezsp/driver/writer"; import {SerialPort} from "../../../src/adapter/serialPort"; const mockSerialPortClose = vi.fn().mockImplementation((cb) => (cb ? cb() : null)); const mockSerialPortFlush = vi.fn().mockImplementation((cb) => cb()); const mockSerialPortAsyncFlushAndClose = vi.fn(); const mockSerialPortPipe = vi.fn(); const mockSerialPortOpen = vi.fn().mockImplementation((cb) => cb()); const mockSerialPortAsyncOpen = vi.fn(); const mockSerialPortConstructor = vi.fn(); const mockSerialPortOnce = vi.fn(); const mockSerialPortSet = vi.fn().mockImplementation((_opts, cb) => cb()); const mockSerialPortWrite = vi.fn((_buffer, cb) => (cb ? cb() : null)); const mockSerialPortIsOpen = false; vi.mock("../../../src/adapter/serialPort", () => ({ SerialPort: vi.fn(() => ({ close: mockSerialPortClose, constructor: mockSerialPortConstructor, emit: () => {}, on: () => {}, once: mockSerialPortOnce, open: mockSerialPortOpen, pipe: mockSerialPortPipe, set: mockSerialPortSet, write: mockSerialPortWrite, flush: mockSerialPortFlush, isOpen: mockSerialPortIsOpen, asyncOpen: mockSerialPortAsyncOpen, asyncFlushAndClose: mockSerialPortAsyncFlushAndClose, })), })); vi.mock("../../../src/utils/wait", () => ({ wait: vi.fn(() => { return new Promise<void>((resolve) => resolve()); }), })); let writeBufferSpy; const mocks = [ mockSerialPortClose, mockSerialPortPipe, mockSerialPortConstructor, mockSerialPortOpen, mockSerialPortOnce, mockSerialPortWrite, SerialPort, mockSerialPortAsyncFlushAndClose, mockSerialPortAsyncOpen, ]; describe("UART", () => { let serialDriver; beforeAll(() => { vi.useFakeTimers(); }); afterAll(() => { vi.useRealTimers(); vi.restoreAllMocks(); }); beforeEach(() => { for (const mock of mocks) { // @ts-ignore mock.mockClear(); } // @ts-ignore; make sure we always get a new instance serialDriver = new SerialDriver(); writeBufferSpy = vi.spyOn(Writer.prototype, "writeBuffer").mockImplementation((buffer) => { if (buffer[0] === 0x1a) { serialDriver.waitress.resolve({sequence: -1}); } }); vi.spyOn(Writer.prototype, "pipe").mockImplementation(vi.fn()); }); afterEach(() => { writeBufferSpy.mockRestore(); }); it("Connect", async () => { await serialDriver.connect({path: "/dev/ttyACM0"}); expect(SerialPort).toHaveBeenCalledTimes(1); expect(SerialPort).toHaveBeenCalledWith({ path: "/dev/ttyACM0", baudRate: 115200, rtscts: false, autoOpen: false, parity: "none", stopBits: 1, xon: true, xoff: true, }); expect(mockSerialPortPipe).toHaveBeenCalledTimes(1); expect(mockSerialPortAsyncOpen).toHaveBeenCalledTimes(1); expect(mockSerialPortOnce).toHaveBeenCalledTimes(1); expect(writeBufferSpy).toHaveBeenCalledTimes(1); }); it("Send data", async () => { await serialDriver.connect("/dev/ttyACM0", {}); // send 8 frames serialDriver.sendDATA(Buffer.from([1, 2, 3])); serialDriver.sendDATA(Buffer.from([1, 2, 3])); serialDriver.sendDATA(Buffer.from([1, 2, 3])); serialDriver.sendDATA(Buffer.from([1, 2, 3])); serialDriver.sendDATA(Buffer.from([1, 2, 3])); serialDriver.sendDATA(Buffer.from([1, 2, 3])); serialDriver.sendDATA(Buffer.from([1, 2, 3])); serialDriver.sendDATA(Buffer.from([1, 2, 3])); expect(writeBufferSpy).toHaveBeenCalledTimes(2); // send another 2 frame - not counted, until resolve 8 promices serialDriver.sendDATA(Buffer.from([1, 2, 3])); serialDriver.sendDATA(Buffer.from([1, 2, 3])); expect(writeBufferSpy).toHaveBeenCalledTimes(2); }); it("Receive data", async () => { const parsed = []; const parser = new Parser(); parser.on("parsed", (result) => parsed.push(result)); // send 4 frames const buffer0 = Buffer.from([0xc1, 0x02, 0x0b, 0x0a, 0x52, 0x7e]); const buffer1 = Buffer.from([ 0x22, 0x5b, 0xb1, 0xa9, 0x0d, 0x2a, 0xc1, 0xd8, 0x19, 0x53, 0x4a, 0x14, 0xaa, 0xe9, 0x87, 0x49, 0xfc, 0xfa, 0x26, 0x7d, 0x5e, 0xc5, 0xaa, 0xc8, 0x7e, ]); const buffer2 = Buffer.from([ 0x32, 0x5b, 0xb1, 0xa9, 0x7d, 0x31, 0x2a, 0x15, 0xb6, 0x58, 0x8d, 0x4a, 0x06, 0xab, 0x55, 0x93, 0x49, 0x9c, 0x45, 0x7b, 0x7d, 0x38, 0x39, 0xa4, 0x98, 0x74, 0xf1, 0xd7, 0x26, 0x88, 0xfc, 0x6b, 0x2f, 0xf6, 0xe9, 0xc5, 0xde, 0x6b, 0x8f, 0xfb, 0xd8, 0xf9, 0x7e, ]); const buffer3 = Buffer.from([0xa2, 0x74, 0x58, 0x7e]); parser._transform(buffer0, "", () => {}); parser._transform(buffer1, "", () => {}); parser._transform(buffer2, "", () => {}); parser._transform(buffer3, "", () => {}); expect(parsed.length).toBe(4); expect(parsed[0].type).toBe(FrameType.RSTACK); expect(parsed[1].type).toBe(FrameType.DATA); expect(parsed[2].type).toBe(FrameType.DATA); expect(parsed[3].type).toBe(FrameType.NAK); }); it("Message in two chunks", async () => { const parsed = []; const parser = new Parser(); parser.on("parsed", (result) => parsed.push(result)); const buffer1 = Buffer.from([0x22, 0x5b, 0xb1, 0xa9, 0x0d, 0x2a, 0xc1, 0xd8, 0x19, 0x53, 0x4a, 0x14]); parser._transform(buffer1, "", () => {}); expect(parsed.length).toBe(0); const buffer2 = Buffer.from([0xaa, 0xe9, 0x87, 0x49, 0xfc, 0xfa, 0x26, 0x7d, 0x5e, 0xc5, 0xaa, 0xc8, 0x7e]); parser._transform(buffer2, "", () => {}); expect(parsed.length).toBe(1); expect(parsed[0].type).toBe(FrameType.DATA); }); it("Two messages in one chunk", async () => { const parsed = []; const parser = new Parser(); parser.on("parsed", (result) => parsed.push(result)); const buffer1 = Buffer.from([ 0x22, 0x5b, 0xb1, 0xa9, 0x0d, 0x2a, 0xc1, 0xd8, 0x19, 0x53, 0x4a, 0x14, 0xaa, 0xe9, 0x87, 0x49, 0xfc, 0xfa, 0x26, 0x7d, 0x5e, 0xc5, 0xaa, 0xc8, 0x7e, 0x32, 0x5b, 0xb1, 0xa9, 0x7d, 0x31, 0x2a, 0x15, 0xb6, 0x58, 0x8d, 0x4a, 0x06, 0xab, 0x55, 0x93, 0x49, 0x9c, 0x45, 0x7b, 0x7d, 0x38, 0x39, 0xa4, 0x98, 0x74, 0xf1, 0xd7, 0x26, 0x88, 0xfc, 0x6b, 0x2f, 0xf6, 0xe9, 0xc5, 0xde, 0x6b, 0x8f, 0xfb, 0xd8, 0xf9, 0x7e, ]); parser._transform(buffer1, "", () => {}); expect(parsed.length).toBe(2); expect(parsed[0].type).toBe(FrameType.DATA); expect(parsed[1].type).toBe(FrameType.DATA); }); });