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