// Copyright 2022 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. /* eslint-env browser */ import { SerialMock } from '../transport/serial_mock'; import { Device } from './'; import { ProtoCollection } from 'pigweedjs/protos/collection'; import { WebSerialTransport } from '../transport/web_serial_transport'; import { Serial } from 'pigweedjs/types/serial'; import { Message } from 'google-protobuf'; import { RpcPacket, PacketType, } from 'pigweedjs/protos/pw_rpc/internal/packet_pb'; import { Method, ServerStreamingMethodStub } from 'pigweedjs/pw_rpc'; import { Status } from 'pigweedjs/pw_status'; import { Response } from 'pigweedjs/protos/pw_rpc/ts/test_pb'; describe('WebSerialTransport', () => { let device: Device; let serialMock: SerialMock; function newResponse(payload = '._.'): Message { const response = new Response(); response.setPayload(payload); return response; } function generateResponsePacket( channelId: number, method: Method, status: Status, response?: Message, ) { const packet = new RpcPacket(); packet.setType(PacketType.RESPONSE); packet.setChannelId(channelId); packet.setServiceId(method.service.id); packet.setMethodId(method.id); packet.setStatus(status); if (response === undefined) { packet.setPayload(new Uint8Array(0)); } else { packet.setPayload(response.serializeBinary()); } return packet.serializeBinary(); } function generateStreamingPacket( channelId: number, method: Method, response: Message, status: Status = Status.OK, ) { const packet = new RpcPacket(); packet.setType(PacketType.SERVER_STREAM); packet.setChannelId(channelId); packet.setServiceId(method.service.id); packet.setMethodId(method.id); packet.setPayload(response.serializeBinary()); packet.setStatus(status); return packet.serializeBinary(); } beforeEach(() => { serialMock = new SerialMock(); device = new Device( new ProtoCollection(), new WebSerialTransport(serialMock as Serial), ); }); it('has rpcs defined', () => { expect(device.rpcs).toBeDefined(); expect(device.rpcs.pw.rpc.EchoService.Echo).toBeDefined(); }); it('has method arguments data', () => { expect(device.getMethodArguments('pw.rpc.EchoService.Echo')).toStrictEqual([ 'msg', ]); expect(device.getMethodArguments('pw.test2.Alpha.Unary')).toStrictEqual([ 'magic_number', ]); }); it('unary rpc sends request to serial', async () => { // prettier-ignore const helloResponse = new Uint8Array([ 126, 165, 3, 42, 7, 10, 5, 104, 101, 108, 108, 111, 8, 1, 16, 1, 29, 82, 208, 251, 20, 37, 233, 14, 71, 139, 109, 127, 108, 165, 126, ]); await device.connect(); serialMock.dataFromDevice(helloResponse); const [status, response] = await device.rpcs.pw.rpc.EchoService.Echo( 'hello', ); expect(response.getMsg()).toBe('hello'); expect(status).toBe(0); }); it('server streaming rpc sends response', async () => { await device.connect(); const response1 = newResponse('!!!'); const response2 = newResponse('?'); const serverStreaming = device.client .channel() ?.methodStub( 'pw.rpc.test1.TheTestService.SomeServerStreaming', )! as ServerStreamingMethodStub; const onNext = jest.fn(); const onCompleted = jest.fn(); const onError = jest.fn(); device.rpcs.pw.rpc.test1.TheTestService.SomeServerStreaming( 4, onNext, onCompleted, onError, ); device.client.processPacket( generateStreamingPacket(1, serverStreaming.method, response1), ); device.client.processPacket( generateStreamingPacket(1, serverStreaming.method, response2), ); device.client.processPacket( generateResponsePacket(1, serverStreaming.method, Status.ABORTED), ); expect(onNext).toBeCalledWith(response1); expect(onNext).toBeCalledWith(response2); expect(onCompleted).toBeCalledWith(Status.ABORTED); }); });