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