1// Copyright 2022 The Pigweed Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may not 4// use this file except in compliance with the License. You may obtain a copy of 5// the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12// License for the specific language governing permissions and limitations under 13// the License. 14 15/* eslint-env browser */ 16import { SerialMock } from '../transport/serial_mock'; 17import { Device } from './'; 18import { ProtoCollection } from 'pigweedjs/protos/collection'; 19import { WebSerialTransport } from '../transport/web_serial_transport'; 20import { Serial } from 'pigweedjs/types/serial'; 21import { Message } from 'google-protobuf'; 22import { 23 RpcPacket, 24 PacketType, 25} from 'pigweedjs/protos/pw_rpc/internal/packet_pb'; 26import { 27 Method, 28 ServerStreamingMethodStub, 29 UnaryMethodStub, 30} from 'pigweedjs/pw_rpc'; 31import { Status } from 'pigweedjs/pw_status'; 32import { Response } from 'pigweedjs/protos/pw_rpc/ts/test_pb'; 33 34describe('WebSerialTransport', () => { 35 let device: Device; 36 let serialMock: SerialMock; 37 38 function newResponse(payload = '._.'): Message { 39 const response = new Response(); 40 response.setPayload(payload); 41 return response; 42 } 43 44 function generateResponsePacket( 45 channelId: number, 46 method: Method, 47 status: Status, 48 callId: number, 49 response?: Message, 50 ) { 51 const packet = new RpcPacket(); 52 packet.setType(PacketType.RESPONSE); 53 packet.setChannelId(channelId); 54 packet.setServiceId(method.service.id); 55 packet.setMethodId(method.id); 56 packet.setCallId(callId); 57 packet.setStatus(status); 58 if (response === undefined) { 59 packet.setPayload(new Uint8Array(0)); 60 } else { 61 packet.setPayload(response.serializeBinary()); 62 } 63 return packet.serializeBinary(); 64 } 65 66 function generateStreamingPacket( 67 channelId: number, 68 method: Method, 69 response: Message, 70 callId: number, 71 status: Status = Status.OK, 72 ) { 73 const packet = new RpcPacket(); 74 packet.setType(PacketType.SERVER_STREAM); 75 packet.setChannelId(channelId); 76 packet.setServiceId(method.service.id); 77 packet.setMethodId(method.id); 78 packet.setCallId(callId); 79 packet.setPayload(response.serializeBinary()); 80 packet.setStatus(status); 81 return packet.serializeBinary(); 82 } 83 84 beforeEach(() => { 85 serialMock = new SerialMock(); 86 device = new Device( 87 new ProtoCollection(), 88 new WebSerialTransport(serialMock as Serial), 89 ); 90 }); 91 92 it('has rpcs defined', () => { 93 expect(device.rpcs).toBeDefined(); 94 expect(device.rpcs.pw.rpc.EchoService.Echo).toBeDefined(); 95 }); 96 97 it('unary rpc sends request to serial', async () => { 98 const methodStub = device.client 99 .channel()! 100 .methodStub('pw.rpc.EchoService.Echo')! as UnaryMethodStub; 101 const req = device.rpcs.pw.rpc.EchoService.Echo.createRequest(); 102 req.setMsg('hello'); 103 await device.connect(); 104 const nextCallId = methodStub.rpcs.nextCallId; 105 setTimeout(() => { 106 device.client.processPacket( 107 generateResponsePacket( 108 1, 109 methodStub.method, 110 Status.OK, 111 nextCallId, 112 req, 113 ), 114 ); 115 }, 10); 116 117 const [status, response] = 118 await device.rpcs.pw.rpc.EchoService.Echo.call(req); 119 expect(response.getMsg()).toBe('hello'); 120 expect(status).toBe(0); 121 }); 122 123 it('server streaming rpc sends response', async () => { 124 await device.connect(); 125 const response1 = newResponse('!!!'); 126 const response2 = newResponse('?'); 127 const serverStreaming = device.client 128 .channel() 129 ?.methodStub( 130 'pw.rpc.test1.TheTestService.SomeServerStreaming', 131 ) as ServerStreamingMethodStub; 132 const nextCallId = serverStreaming.rpcs.nextCallId; 133 const onNext = jest.fn(); 134 const onCompleted = jest.fn(); 135 const onError = jest.fn(); 136 137 const req = 138 device.rpcs.pw.rpc.test1.TheTestService.SomeServerStreaming.createRequest(); 139 req.setMagicNumber(4); 140 const rpcCall = 141 device.rpcs.pw.rpc.test1.TheTestService.SomeServerStreaming.call(req); 142 device.client.processPacket( 143 generateStreamingPacket(1, serverStreaming.method, response1, nextCallId), 144 ); 145 device.client.processPacket( 146 generateStreamingPacket(1, serverStreaming.method, response2, nextCallId), 147 ); 148 device.client.processPacket( 149 generateResponsePacket( 150 1, 151 serverStreaming.method, 152 Status.ABORTED, 153 nextCallId, 154 ), 155 ); 156 157 for await (const msg of rpcCall) { 158 onNext(msg); 159 } 160 161 onCompleted(rpcCall.call.status); 162 163 expect(onNext).toBeCalledWith(response1); 164 expect(onNext).toBeCalledWith(response2); 165 expect(onCompleted).toBeCalledWith(Status.ABORTED); 166 }); 167}); 168