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 "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/pwpb/test_method_context.h"
20 #include "pw_rpc_pwpb_private/internal_test_utils.h"
21 #include "pw_rpc_test_protos/test.rpc.pwpb.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
TEST(PwpbCodegen,Client_InvokesServerStreamingRpcWithCallback)261 TEST(PwpbCodegen, Client_InvokesServerStreamingRpcWithCallback) {
262 constexpr uint32_t kServiceId = internal::Hash("pw.rpc.test.TestService");
263 constexpr uint32_t kMethodId = internal::Hash("TestServerStreamRpc");
264
265 ClientContextForTest<128, 99, kServiceId, kMethodId> context;
266
267 TestServiceClient test_client(context.client(), context.channel().id());
268
269 struct {
270 bool active = true;
271 Status stream_status = Status::Unknown();
272 int response_value = -1;
273 } result;
274
275 auto call = test_client.TestServerStreamRpc(
276 {.integer = 123, .status_code = 0},
277 [&result](const test::pwpb::TestStreamResponse::Message& response) {
278 result.active = true;
279 result.response_value = response.number;
280 },
281 [&result](Status status) {
282 result.active = false;
283 result.stream_status = status;
284 });
285
286 EXPECT_TRUE(call.active());
287
288 EXPECT_EQ(context.output().total_packets(), 1u);
289 auto packet =
290 static_cast<const internal::test::FakeChannelOutput&>(context.output())
291 .last_packet();
292 EXPECT_EQ(packet.channel_id(), context.channel().id());
293 EXPECT_EQ(packet.service_id(), kServiceId);
294 EXPECT_EQ(packet.method_id(), kMethodId);
295 PW_DECODE_PB(test::pwpb::TestRequest, sent_proto, packet.payload());
296 EXPECT_EQ(sent_proto.integer, 123);
297
298 PW_ENCODE_PB(
299 test::pwpb::TestStreamResponse, response, .chunk = {}, .number = 11u);
300 EXPECT_EQ(OkStatus(), context.SendServerStream(response));
301 EXPECT_TRUE(result.active);
302 EXPECT_EQ(result.response_value, 11);
303
304 EXPECT_EQ(OkStatus(), context.SendResponse(Status::NotFound()));
305 EXPECT_FALSE(result.active);
306 EXPECT_EQ(result.stream_status, Status::NotFound());
307 }
308
TEST(PwpbCodegen,Client_StaticMethod_InvokesUnaryRpcWithCallback)309 TEST(PwpbCodegen, Client_StaticMethod_InvokesUnaryRpcWithCallback) {
310 constexpr uint32_t kServiceId = internal::Hash("pw.rpc.test.TestService");
311 constexpr uint32_t kMethodId = internal::Hash("TestUnaryRpc");
312
313 ClientContextForTest<128, 99, kServiceId, kMethodId> context;
314
315 struct {
316 Status last_status = Status::Unknown();
317 int response_value = -1;
318 } result;
319
320 auto call = test::pw_rpc::pwpb::TestService::TestUnaryRpc(
321 context.client(),
322 context.channel().id(),
323 {.integer = 123, .status_code = 0},
324 [&result](const test::pwpb::TestResponse::Message& response,
325 Status status) {
326 result.last_status = status;
327 result.response_value = response.value;
328 });
329
330 EXPECT_TRUE(call.active());
331
332 EXPECT_EQ(context.output().total_packets(), 1u);
333 auto packet =
334 static_cast<const internal::test::FakeChannelOutput&>(context.output())
335 .last_packet();
336 EXPECT_EQ(packet.channel_id(), context.channel().id());
337 EXPECT_EQ(packet.service_id(), kServiceId);
338 EXPECT_EQ(packet.method_id(), kMethodId);
339 PW_DECODE_PB(test::pwpb::TestRequest, sent_proto, packet.payload());
340 EXPECT_EQ(sent_proto.integer, 123);
341
342 PW_ENCODE_PB(test::pwpb::TestResponse, response, .value = 42);
343 EXPECT_EQ(OkStatus(), context.SendResponse(OkStatus(), response));
344 EXPECT_EQ(result.last_status, OkStatus());
345 EXPECT_EQ(result.response_value, 42);
346 }
347
TEST(PwpbCodegen,Client_StaticMethod_InvokesServerStreamingRpcWithCallback)348 TEST(PwpbCodegen, Client_StaticMethod_InvokesServerStreamingRpcWithCallback) {
349 constexpr uint32_t kServiceId = internal::Hash("pw.rpc.test.TestService");
350 constexpr uint32_t kMethodId = internal::Hash("TestServerStreamRpc");
351
352 ClientContextForTest<128, 99, kServiceId, kMethodId> context;
353
354 struct {
355 bool active = true;
356 Status stream_status = Status::Unknown();
357 int response_value = -1;
358 } result;
359
360 auto call = test::pw_rpc::pwpb::TestService::TestServerStreamRpc(
361 context.client(),
362 context.channel().id(),
363 {.integer = 123, .status_code = 0},
364 [&result](const test::pwpb::TestStreamResponse::Message& response) {
365 result.active = true;
366 result.response_value = response.number;
367 },
368 [&result](Status status) {
369 result.active = false;
370 result.stream_status = status;
371 });
372
373 EXPECT_TRUE(call.active());
374
375 EXPECT_EQ(context.output().total_packets(), 1u);
376 auto packet =
377 static_cast<const internal::test::FakeChannelOutput&>(context.output())
378 .last_packet();
379 EXPECT_EQ(packet.channel_id(), context.channel().id());
380 EXPECT_EQ(packet.service_id(), kServiceId);
381 EXPECT_EQ(packet.method_id(), kMethodId);
382 PW_DECODE_PB(test::pwpb::TestRequest, sent_proto, packet.payload());
383 EXPECT_EQ(sent_proto.integer, 123);
384
385 PW_ENCODE_PB(
386 test::pwpb::TestStreamResponse, response, .chunk = {}, .number = 11u);
387 EXPECT_EQ(OkStatus(), context.SendServerStream(response));
388 EXPECT_TRUE(result.active);
389 EXPECT_EQ(result.response_value, 11);
390
391 EXPECT_EQ(OkStatus(), context.SendResponse(Status::NotFound()));
392 EXPECT_FALSE(result.active);
393 EXPECT_EQ(result.stream_status, Status::NotFound());
394 }
395
396 } // namespace
397 } // namespace pw::rpc
398
399 PW_MODIFY_DIAGNOSTICS_POP();
400