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