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