1 // Copyright 2021 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 #include "pw_preprocessor/compiler.h"
16 #include "pw_rpc/internal/hash.h"
17 #include "pw_rpc/internal/test_utils.h"
18 #include "pw_rpc/nanopb/test_method_context.h"
19 #include "pw_rpc_nanopb_private/internal_test_utils.h"
20 #include "pw_rpc_test_protos/test.rpc.pb.h"
21 #include "pw_unit_test/framework.h"
22
23 PW_MODIFY_DIAGNOSTICS_PUSH();
24 PW_MODIFY_DIAGNOSTIC(ignored, "-Wmissing-field-initializers");
25
26 namespace pw::rpc {
27 namespace test {
28
29 class TestService final
30 : public pw_rpc::nanopb::TestService::Service<TestService> {
31 public:
TestUnaryRpc(const pw_rpc_test_TestRequest & request,pw_rpc_test_TestResponse & response)32 Status TestUnaryRpc(const pw_rpc_test_TestRequest& request,
33 pw_rpc_test_TestResponse& response) {
34 response.value = request.integer + 1;
35 return static_cast<Status::Code>(request.status_code);
36 }
37
TestAnotherUnaryRpc(const pw_rpc_test_TestRequest & request,NanopbUnaryResponder<pw_rpc_test_TestResponse> & responder)38 void TestAnotherUnaryRpc(
39 const pw_rpc_test_TestRequest& request,
40 NanopbUnaryResponder<pw_rpc_test_TestResponse>& responder) {
41 pw_rpc_test_TestResponse response{};
42 EXPECT_EQ(OkStatus(),
43 responder.Finish(response, TestUnaryRpc(request, response)));
44 }
45
TestServerStreamRpc(const pw_rpc_test_TestRequest & request,ServerWriter<pw_rpc_test_TestStreamResponse> & writer)46 static void TestServerStreamRpc(
47 const pw_rpc_test_TestRequest& request,
48 ServerWriter<pw_rpc_test_TestStreamResponse>& writer) {
49 for (int i = 0; i < request.integer; ++i) {
50 EXPECT_EQ(
51 OkStatus(),
52 writer.Write({.chunk = {}, .number = static_cast<uint32_t>(i)}));
53 }
54
55 EXPECT_EQ(OkStatus(),
56 writer.Finish(static_cast<Status::Code>(request.status_code)));
57 }
58
TestClientStreamRpc(ServerReader<pw_rpc_test_TestRequest,pw_rpc_test_TestStreamResponse> & new_reader)59 void TestClientStreamRpc(
60 ServerReader<pw_rpc_test_TestRequest, pw_rpc_test_TestStreamResponse>&
61 new_reader) {
62 reader = std::move(new_reader);
63 }
64
TestBidirectionalStreamRpc(ServerReaderWriter<pw_rpc_test_TestRequest,pw_rpc_test_TestStreamResponse> & new_reader_writer)65 void TestBidirectionalStreamRpc(
66 ServerReaderWriter<pw_rpc_test_TestRequest,
67 pw_rpc_test_TestStreamResponse>& new_reader_writer) {
68 reader_writer = std::move(new_reader_writer);
69 }
70
71 ServerReader<pw_rpc_test_TestRequest, pw_rpc_test_TestStreamResponse> reader;
72 ServerReaderWriter<pw_rpc_test_TestRequest, pw_rpc_test_TestStreamResponse>
73 reader_writer;
74 };
75
76 } // namespace test
77
78 namespace {
79
80 using internal::ClientContextForTest;
81
TEST(NanopbCodegen,CompilesProperly)82 TEST(NanopbCodegen, CompilesProperly) {
83 test::TestService service;
84 EXPECT_EQ(internal::UnwrapServiceId(service.service_id()),
85 internal::Hash("pw.rpc.test.TestService"));
86 EXPECT_STREQ(service.name(), "TestService");
87 }
88
TEST(NanopbCodegen,Server_InvokeUnaryRpc)89 TEST(NanopbCodegen, Server_InvokeUnaryRpc) {
90 PW_NANOPB_TEST_METHOD_CONTEXT(test::TestService, TestUnaryRpc) context;
91
92 EXPECT_EQ(OkStatus(),
93 context.call({.integer = 123, .status_code = OkStatus().code()}));
94
95 EXPECT_EQ(124, context.response().value);
96
97 EXPECT_EQ(Status::InvalidArgument(),
98 context.call({.integer = 999,
99 .status_code = Status::InvalidArgument().code()}));
100 EXPECT_EQ(1000, context.response().value);
101 }
102
TEST(NanopbCodegen,Server_InvokeAsyncUnaryRpc)103 TEST(NanopbCodegen, Server_InvokeAsyncUnaryRpc) {
104 PW_NANOPB_TEST_METHOD_CONTEXT(test::TestService, TestAnotherUnaryRpc) context;
105
106 context.call({.integer = 123, .status_code = OkStatus().code()});
107
108 EXPECT_EQ(OkStatus(), context.status());
109 EXPECT_EQ(124, context.response().value);
110
111 context.call(
112 {.integer = 999, .status_code = Status::InvalidArgument().code()});
113 EXPECT_EQ(Status::InvalidArgument(), context.status());
114 EXPECT_EQ(1000, context.response().value);
115 }
116
TEST(NanopbCodegen,Server_InvokeServerStreamingRpc)117 TEST(NanopbCodegen, Server_InvokeServerStreamingRpc) {
118 PW_NANOPB_TEST_METHOD_CONTEXT(test::TestService, TestServerStreamRpc) context;
119
120 context.call({.integer = 0, .status_code = Status::Aborted().code()});
121
122 EXPECT_EQ(Status::Aborted(), context.status());
123 EXPECT_TRUE(context.done());
124 EXPECT_EQ(context.total_responses(), 0u);
125
126 context.call({.integer = 4, .status_code = OkStatus().code()});
127
128 ASSERT_EQ(4u, context.responses().size());
129
130 for (size_t i = 0; i < context.responses().size(); ++i) {
131 EXPECT_EQ(context.responses()[i].number, i);
132 }
133
134 EXPECT_EQ(OkStatus().code(), context.status());
135 }
136
TEST(NanopbCodegen,Server_InvokeServerStreamingRpc_ManualWriting)137 TEST(NanopbCodegen, Server_InvokeServerStreamingRpc_ManualWriting) {
138 PW_NANOPB_TEST_METHOD_CONTEXT(test::TestService, TestServerStreamRpc, 4)
139 context;
140
141 ASSERT_EQ(4u, context.max_packets());
142
143 auto writer = context.writer();
144
145 EXPECT_EQ(OkStatus(), writer.Write({.chunk = {}, .number = 3}));
146 EXPECT_EQ(OkStatus(), writer.Write({.chunk = {}, .number = 6}));
147 EXPECT_EQ(OkStatus(), writer.Write({.chunk = {}, .number = 9}));
148
149 EXPECT_FALSE(context.done());
150
151 EXPECT_EQ(OkStatus(), writer.Finish(Status::Cancelled()));
152 ASSERT_TRUE(context.done());
153 EXPECT_EQ(Status::Cancelled(), context.status());
154
155 ASSERT_EQ(3u, context.responses().size());
156
157 EXPECT_EQ(context.responses()[0].number, 3u);
158 EXPECT_EQ(context.responses()[1].number, 6u);
159 EXPECT_EQ(context.responses()[2].number, 9u);
160 }
161
TEST(NanopbCodegen,Server_InvokeClientStreamingRpc)162 TEST(NanopbCodegen, Server_InvokeClientStreamingRpc) {
163 PW_NANOPB_TEST_METHOD_CONTEXT(test::TestService, TestClientStreamRpc) context;
164
165 context.call();
166
167 pw_rpc_test_TestRequest request = {};
168 context.service().reader.set_on_next(
169 [&request](const pw_rpc_test_TestRequest& req) { request = req; });
170
171 context.SendClientStream({.integer = -99, .status_code = 10});
172 EXPECT_EQ(request.integer, -99);
173 EXPECT_EQ(request.status_code, 10u);
174
175 ASSERT_EQ(OkStatus(),
176 context.service().reader.Finish({.chunk = {}, .number = 3},
177 Status::Unimplemented()));
178 EXPECT_EQ(Status::Unimplemented(), context.status());
179 EXPECT_EQ(context.response().number, 3u);
180 }
181
TEST(NanopbCodegen,Server_InvokeBidirectionalStreamingRpc)182 TEST(NanopbCodegen, Server_InvokeBidirectionalStreamingRpc) {
183 PW_NANOPB_TEST_METHOD_CONTEXT(test::TestService, TestBidirectionalStreamRpc)
184 context;
185
186 context.call();
187
188 pw_rpc_test_TestRequest request = {};
189 context.service().reader_writer.set_on_next(
190 [&request](const pw_rpc_test_TestRequest& req) { request = req; });
191
192 context.SendClientStream({.integer = -99, .status_code = 10});
193 EXPECT_EQ(request.integer, -99);
194 EXPECT_EQ(request.status_code, 10u);
195
196 ASSERT_EQ(OkStatus(),
197 context.service().reader_writer.Write({.chunk = {}, .number = 2}));
198 EXPECT_EQ(context.responses()[0].number, 2u);
199
200 ASSERT_EQ(OkStatus(),
201 context.service().reader_writer.Finish(Status::NotFound()));
202 EXPECT_EQ(Status::NotFound(), context.status());
203 }
204
TEST(NanopbCodegen,ClientCall_DefaultConstructor)205 TEST(NanopbCodegen, ClientCall_DefaultConstructor) {
206 NanopbUnaryReceiver<pw_rpc_test_TestResponse> unary_call;
207 NanopbClientReader<pw_rpc_test_TestStreamResponse> server_streaming_call;
208 }
209
210 using TestServiceClient = test::pw_rpc::nanopb::TestService::Client;
211
TEST(NanopbCodegen,Client_InvokesUnaryRpcWithCallback)212 TEST(NanopbCodegen, Client_InvokesUnaryRpcWithCallback) {
213 constexpr uint32_t kServiceId = internal::Hash("pw.rpc.test.TestService");
214 constexpr uint32_t kMethodId = internal::Hash("TestUnaryRpc");
215
216 ClientContextForTest<128, 99, kServiceId, kMethodId> context;
217
218 TestServiceClient test_client(context.client(), context.channel().id());
219
220 struct {
221 Status last_status = Status::Unknown();
222 int response_value = -1;
223 } result;
224
225 auto call = test_client.TestUnaryRpc(
226 {.integer = 123, .status_code = 0},
227 [&result](const pw_rpc_test_TestResponse& response, Status status) {
228 result.last_status = status;
229 result.response_value = response.value;
230 });
231
232 EXPECT_TRUE(call.active());
233
234 EXPECT_EQ(context.output().total_packets(), 1u);
235 auto packet =
236 static_cast<const internal::test::FakeChannelOutput&>(context.output())
237 .last_packet();
238 EXPECT_EQ(packet.channel_id(), context.channel().id());
239 EXPECT_EQ(packet.service_id(), kServiceId);
240 EXPECT_EQ(packet.method_id(), kMethodId);
241 PW_DECODE_PB(pw_rpc_test_TestRequest, sent_proto, packet.payload());
242 EXPECT_EQ(sent_proto.integer, 123);
243
244 PW_ENCODE_PB(pw_rpc_test_TestResponse, response, .value = 42);
245 EXPECT_EQ(OkStatus(), context.SendResponse(OkStatus(), response));
246 EXPECT_EQ(result.last_status, OkStatus());
247 EXPECT_EQ(result.response_value, 42);
248
249 EXPECT_FALSE(call.active());
250 }
251
TEST(NanopbCodegen,Client_InvokesServerStreamingRpcWithCallback)252 TEST(NanopbCodegen, Client_InvokesServerStreamingRpcWithCallback) {
253 constexpr uint32_t kServiceId = internal::Hash("pw.rpc.test.TestService");
254 constexpr uint32_t kMethodId = internal::Hash("TestServerStreamRpc");
255
256 ClientContextForTest<128, 99, kServiceId, kMethodId> context;
257
258 TestServiceClient test_client(context.client(), context.channel().id());
259
260 struct {
261 bool active = true;
262 Status stream_status = Status::Unknown();
263 int response_value = -1;
264 } result;
265
266 auto call = test_client.TestServerStreamRpc(
267 {.integer = 123, .status_code = 0},
268 [&result](const pw_rpc_test_TestStreamResponse& response) {
269 result.active = true;
270 result.response_value = response.number;
271 },
272 [&result](Status status) {
273 result.active = false;
274 result.stream_status = status;
275 });
276
277 EXPECT_TRUE(call.active());
278
279 EXPECT_EQ(context.output().total_packets(), 1u);
280 auto packet =
281 static_cast<const internal::test::FakeChannelOutput&>(context.output())
282 .last_packet();
283 EXPECT_EQ(packet.channel_id(), context.channel().id());
284 EXPECT_EQ(packet.service_id(), kServiceId);
285 EXPECT_EQ(packet.method_id(), kMethodId);
286 PW_DECODE_PB(pw_rpc_test_TestRequest, sent_proto, packet.payload());
287 EXPECT_EQ(sent_proto.integer, 123);
288
289 PW_ENCODE_PB(
290 pw_rpc_test_TestStreamResponse, response, .chunk = {}, .number = 11u);
291 EXPECT_EQ(OkStatus(), context.SendServerStream(response));
292 EXPECT_TRUE(result.active);
293 EXPECT_EQ(result.response_value, 11);
294
295 EXPECT_EQ(OkStatus(), context.SendResponse(Status::NotFound()));
296 EXPECT_FALSE(result.active);
297 EXPECT_EQ(result.stream_status, Status::NotFound());
298 }
299
TEST(NanopbCodegen,Client_StaticMethod_InvokesUnaryRpcWithCallback)300 TEST(NanopbCodegen, Client_StaticMethod_InvokesUnaryRpcWithCallback) {
301 constexpr uint32_t kServiceId = internal::Hash("pw.rpc.test.TestService");
302 constexpr uint32_t kMethodId = internal::Hash("TestUnaryRpc");
303
304 ClientContextForTest<128, 99, kServiceId, kMethodId> context;
305
306 struct {
307 Status last_status = Status::Unknown();
308 int response_value = -1;
309 } result;
310
311 auto call = test::pw_rpc::nanopb::TestService::TestUnaryRpc(
312 context.client(),
313 context.channel().id(),
314 {.integer = 123, .status_code = 0},
315 [&result](const pw_rpc_test_TestResponse& response, Status status) {
316 result.last_status = status;
317 result.response_value = response.value;
318 });
319
320 EXPECT_TRUE(call.active());
321
322 EXPECT_EQ(context.output().total_packets(), 1u);
323 auto packet =
324 static_cast<const internal::test::FakeChannelOutput&>(context.output())
325 .last_packet();
326 EXPECT_EQ(packet.channel_id(), context.channel().id());
327 EXPECT_EQ(packet.service_id(), kServiceId);
328 EXPECT_EQ(packet.method_id(), kMethodId);
329 PW_DECODE_PB(pw_rpc_test_TestRequest, sent_proto, packet.payload());
330 EXPECT_EQ(sent_proto.integer, 123);
331
332 PW_ENCODE_PB(pw_rpc_test_TestResponse, response, .value = 42);
333 EXPECT_EQ(OkStatus(), context.SendResponse(OkStatus(), response));
334 EXPECT_EQ(result.last_status, OkStatus());
335 EXPECT_EQ(result.response_value, 42);
336 }
337
TEST(NanopbCodegen,Client_StaticMethod_InvokesServerStreamingRpcWithCallback)338 TEST(NanopbCodegen, Client_StaticMethod_InvokesServerStreamingRpcWithCallback) {
339 constexpr uint32_t kServiceId = internal::Hash("pw.rpc.test.TestService");
340 constexpr uint32_t kMethodId = internal::Hash("TestServerStreamRpc");
341
342 ClientContextForTest<128, 99, kServiceId, kMethodId> context;
343
344 struct {
345 bool active = true;
346 Status stream_status = Status::Unknown();
347 int response_value = -1;
348 } result;
349
350 auto call = test::pw_rpc::nanopb::TestService::TestServerStreamRpc(
351 context.client(),
352 context.channel().id(),
353 {.integer = 123, .status_code = 0},
354 [&result](const pw_rpc_test_TestStreamResponse& response) {
355 result.active = true;
356 result.response_value = response.number;
357 },
358 [&result](Status status) {
359 result.active = false;
360 result.stream_status = status;
361 });
362
363 EXPECT_TRUE(call.active());
364
365 EXPECT_EQ(context.output().total_packets(), 1u);
366 auto packet =
367 static_cast<const internal::test::FakeChannelOutput&>(context.output())
368 .last_packet();
369 EXPECT_EQ(packet.channel_id(), context.channel().id());
370 EXPECT_EQ(packet.service_id(), kServiceId);
371 EXPECT_EQ(packet.method_id(), kMethodId);
372 PW_DECODE_PB(pw_rpc_test_TestRequest, sent_proto, packet.payload());
373 EXPECT_EQ(sent_proto.integer, 123);
374
375 PW_ENCODE_PB(
376 pw_rpc_test_TestStreamResponse, response, .chunk = {}, .number = 11u);
377 EXPECT_EQ(OkStatus(), context.SendServerStream(response));
378 EXPECT_TRUE(result.active);
379 EXPECT_EQ(result.response_value, 11);
380
381 EXPECT_EQ(OkStatus(), context.SendResponse(Status::NotFound()));
382 EXPECT_FALSE(result.active);
383 EXPECT_EQ(result.stream_status, Status::NotFound());
384 }
385
386 } // namespace
387 } // namespace pw::rpc
388
389 PW_MODIFY_DIAGNOSTICS_POP();
390