• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
34import { EchoMessage } from 'pigweedjs/protos/pw_rpc/echo_pb';
35
36describe('WebSerialTransport', () => {
37  let device: Device;
38  let serialMock: SerialMock;
39
40  function newResponse(payload = '._.'): Message {
41    const response = new Response();
42    response.setPayload(payload);
43    return response;
44  }
45
46  function generateResponsePacket(
47    channelId: number,
48    method: Method,
49    status: Status,
50    callId: number,
51    response?: Message,
52  ) {
53    const packet = new RpcPacket();
54    packet.setType(PacketType.RESPONSE);
55    packet.setChannelId(channelId);
56    packet.setServiceId(method.service.id);
57    packet.setMethodId(method.id);
58    packet.setCallId(callId);
59    packet.setStatus(status);
60    if (response === undefined) {
61      packet.setPayload(new Uint8Array(0));
62    } else {
63      packet.setPayload(response.serializeBinary());
64    }
65    return packet.serializeBinary();
66  }
67
68  function generateStreamingPacket(
69    channelId: number,
70    method: Method,
71    response: Message,
72    callId: number,
73    status: Status = Status.OK,
74  ) {
75    const packet = new RpcPacket();
76    packet.setType(PacketType.SERVER_STREAM);
77    packet.setChannelId(channelId);
78    packet.setServiceId(method.service.id);
79    packet.setMethodId(method.id);
80    packet.setCallId(callId);
81    packet.setPayload(response.serializeBinary());
82    packet.setStatus(status);
83    return packet.serializeBinary();
84  }
85
86  beforeEach(() => {
87    serialMock = new SerialMock();
88    device = new Device(
89      new ProtoCollection(),
90      new WebSerialTransport(serialMock as Serial),
91    );
92  });
93
94  it('has rpcs defined', () => {
95    expect(device.rpcs).toBeDefined();
96    expect(device.rpcs.pw.rpc.EchoService.Echo).toBeDefined();
97  });
98
99  it('has method arguments data', () => {
100    expect(device.getMethodArguments('pw.rpc.EchoService.Echo')).toStrictEqual([
101      'msg',
102    ]);
103    expect(device.getMethodArguments('pw.test2.Alpha.Unary')).toStrictEqual([
104      'magic_number',
105    ]);
106  });
107
108  it('unary rpc sends request to serial', async () => {
109    const methodStub = device.client
110      .channel()!
111      .methodStub('pw.rpc.EchoService.Echo')! as UnaryMethodStub;
112    const responseMsg = new EchoMessage();
113    responseMsg.setMsg('hello');
114    await device.connect();
115    const nextCallId = methodStub.rpcs.nextCallId;
116    setTimeout(() => {
117      device.client.processPacket(
118        generateResponsePacket(
119          1,
120          methodStub.method,
121          Status.OK,
122          nextCallId,
123          responseMsg,
124        ),
125      );
126    }, 10);
127    const [status, response] =
128      await device.rpcs.pw.rpc.EchoService.Echo('hello');
129    expect(response.getMsg()).toBe('hello');
130    expect(status).toBe(0);
131  });
132
133  it('server streaming rpc sends response', async () => {
134    await device.connect();
135    const response1 = newResponse('!!!');
136    const response2 = newResponse('?');
137    const serverStreaming = device.client
138      .channel()
139      ?.methodStub(
140        'pw.rpc.test1.TheTestService.SomeServerStreaming',
141      ) as ServerStreamingMethodStub;
142    const nextCallId = serverStreaming.rpcs.nextCallId;
143    const onNext = jest.fn();
144    const onCompleted = jest.fn();
145    const onError = jest.fn();
146
147    device.rpcs.pw.rpc.test1.TheTestService.SomeServerStreaming(
148      4,
149      onNext,
150      onCompleted,
151      onError,
152    );
153    device.client.processPacket(
154      generateStreamingPacket(1, serverStreaming.method, response1, nextCallId),
155    );
156    device.client.processPacket(
157      generateStreamingPacket(1, serverStreaming.method, response2, nextCallId),
158    );
159    device.client.processPacket(
160      generateResponsePacket(
161        1,
162        serverStreaming.method,
163        Status.ABORTED,
164        nextCallId,
165      ),
166    );
167
168    expect(onNext).toBeCalledWith(response1);
169    expect(onNext).toBeCalledWith(response2);
170    expect(onCompleted).toBeCalledWith(Status.ABORTED);
171  });
172});
173