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 "pw_assert/check.h"
16 #include "pw_rpc/benchmark.rpc.pb.h"
17 #include "pw_rpc/integration_testing.h"
18 #include "pw_sync/binary_semaphore.h"
19 #include "pw_unit_test/framework.h"
20
21 namespace nanopb_rpc_test {
22 namespace {
23
24 using namespace std::chrono_literals;
25 using pw::ByteSpan;
26 using pw::ConstByteSpan;
27 using pw::Function;
28 using pw::OkStatus;
29 using pw::Status;
30
31 using pw::rpc::pw_rpc::nanopb::Benchmark;
32
33 constexpr int kIterations = 10;
34
35 class PayloadReceiver {
36 public:
Wait()37 const char* Wait() {
38 PW_CHECK(sem_.try_acquire_for(1500ms));
39 return reinterpret_cast<const char*>(payload_.payload.bytes);
40 }
41
UnaryOnCompleted()42 Function<void(const pw_rpc_Payload&, Status)> UnaryOnCompleted() {
43 return [this](const pw_rpc_Payload& data, Status) { CopyPayload(data); };
44 }
45
OnNext()46 Function<void(const pw_rpc_Payload&)> OnNext() {
47 return [this](const pw_rpc_Payload& data) { CopyPayload(data); };
48 }
49
50 private:
CopyPayload(const pw_rpc_Payload & data)51 void CopyPayload(const pw_rpc_Payload& data) {
52 payload_ = data;
53 sem_.release();
54 }
55
56 pw::sync::BinarySemaphore sem_;
57 pw_rpc_Payload payload_ = {};
58 };
59
60 template <size_t kSize>
Payload(const char (& string)[kSize])61 pw_rpc_Payload Payload(const char (&string)[kSize]) {
62 static_assert(kSize <= sizeof(pw_rpc_Payload::payload));
63 pw_rpc_Payload payload{};
64 std::memcpy(payload.payload.bytes, string, kSize);
65 payload.payload.size = kSize;
66 return payload;
67 }
68
69 const Benchmark::Client kClient(pw::rpc::integration_test::client(),
70 pw::rpc::integration_test::kChannelId);
71
TEST(NanopbRpcIntegrationTest,Unary)72 TEST(NanopbRpcIntegrationTest, Unary) {
73 char value[] = {"hello, world!"};
74
75 for (int i = 0; i < kIterations; ++i) {
76 PayloadReceiver receiver;
77
78 value[0] = static_cast<char>(i);
79 pw::rpc::NanopbUnaryReceiver call =
80 kClient.UnaryEcho(Payload(value), receiver.UnaryOnCompleted());
81 ASSERT_STREQ(receiver.Wait(), value);
82 }
83 }
84
TEST(NanopbRpcIntegrationTest,Unary_ReuseCall)85 TEST(NanopbRpcIntegrationTest, Unary_ReuseCall) {
86 pw::rpc::NanopbUnaryReceiver<pw_rpc_Payload> call;
87 char value[] = {"O_o "};
88
89 for (int i = 0; i < kIterations; ++i) {
90 PayloadReceiver receiver;
91
92 value[sizeof(value) - 2] = static_cast<char>(i);
93 call = kClient.UnaryEcho(Payload(value), receiver.UnaryOnCompleted());
94 ASSERT_STREQ(receiver.Wait(), value);
95 }
96 }
97
TEST(NanopbRpcIntegrationTest,Unary_DiscardCalls)98 TEST(NanopbRpcIntegrationTest, Unary_DiscardCalls) {
99 constexpr int iterations = PW_RPC_USE_GLOBAL_MUTEX ? 10000 : 1;
100 for (int i = 0; i < iterations; ++i) {
101 kClient.UnaryEcho(Payload("O_o"));
102 }
103 }
104
TEST(NanopbRpcIntegrationTest,BidirectionalStreaming_MoveCalls)105 TEST(NanopbRpcIntegrationTest, BidirectionalStreaming_MoveCalls) {
106 for (int i = 0; i < kIterations; ++i) {
107 PayloadReceiver receiver;
108 pw::rpc::NanopbClientReaderWriter call =
109 kClient.BidirectionalEcho(receiver.OnNext());
110
111 ASSERT_EQ(OkStatus(), call.Write(Payload("Yello")));
112 ASSERT_STREQ(receiver.Wait(), "Yello");
113
114 pw::rpc::NanopbClientReaderWriter<pw_rpc_Payload, pw_rpc_Payload> new_call =
115 std::move(call);
116
117 // NOLINTNEXTLINE(bugprone-use-after-move)
118 EXPECT_EQ(Status::FailedPrecondition(), call.Write(Payload("Dello")));
119
120 ASSERT_EQ(OkStatus(), new_call.Write(Payload("Dello")));
121 ASSERT_STREQ(receiver.Wait(), "Dello");
122
123 call = std::move(new_call);
124
125 // NOLINTNEXTLINE(bugprone-use-after-move)
126 EXPECT_EQ(Status::FailedPrecondition(), new_call.Write(Payload("Dello")));
127
128 ASSERT_EQ(OkStatus(), call.Write(Payload("???")));
129 ASSERT_STREQ(receiver.Wait(), "???");
130
131 EXPECT_EQ(OkStatus(), call.Cancel());
132 EXPECT_EQ(Status::FailedPrecondition(), new_call.Cancel());
133 }
134 }
135
TEST(NanopbRpcIntegrationTest,BidirectionalStreaming_ReuseCall)136 TEST(NanopbRpcIntegrationTest, BidirectionalStreaming_ReuseCall) {
137 pw::rpc::NanopbClientReaderWriter<pw_rpc_Payload, pw_rpc_Payload> call;
138
139 for (int i = 0; i < kIterations; ++i) {
140 PayloadReceiver receiver;
141 call = kClient.BidirectionalEcho(receiver.OnNext());
142
143 ASSERT_EQ(OkStatus(), call.Write(Payload("Yello")));
144 ASSERT_STREQ(receiver.Wait(), "Yello");
145
146 ASSERT_EQ(OkStatus(), call.Write(Payload("Dello")));
147 ASSERT_STREQ(receiver.Wait(), "Dello");
148
149 ASSERT_EQ(OkStatus(), call.Write(Payload("???")));
150 ASSERT_STREQ(receiver.Wait(), "???");
151 }
152 }
153
154 } // namespace
155 } // namespace nanopb_rpc_test
156