xref: /aosp_15_r20/external/pigweed/pw_rpc/nanopb/codegen_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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