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