xref: /aosp_15_r20/external/pigweed/pw_rpc/nanopb/client_server_context_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 #include <array>
15 
16 #include "pw_rpc/nanopb/client_server_testing.h"
17 #include "pw_rpc_test_protos/test.rpc.pb.h"
18 #include "pw_sync/mutex.h"
19 #include "pw_unit_test/framework.h"
20 
21 namespace pw::rpc {
22 namespace {
23 
24 using GeneratedService = ::pw::rpc::test::pw_rpc::nanopb::TestService;
25 
26 class TestService final : public GeneratedService::Service<TestService> {
27  public:
TestUnaryRpc(const pw_rpc_test_TestRequest & request,pw_rpc_test_TestResponse & response)28   Status TestUnaryRpc(const pw_rpc_test_TestRequest& request,
29                       pw_rpc_test_TestResponse& response) {
30     response.value = request.integer + 1;
31     return static_cast<Status::Code>(request.status_code);
32   }
33 
TestAnotherUnaryRpc(const pw_rpc_test_TestRequest & request,pw_rpc_test_TestResponse & response)34   Status TestAnotherUnaryRpc(const pw_rpc_test_TestRequest& request,
35                              pw_rpc_test_TestResponse& response) {
36     using ArgType = std::array<uint32_t, 3>;
37     // The values array needs to be kept in memory until after this method call
38     // returns since the response is not encoded until after returning from this
39     // method.
40     static const ArgType values = {7, 8, 9};
41     response.repeated_field.funcs.encode = +[](pb_ostream_t* stream,
42                                                const pb_field_t* field,
43                                                void* const* arg) -> bool {
44       // Note: nanopb passes the pointer to the repeated_filed.arg member as
45       // arg, not its contents.
46       for (auto elem : *static_cast<const ArgType*>(*arg)) {
47         if (!pb_encode_tag_for_field(stream, field) ||
48             !pb_encode_varint(stream, elem))
49           return false;
50       }
51       return true;
52     };
53     response.repeated_field.arg = const_cast<ArgType*>(&values);
54     return static_cast<Status::Code>(request.status_code);
55   }
56 
TestServerStreamRpc(const pw_rpc_test_TestRequest &,ServerWriter<pw_rpc_test_TestStreamResponse> &)57   static void TestServerStreamRpc(
58       const pw_rpc_test_TestRequest&,
59       ServerWriter<pw_rpc_test_TestStreamResponse>&) {}
60 
TestClientStreamRpc(ServerReader<pw_rpc_test_TestRequest,pw_rpc_test_TestStreamResponse> &)61   void TestClientStreamRpc(
62       ServerReader<pw_rpc_test_TestRequest, pw_rpc_test_TestStreamResponse>&) {}
63 
TestBidirectionalStreamRpc(ServerReaderWriter<pw_rpc_test_TestRequest,pw_rpc_test_TestStreamResponse> &)64   void TestBidirectionalStreamRpc(
65       ServerReaderWriter<pw_rpc_test_TestRequest,
66                          pw_rpc_test_TestStreamResponse>&) {}
67 };
68 
TEST(NanopbClientServerTestContext,ReceivesUnaryRpcResponse)69 TEST(NanopbClientServerTestContext, ReceivesUnaryRpcResponse) {
70   NanopbClientServerTestContext<> ctx;
71   TestService service;
72   ctx.server().RegisterService(service);
73 
74   pw_rpc_test_TestResponse response pw_rpc_test_TestResponse_init_default;
75   auto handler = [&response](const pw_rpc_test_TestResponse& server_response,
76                              pw::Status) { response = server_response; };
77 
78   pw_rpc_test_TestRequest request{.integer = 1,
79                                   .status_code = OkStatus().code()};
80   auto call = GeneratedService::TestUnaryRpc(
81       ctx.client(), ctx.channel().id(), request, handler);
82   // Force manual forwarding of packets as context is not threaded
83   ctx.ForwardNewPackets();
84 
85   const auto sent_request =
86       ctx.request<test::pw_rpc::nanopb::TestService::TestUnaryRpc>(0);
87   const auto sent_response =
88       ctx.response<test::pw_rpc::nanopb::TestService::TestUnaryRpc>(0);
89 
90   EXPECT_EQ(response.value, sent_response.value);
91   EXPECT_EQ(response.value, request.integer + 1);
92   EXPECT_EQ(request.integer, sent_request.integer);
93 }
94 
TEST(NanopbClientServerTestContext,ReceivesMultipleResponses)95 TEST(NanopbClientServerTestContext, ReceivesMultipleResponses) {
96   NanopbClientServerTestContext<> ctx;
97   TestService service;
98   ctx.server().RegisterService(service);
99 
100   pw_rpc_test_TestResponse response1 pw_rpc_test_TestResponse_init_default;
101   pw_rpc_test_TestResponse response2 pw_rpc_test_TestResponse_init_default;
102   auto handler1 = [&response1](const pw_rpc_test_TestResponse& server_response,
103                                pw::Status) { response1 = server_response; };
104   auto handler2 = [&response2](const pw_rpc_test_TestResponse& server_response,
105                                pw::Status) { response2 = server_response; };
106 
107   pw_rpc_test_TestRequest request1{.integer = 1,
108                                    .status_code = OkStatus().code()};
109   pw_rpc_test_TestRequest request2{.integer = 2,
110                                    .status_code = OkStatus().code()};
111   const auto call1 = GeneratedService::TestUnaryRpc(
112       ctx.client(), ctx.channel().id(), request1, handler1);
113   // Force manual forwarding of packets as context is not threaded
114   ctx.ForwardNewPackets();
115   const auto call2 = GeneratedService::TestUnaryRpc(
116       ctx.client(), ctx.channel().id(), request2, handler2);
117   // Force manual forwarding of packets as context is not threaded
118   ctx.ForwardNewPackets();
119 
120   const auto sent_request1 =
121       ctx.request<test::pw_rpc::nanopb::TestService::TestUnaryRpc>(0);
122   const auto sent_request2 =
123       ctx.request<test::pw_rpc::nanopb::TestService::TestUnaryRpc>(1);
124   const auto sent_response1 =
125       ctx.response<test::pw_rpc::nanopb::TestService::TestUnaryRpc>(0);
126   const auto sent_response2 =
127       ctx.response<test::pw_rpc::nanopb::TestService::TestUnaryRpc>(1);
128 
129   EXPECT_EQ(response1.value, request1.integer + 1);
130   EXPECT_EQ(response2.value, request2.integer + 1);
131   EXPECT_EQ(response1.value, sent_response1.value);
132   EXPECT_EQ(response2.value, sent_response2.value);
133   EXPECT_EQ(request1.integer, sent_request1.integer);
134   EXPECT_EQ(request2.integer, sent_request2.integer);
135 }
136 
TEST(NanopbClientServerTestContext,ReceivesMultipleResponsesWithPacketProcessor)137 TEST(NanopbClientServerTestContext,
138      ReceivesMultipleResponsesWithPacketProcessor) {
139   using ProtectedInt = std::pair<int, pw::sync::Mutex>;
140   ProtectedInt server_counter{};
141   auto server_processor = [&server_counter](
142                               ClientServer& client_server,
143                               pw::ConstByteSpan packet) -> pw::Status {
144     server_counter.second.lock();
145     ++server_counter.first;
146     server_counter.second.unlock();
147     return client_server.ProcessPacket(packet);
148   };
149 
150   ProtectedInt client_counter{};
151   auto client_processor = [&client_counter](
152                               ClientServer& client_server,
153                               pw::ConstByteSpan packet) -> pw::Status {
154     client_counter.second.lock();
155     ++client_counter.first;
156     client_counter.second.unlock();
157     return client_server.ProcessPacket(packet);
158   };
159 
160   NanopbClientServerTestContext<> ctx(server_processor, client_processor);
161   TestService service;
162   ctx.server().RegisterService(service);
163 
164   pw_rpc_test_TestResponse response1 pw_rpc_test_TestResponse_init_default;
165   pw_rpc_test_TestResponse response2 pw_rpc_test_TestResponse_init_default;
166   auto handler1 = [&response1](const pw_rpc_test_TestResponse& server_response,
167                                pw::Status) { response1 = server_response; };
168   auto handler2 = [&response2](const pw_rpc_test_TestResponse& server_response,
169                                pw::Status) { response2 = server_response; };
170 
171   pw_rpc_test_TestRequest request1{.integer = 1,
172                                    .status_code = OkStatus().code()};
173   pw_rpc_test_TestRequest request2{.integer = 2,
174                                    .status_code = OkStatus().code()};
175   const auto call1 = GeneratedService::TestUnaryRpc(
176       ctx.client(), ctx.channel().id(), request1, handler1);
177   // Force manual forwarding of packets as context is not threaded
178   ctx.ForwardNewPackets();
179   const auto call2 = GeneratedService::TestUnaryRpc(
180       ctx.client(), ctx.channel().id(), request2, handler2);
181   // Force manual forwarding of packets as context is not threaded
182   ctx.ForwardNewPackets();
183 
184   const auto sent_request1 =
185       ctx.request<test::pw_rpc::nanopb::TestService::TestUnaryRpc>(0);
186   const auto sent_request2 =
187       ctx.request<test::pw_rpc::nanopb::TestService::TestUnaryRpc>(1);
188   const auto sent_response1 =
189       ctx.response<test::pw_rpc::nanopb::TestService::TestUnaryRpc>(0);
190   const auto sent_response2 =
191       ctx.response<test::pw_rpc::nanopb::TestService::TestUnaryRpc>(1);
192 
193   EXPECT_EQ(response1.value, request1.integer + 1);
194   EXPECT_EQ(response2.value, request2.integer + 1);
195   EXPECT_EQ(response1.value, sent_response1.value);
196   EXPECT_EQ(response2.value, sent_response2.value);
197   EXPECT_EQ(request1.integer, sent_request1.integer);
198   EXPECT_EQ(request2.integer, sent_request2.integer);
199 
200   server_counter.second.lock();
201   EXPECT_EQ(server_counter.first, 2);
202   server_counter.second.unlock();
203   client_counter.second.lock();
204   EXPECT_EQ(client_counter.first, 2);
205   client_counter.second.unlock();
206 }
207 
TEST(NanopbClientServerTestContext,ResponseWithCallbacks)208 TEST(NanopbClientServerTestContext, ResponseWithCallbacks) {
209   NanopbClientServerTestContext<> ctx;
210   TestService service;
211   ctx.server().RegisterService(service);
212 
213   const auto call = GeneratedService::TestAnotherUnaryRpc(
214       ctx.client(), ctx.channel().id(), pw_rpc_test_TestRequest_init_default);
215   // Force manual forwarding of packets as context is not threaded
216   ctx.ForwardNewPackets();
217 
218   // To decode a response object that requires to set pb_callback_t members,
219   // pass it to the response() method as a parameter.
220   constexpr size_t kMaxNumValues = 4;
221   struct DecoderContext {
222     uint32_t num_calls = 0;
223     uint32_t values[kMaxNumValues];
224     bool failed = false;
225   } decoder_context;
226 
227   pw_rpc_test_TestResponse response = pw_rpc_test_TestResponse_init_default;
228   response.repeated_field.funcs.decode = +[](pb_istream_t* stream,
229                                              const pb_field_t* /* field */,
230                                              void** arg) -> bool {
231     DecoderContext* dec_ctx = static_cast<DecoderContext*>(*arg);
232     uint64_t value;
233     if (!pb_decode_varint(stream, &value)) {
234       dec_ctx->failed = true;
235       return false;
236     }
237     if (dec_ctx->num_calls < kMaxNumValues) {
238       dec_ctx->values[dec_ctx->num_calls] = value;
239     }
240     dec_ctx->num_calls++;
241     return true;
242   };
243   response.repeated_field.arg = &decoder_context;
244   ctx.response<GeneratedService::TestAnotherUnaryRpc>(0, response);
245 
246   EXPECT_FALSE(decoder_context.failed);
247   EXPECT_EQ(3u, decoder_context.num_calls);
248   EXPECT_EQ(7u, decoder_context.values[0]);
249   EXPECT_EQ(8u, decoder_context.values[1]);
250   EXPECT_EQ(9u, decoder_context.values[2]);
251 }
252 
253 }  // namespace
254 }  // namespace pw::rpc
255