1 // Copyright 2020 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_rpc/internal/hash.h"
17 #include "pw_rpc/nanopb_test_method_context.h"
18 #include "pw_rpc_nanopb_private/internal_test_utils.h"
19 #include "pw_rpc_private/internal_test_utils.h"
20 #include "pw_rpc_test_protos/test.rpc.pb.h"
21
22 namespace pw::rpc {
23 namespace test {
24
25 class TestService final : public generated::TestService<TestService> {
26 public:
TestRpc(ServerContext &,const pw_rpc_test_TestRequest & request,pw_rpc_test_TestResponse & response)27 Status TestRpc(ServerContext&,
28 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
TestStreamRpc(ServerContext &,const pw_rpc_test_TestRequest & request,ServerWriter<pw_rpc_test_TestStreamResponse> & writer)34 static void TestStreamRpc(
35 ServerContext&,
36 const pw_rpc_test_TestRequest& request,
37 ServerWriter<pw_rpc_test_TestStreamResponse>& writer) {
38 for (int i = 0; i < request.integer; ++i) {
39 writer.Write({.chunk = {}, .number = static_cast<uint32_t>(i)});
40 }
41
42 writer.Finish(static_cast<Status::Code>(request.status_code));
43 }
44 };
45
46 } // namespace test
47
48 namespace {
49
TEST(NanopbCodegen,CompilesProperly)50 TEST(NanopbCodegen, CompilesProperly) {
51 test::TestService service;
52 EXPECT_EQ(service.id(), internal::Hash("pw.rpc.test.TestService"));
53 EXPECT_STREQ(service.name(), "TestService");
54 }
55
TEST(NanopbCodegen,Server_InvokeUnaryRpc)56 TEST(NanopbCodegen, Server_InvokeUnaryRpc) {
57 PW_NANOPB_TEST_METHOD_CONTEXT(test::TestService, TestRpc) context;
58
59 EXPECT_EQ(OkStatus(),
60 context.call({.integer = 123, .status_code = OkStatus().code()}));
61
62 EXPECT_EQ(124, context.response().value);
63
64 EXPECT_EQ(Status::InvalidArgument(),
65 context.call({.integer = 999,
66 .status_code = Status::InvalidArgument().code()}));
67 EXPECT_EQ(1000, context.response().value);
68 }
69
TEST(NanopbCodegen,Server_InvokeStreamingRpc)70 TEST(NanopbCodegen, Server_InvokeStreamingRpc) {
71 PW_NANOPB_TEST_METHOD_CONTEXT(test::TestService, TestStreamRpc) context;
72
73 context.call({.integer = 0, .status_code = Status::Aborted().code()});
74
75 EXPECT_EQ(Status::Aborted(), context.status());
76 EXPECT_TRUE(context.done());
77 EXPECT_TRUE(context.responses().empty());
78 EXPECT_EQ(0u, context.total_responses());
79
80 context.call({.integer = 4, .status_code = OkStatus().code()});
81
82 ASSERT_EQ(4u, context.responses().size());
83 ASSERT_EQ(4u, context.total_responses());
84
85 for (size_t i = 0; i < context.responses().size(); ++i) {
86 EXPECT_EQ(context.responses()[i].number, i);
87 }
88
89 EXPECT_EQ(OkStatus().code(), context.status());
90 }
91
TEST(NanopbCodegen,Server_InvokeStreamingRpc_ContextKeepsFixedNumberOfResponses)92 TEST(NanopbCodegen,
93 Server_InvokeStreamingRpc_ContextKeepsFixedNumberOfResponses) {
94 PW_NANOPB_TEST_METHOD_CONTEXT(test::TestService, TestStreamRpc, 3) context;
95
96 ASSERT_EQ(3u, context.responses().max_size());
97
98 context.call({.integer = 5, .status_code = Status::NotFound().code()});
99
100 ASSERT_EQ(3u, context.responses().size());
101 ASSERT_EQ(5u, context.total_responses());
102
103 EXPECT_EQ(context.responses()[0].number, 0u);
104 EXPECT_EQ(context.responses()[1].number, 1u);
105 EXPECT_EQ(context.responses()[2].number, 4u);
106 }
107
TEST(NanopbCodegen,Server_InvokeStreamingRpc_ManualWriting)108 TEST(NanopbCodegen, Server_InvokeStreamingRpc_ManualWriting) {
109 PW_NANOPB_TEST_METHOD_CONTEXT(test::TestService, TestStreamRpc, 3) context;
110
111 ASSERT_EQ(3u, context.responses().max_size());
112
113 auto writer = context.writer();
114
115 writer.Write({.chunk = {}, .number = 3});
116 writer.Write({.chunk = {}, .number = 6});
117 writer.Write({.chunk = {}, .number = 9});
118
119 EXPECT_FALSE(context.done());
120
121 writer.Finish(Status::Cancelled());
122 ASSERT_TRUE(context.done());
123 EXPECT_EQ(Status::Cancelled(), context.status());
124
125 ASSERT_EQ(3u, context.responses().size());
126 ASSERT_EQ(3u, context.total_responses());
127
128 EXPECT_EQ(context.responses()[0].number, 3u);
129 EXPECT_EQ(context.responses()[1].number, 6u);
130 EXPECT_EQ(context.responses()[2].number, 9u);
131 }
132
133 using TestServiceClient = test::nanopb::TestServiceClient;
134 using internal::TestServerStreamingResponseHandler;
135 using internal::TestUnaryResponseHandler;
136
TEST(NanopbCodegen,Client_InvokesUnaryRpcWithCallback)137 TEST(NanopbCodegen, Client_InvokesUnaryRpcWithCallback) {
138 constexpr uint32_t service_id = internal::Hash("pw.rpc.test.TestService");
139 constexpr uint32_t method_id = internal::Hash("TestRpc");
140
141 ClientContextForTest<128, 128, 99, service_id, method_id> context;
142 TestUnaryResponseHandler<pw_rpc_test_TestResponse> handler;
143
144 auto call = TestServiceClient::TestRpc(
145 context.channel(), {.integer = 123, .status_code = 0}, handler);
146 EXPECT_EQ(context.output().packet_count(), 1u);
147 auto packet = context.output().sent_packet();
148 EXPECT_EQ(packet.channel_id(), context.channel().id());
149 EXPECT_EQ(packet.service_id(), service_id);
150 EXPECT_EQ(packet.method_id(), method_id);
151 PW_DECODE_PB(pw_rpc_test_TestRequest, sent_proto, packet.payload());
152 EXPECT_EQ(sent_proto.integer, 123);
153
154 PW_ENCODE_PB(pw_rpc_test_TestResponse, response, .value = 42);
155 context.SendResponse(OkStatus(), response);
156 ASSERT_EQ(handler.responses_received(), 1u);
157 EXPECT_EQ(handler.last_status(), OkStatus());
158 EXPECT_EQ(handler.last_response().value, 42);
159 }
160
TEST(NanopbCodegen,Client_InvokesServerStreamingRpcWithCallback)161 TEST(NanopbCodegen, Client_InvokesServerStreamingRpcWithCallback) {
162 constexpr uint32_t service_id = internal::Hash("pw.rpc.test.TestService");
163 constexpr uint32_t method_id = internal::Hash("TestStreamRpc");
164
165 ClientContextForTest<128, 128, 99, service_id, method_id> context;
166 TestServerStreamingResponseHandler<pw_rpc_test_TestStreamResponse> handler;
167
168 auto call = TestServiceClient::TestStreamRpc(
169 context.channel(), {.integer = 123, .status_code = 0}, handler);
170 EXPECT_EQ(context.output().packet_count(), 1u);
171 auto packet = context.output().sent_packet();
172 EXPECT_EQ(packet.channel_id(), context.channel().id());
173 EXPECT_EQ(packet.service_id(), service_id);
174 EXPECT_EQ(packet.method_id(), method_id);
175 PW_DECODE_PB(pw_rpc_test_TestRequest, sent_proto, packet.payload());
176 EXPECT_EQ(sent_proto.integer, 123);
177
178 PW_ENCODE_PB(
179 pw_rpc_test_TestStreamResponse, response, .chunk = {}, .number = 11u);
180 context.SendResponse(OkStatus(), response);
181 ASSERT_EQ(handler.responses_received(), 1u);
182 EXPECT_EQ(handler.last_response().number, 11u);
183
184 context.SendPacket(internal::PacketType::SERVER_STREAM_END,
185 Status::NotFound());
186 EXPECT_FALSE(handler.active());
187 EXPECT_EQ(handler.status(), Status::NotFound());
188 }
189
190 } // namespace
191 } // namespace pw::rpc
192