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 <algorithm>
16 #include <array>
17 #include <cstring>
18
19 #include "pw_assert/check.h"
20 #include "pw_log/log.h"
21 #include "pw_rpc/benchmark.raw_rpc.pb.h"
22 #include "pw_rpc/integration_testing.h"
23 #include "pw_sync/binary_semaphore.h"
24 #include "pw_unit_test/framework.h"
25
26 namespace rpc_test {
27 namespace {
28
29 constexpr int kIterations = 3;
30
31 using namespace std::chrono_literals;
32 using pw::ByteSpan;
33 using pw::ConstByteSpan;
34 using pw::Function;
35 using pw::OkStatus;
36 using pw::Status;
37
38 using pw::rpc::pw_rpc::raw::Benchmark;
39
40 Benchmark::Client kServiceClient(pw::rpc::integration_test::client(),
41 pw::rpc::integration_test::kChannelId);
42
43 class StringReceiver {
44 public:
Wait()45 const char* Wait() {
46 PW_CHECK(sem_.try_acquire_for(10s));
47 return reinterpret_cast<const char*>(buffer_.data());
48 }
49
UnaryOnCompleted()50 Function<void(ConstByteSpan, Status)> UnaryOnCompleted() {
51 return [this](ConstByteSpan data, Status) { CopyStringPayload(data); };
52 }
53
OnNext()54 Function<void(ConstByteSpan)> OnNext() {
55 return [this](ConstByteSpan data) { CopyStringPayload(data); };
56 }
57
CopyStringPayload(ConstByteSpan data)58 void CopyStringPayload(ConstByteSpan data) {
59 std::memset(buffer_.data(), 0, buffer_.size());
60 PW_CHECK_UINT_LE(data.size(), buffer_.size());
61 std::copy(data.begin(), data.end(), buffer_.begin());
62 sem_.release();
63 }
64
ReverseCopyStringPayload(ConstByteSpan data)65 void ReverseCopyStringPayload(ConstByteSpan data) {
66 std::memset(buffer_.data(), 0, buffer_.size());
67 PW_CHECK_UINT_LE(data.size(), buffer_.size());
68 std::reverse_copy(data.begin(), data.end() - 1, buffer_.begin());
69 sem_.release();
70 }
71
72 private:
73 pw::sync::BinarySemaphore sem_;
74 std::array<std::byte, 64> buffer_;
75 };
76
TEST(RawRpcIntegrationTest,Unary)77 TEST(RawRpcIntegrationTest, Unary) {
78 for (int i = 0; i < kIterations; ++i) {
79 StringReceiver receiver;
80 pw::rpc::RawUnaryReceiver call = kServiceClient.UnaryEcho(
81 pw::as_bytes(pw::span("hello")), receiver.UnaryOnCompleted());
82 EXPECT_STREQ(receiver.Wait(), "hello");
83 }
84 }
85
TEST(RawRpcIntegrationTest,BidirectionalStreaming)86 TEST(RawRpcIntegrationTest, BidirectionalStreaming) {
87 for (int i = 0; i < kIterations; ++i) {
88 StringReceiver receiver;
89 pw::rpc::RawClientReaderWriter call =
90 kServiceClient.BidirectionalEcho(receiver.OnNext());
91
92 ASSERT_EQ(OkStatus(), call.Write(pw::as_bytes(pw::span("Yello"))));
93 EXPECT_STREQ(receiver.Wait(), "Yello");
94
95 ASSERT_EQ(OkStatus(), call.Write(pw::as_bytes(pw::span("Dello"))));
96 EXPECT_STREQ(receiver.Wait(), "Dello");
97
98 ASSERT_EQ(OkStatus(), call.Cancel());
99 }
100 }
101
102 // This test sometimes fails due to a server stream packet being dropped.
103 // TODO: b/290048137 - Enable this test after the flakiness is fixed.
TEST(RawRpcIntegrationTest,DISABLED_OnNextOverwritesItsOwnCall)104 TEST(RawRpcIntegrationTest, DISABLED_OnNextOverwritesItsOwnCall) {
105 for (int i = 0; i < kIterations; ++i) {
106 struct {
107 StringReceiver receiver;
108 pw::rpc::RawClientReaderWriter call;
109 } ctx;
110
111 // Chain together three calls. The first and third copy the string in normal
112 // order, while the second copies the string in reverse order.
113 ctx.call = kServiceClient.BidirectionalEcho([&ctx](ConstByteSpan data_1) {
114 ctx.call = kServiceClient.BidirectionalEcho([&ctx](ConstByteSpan data_2) {
115 ctx.receiver.ReverseCopyStringPayload(data_2);
116 ctx.call = kServiceClient.BidirectionalEcho(ctx.receiver.OnNext());
117 });
118 ctx.receiver.CopyStringPayload(data_1);
119 });
120
121 ASSERT_EQ(OkStatus(), ctx.call.Write(pw::as_bytes(pw::span("Window"))));
122 EXPECT_STREQ(ctx.receiver.Wait(), "Window");
123
124 ASSERT_EQ(OkStatus(), ctx.call.Write(pw::as_bytes(pw::span("Door"))));
125 EXPECT_STREQ(ctx.receiver.Wait(), "rooD");
126
127 ASSERT_EQ(OkStatus(), ctx.call.Write(pw::as_bytes(pw::span("Roof"))));
128 EXPECT_STREQ(ctx.receiver.Wait(), "Roof");
129
130 ASSERT_EQ(OkStatus(), ctx.call.Cancel());
131 }
132 }
133
134 } // namespace
135 } // namespace rpc_test
136
main(int argc,char * argv[])137 int main(int argc, char* argv[]) {
138 if (!pw::rpc::integration_test::InitializeClient(argc, argv).ok()) {
139 return 1;
140 }
141
142 int test_retval = RUN_ALL_TESTS();
143
144 pw::rpc::integration_test::TerminateClient();
145
146 return test_retval;
147 }
148