xref: /aosp_15_r20/external/pigweed/ts/device/index_test.ts (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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