xref: /aosp_15_r20/external/pigweed/pw_rpc/test_helpers_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2022 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/test_helpers.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <mutex>
18*61c4878aSAndroid Build Coastguard Worker 
19*61c4878aSAndroid Build Coastguard Worker #include "pw_chrono/system_clock.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_containers/vector.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_result/result.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/echo.pwpb.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/echo.rpc.pwpb.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/pwpb/client_testing.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/pwpb/server_reader_writer.h"
26*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status.h"
27*61c4878aSAndroid Build Coastguard Worker #include "pw_sync/interrupt_spin_lock.h"
28*61c4878aSAndroid Build Coastguard Worker #include "pw_sync/lock_annotations.h"
29*61c4878aSAndroid Build Coastguard Worker #include "pw_sync/timed_thread_notification.h"
30*61c4878aSAndroid Build Coastguard Worker #include "pw_unit_test/framework.h"
31*61c4878aSAndroid Build Coastguard Worker 
32*61c4878aSAndroid Build Coastguard Worker namespace pw::rpc::test {
33*61c4878aSAndroid Build Coastguard Worker namespace {
34*61c4878aSAndroid Build Coastguard Worker using namespace std::chrono_literals;
35*61c4878aSAndroid Build Coastguard Worker 
36*61c4878aSAndroid Build Coastguard Worker constexpr auto kWaitForEchoTimeout =
37*61c4878aSAndroid Build Coastguard Worker     pw::chrono::SystemClock::for_at_least(100ms);
38*61c4878aSAndroid Build Coastguard Worker 
39*61c4878aSAndroid Build Coastguard Worker // Class that we want to test.
40*61c4878aSAndroid Build Coastguard Worker //
41*61c4878aSAndroid Build Coastguard Worker // It's main purpose is to ask EchoService for Echo and provide its result
42*61c4878aSAndroid Build Coastguard Worker // through WaitForEcho/LastEcho pair to the user.
43*61c4878aSAndroid Build Coastguard Worker class EntityUnderTest {
44*61c4878aSAndroid Build Coastguard Worker  public:
EntityUnderTest(pw_rpc::pwpb::EchoService::Client & echo_client)45*61c4878aSAndroid Build Coastguard Worker   explicit EntityUnderTest(pw_rpc::pwpb::EchoService::Client& echo_client)
46*61c4878aSAndroid Build Coastguard Worker       : echo_client_(echo_client) {}
47*61c4878aSAndroid Build Coastguard Worker 
AskForEcho()48*61c4878aSAndroid Build Coastguard Worker   void AskForEcho() {
49*61c4878aSAndroid Build Coastguard Worker     call_ = echo_client_.Echo(
50*61c4878aSAndroid Build Coastguard Worker         pwpb::EchoMessage::Message{},
51*61c4878aSAndroid Build Coastguard Worker         [this](const pwpb::EchoMessage::Message& response, pw::Status status) {
52*61c4878aSAndroid Build Coastguard Worker           lock_.lock();
53*61c4878aSAndroid Build Coastguard Worker           if (status.ok()) {
54*61c4878aSAndroid Build Coastguard Worker             last_echo_ = response.msg;
55*61c4878aSAndroid Build Coastguard Worker           } else {
56*61c4878aSAndroid Build Coastguard Worker             last_echo_ = status;
57*61c4878aSAndroid Build Coastguard Worker           }
58*61c4878aSAndroid Build Coastguard Worker           lock_.unlock();
59*61c4878aSAndroid Build Coastguard Worker           notifier_.release();
60*61c4878aSAndroid Build Coastguard Worker         },
61*61c4878aSAndroid Build Coastguard Worker         [this](pw::Status status) {
62*61c4878aSAndroid Build Coastguard Worker           lock_.lock();
63*61c4878aSAndroid Build Coastguard Worker           last_echo_ = status;
64*61c4878aSAndroid Build Coastguard Worker           lock_.unlock();
65*61c4878aSAndroid Build Coastguard Worker           notifier_.release();
66*61c4878aSAndroid Build Coastguard Worker         });
67*61c4878aSAndroid Build Coastguard Worker   }
68*61c4878aSAndroid Build Coastguard Worker 
WaitForEcho(pw::chrono::SystemClock::duration duration)69*61c4878aSAndroid Build Coastguard Worker   bool WaitForEcho(pw::chrono::SystemClock::duration duration) {
70*61c4878aSAndroid Build Coastguard Worker     return notifier_.try_acquire_for(duration);
71*61c4878aSAndroid Build Coastguard Worker   }
72*61c4878aSAndroid Build Coastguard Worker 
LastEcho() const73*61c4878aSAndroid Build Coastguard Worker   pw::Result<pw::InlineString<64>> LastEcho() const {
74*61c4878aSAndroid Build Coastguard Worker     std::lock_guard<pw::sync::InterruptSpinLock> lock(lock_);
75*61c4878aSAndroid Build Coastguard Worker     return last_echo_;
76*61c4878aSAndroid Build Coastguard Worker   }
77*61c4878aSAndroid Build Coastguard Worker 
78*61c4878aSAndroid Build Coastguard Worker  private:
79*61c4878aSAndroid Build Coastguard Worker   pw_rpc::pwpb::EchoService::Client& echo_client_;
80*61c4878aSAndroid Build Coastguard Worker   PwpbUnaryReceiver<pwpb::EchoMessage::Message> call_;
81*61c4878aSAndroid Build Coastguard Worker   pw::sync::TimedThreadNotification notifier_;
82*61c4878aSAndroid Build Coastguard Worker   pw::Result<pw::InlineString<64>> last_echo_ PW_GUARDED_BY(lock_);
83*61c4878aSAndroid Build Coastguard Worker   mutable pw::sync::InterruptSpinLock lock_;
84*61c4878aSAndroid Build Coastguard Worker };
85*61c4878aSAndroid Build Coastguard Worker 
TEST(RpcTestHelpersTest,SendResponseIfCalledOk)86*61c4878aSAndroid Build Coastguard Worker TEST(RpcTestHelpersTest, SendResponseIfCalledOk) {
87*61c4878aSAndroid Build Coastguard Worker   PwpbClientTestContext client_context;
88*61c4878aSAndroid Build Coastguard Worker   pw_rpc::pwpb::EchoService::Client client(client_context.client(),
89*61c4878aSAndroid Build Coastguard Worker                                            client_context.channel().id());
90*61c4878aSAndroid Build Coastguard Worker   EntityUnderTest entity(client);
91*61c4878aSAndroid Build Coastguard Worker 
92*61c4878aSAndroid Build Coastguard Worker   // We need to call the function that will initiate the request before we can
93*61c4878aSAndroid Build Coastguard Worker   // send the response back.
94*61c4878aSAndroid Build Coastguard Worker   entity.AskForEcho();
95*61c4878aSAndroid Build Coastguard Worker 
96*61c4878aSAndroid Build Coastguard Worker   // SendResponseIfCalled blocks until request is received by the service (it is
97*61c4878aSAndroid Build Coastguard Worker   // sent by AskForEcho to EchoService in this case) and responds to it with the
98*61c4878aSAndroid Build Coastguard Worker   // response.
99*61c4878aSAndroid Build Coastguard Worker   //
100*61c4878aSAndroid Build Coastguard Worker   // SendResponseIfCalled will timeout if no request were sent in the `timeout`
101*61c4878aSAndroid Build Coastguard Worker   // interval (see SendResponseIfCalledWithoutRequest test for the example).
102*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(SendResponseIfCalled<pw_rpc::pwpb::EchoService::Echo>(
103*61c4878aSAndroid Build Coastguard Worker                 client_context, {.msg = "Hello"}),
104*61c4878aSAndroid Build Coastguard Worker             OkStatus());
105*61c4878aSAndroid Build Coastguard Worker 
106*61c4878aSAndroid Build Coastguard Worker   // After SendResponseIfCalled returned OkStatus client should have received
107*61c4878aSAndroid Build Coastguard Worker   // the response back in the RPC thread, so we can check it here. Because it is
108*61c4878aSAndroid Build Coastguard Worker   // a separate thread we still need to wait with the timeout.
109*61c4878aSAndroid Build Coastguard Worker   ASSERT_TRUE(entity.WaitForEcho(kWaitForEchoTimeout));
110*61c4878aSAndroid Build Coastguard Worker 
111*61c4878aSAndroid Build Coastguard Worker   pw::Result<pw::InlineString<64>> result = entity.LastEcho();
112*61c4878aSAndroid Build Coastguard Worker   ASSERT_TRUE(result.ok());
113*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(result.value(), "Hello");
114*61c4878aSAndroid Build Coastguard Worker }
115*61c4878aSAndroid Build Coastguard Worker 
TEST(RpcTestHelpersTest,SendResponseIfCalledNotOk)116*61c4878aSAndroid Build Coastguard Worker TEST(RpcTestHelpersTest, SendResponseIfCalledNotOk) {
117*61c4878aSAndroid Build Coastguard Worker   PwpbClientTestContext client_context;
118*61c4878aSAndroid Build Coastguard Worker   pw_rpc::pwpb::EchoService::Client client(client_context.client(),
119*61c4878aSAndroid Build Coastguard Worker                                            client_context.channel().id());
120*61c4878aSAndroid Build Coastguard Worker   EntityUnderTest entity(client);
121*61c4878aSAndroid Build Coastguard Worker 
122*61c4878aSAndroid Build Coastguard Worker   // We need to call the function that will initiate the request before we can
123*61c4878aSAndroid Build Coastguard Worker   // send the response back.
124*61c4878aSAndroid Build Coastguard Worker   entity.AskForEcho();
125*61c4878aSAndroid Build Coastguard Worker 
126*61c4878aSAndroid Build Coastguard Worker   // SendResponseIfCalled also can be used to respond with failures. In this
127*61c4878aSAndroid Build Coastguard Worker   // case we are sending back pw::Status::InvalidArgument and expect to see it
128*61c4878aSAndroid Build Coastguard Worker   // on the client side.
129*61c4878aSAndroid Build Coastguard Worker   //
130*61c4878aSAndroid Build Coastguard Worker   // SendResponseIfCalled result status is not the same status as it sends to
131*61c4878aSAndroid Build Coastguard Worker   // the client, so we still are expecting the OkStatus here.
132*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(SendResponseIfCalled<pw_rpc::pwpb::EchoService::Echo>(
133*61c4878aSAndroid Build Coastguard Worker                 client_context, {}, pw::Status::InvalidArgument()),
134*61c4878aSAndroid Build Coastguard Worker             OkStatus());
135*61c4878aSAndroid Build Coastguard Worker 
136*61c4878aSAndroid Build Coastguard Worker   // After SendResponseIfCalled returned OkStatus client should have received
137*61c4878aSAndroid Build Coastguard Worker   // the response back in the RPC thread, so we can check it here. Because it is
138*61c4878aSAndroid Build Coastguard Worker   // a separate thread we still need to wait with the timeout.
139*61c4878aSAndroid Build Coastguard Worker   ASSERT_TRUE(entity.WaitForEcho(kWaitForEchoTimeout));
140*61c4878aSAndroid Build Coastguard Worker 
141*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(entity.LastEcho().status(), Status::InvalidArgument());
142*61c4878aSAndroid Build Coastguard Worker }
143*61c4878aSAndroid Build Coastguard Worker 
TEST(RpcTestHelpersTest,SendResponseIfCalledNotOkShortcut)144*61c4878aSAndroid Build Coastguard Worker TEST(RpcTestHelpersTest, SendResponseIfCalledNotOkShortcut) {
145*61c4878aSAndroid Build Coastguard Worker   PwpbClientTestContext client_context;
146*61c4878aSAndroid Build Coastguard Worker   pw_rpc::pwpb::EchoService::Client client(client_context.client(),
147*61c4878aSAndroid Build Coastguard Worker                                            client_context.channel().id());
148*61c4878aSAndroid Build Coastguard Worker   EntityUnderTest entity(client);
149*61c4878aSAndroid Build Coastguard Worker 
150*61c4878aSAndroid Build Coastguard Worker   // We need to call the function that will initiate the request before we can
151*61c4878aSAndroid Build Coastguard Worker   // send the response back.
152*61c4878aSAndroid Build Coastguard Worker   entity.AskForEcho();
153*61c4878aSAndroid Build Coastguard Worker 
154*61c4878aSAndroid Build Coastguard Worker   // SendResponseIfCalled shortcut version exists to respond with failures. It
155*61c4878aSAndroid Build Coastguard Worker   // works exactly the same, but doesn't have the response argument. In this
156*61c4878aSAndroid Build Coastguard Worker   // case we are sending back pw::Status::InvalidArgument and expect to see it
157*61c4878aSAndroid Build Coastguard Worker   // on the client side.
158*61c4878aSAndroid Build Coastguard Worker   //
159*61c4878aSAndroid Build Coastguard Worker   // SendResponseIfCalled result status is not the same status as it sends to
160*61c4878aSAndroid Build Coastguard Worker   // the client, so we still are expecting the OkStatus here.
161*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(SendResponseIfCalled<pw_rpc::pwpb::EchoService::Echo>(
162*61c4878aSAndroid Build Coastguard Worker                 client_context, pw::Status::InvalidArgument()),
163*61c4878aSAndroid Build Coastguard Worker             OkStatus());
164*61c4878aSAndroid Build Coastguard Worker 
165*61c4878aSAndroid Build Coastguard Worker   // After SendResponseIfCalled returned OkStatus client should have received
166*61c4878aSAndroid Build Coastguard Worker   // the response back in the RPC thread, so we can check it here. Because it is
167*61c4878aSAndroid Build Coastguard Worker   // a separate thread we still need to wait with the timeout.
168*61c4878aSAndroid Build Coastguard Worker   ASSERT_TRUE(entity.WaitForEcho(kWaitForEchoTimeout));
169*61c4878aSAndroid Build Coastguard Worker 
170*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(entity.LastEcho().status(), Status::InvalidArgument());
171*61c4878aSAndroid Build Coastguard Worker }
172*61c4878aSAndroid Build Coastguard Worker 
TEST(RpcTestHelpersTest,SendResponseIfCalledWithoutRequest)173*61c4878aSAndroid Build Coastguard Worker TEST(RpcTestHelpersTest, SendResponseIfCalledWithoutRequest) {
174*61c4878aSAndroid Build Coastguard Worker   PwpbClientTestContext client_context;
175*61c4878aSAndroid Build Coastguard Worker   pw_rpc::pwpb::EchoService::Client client(client_context.client(),
176*61c4878aSAndroid Build Coastguard Worker                                            client_context.channel().id());
177*61c4878aSAndroid Build Coastguard Worker 
178*61c4878aSAndroid Build Coastguard Worker   // We don't send any request in this test and SendResponseIfCalled is expected
179*61c4878aSAndroid Build Coastguard Worker   // to fail on waiting for the request with pw::Status::FailedPrecondition.
180*61c4878aSAndroid Build Coastguard Worker 
181*61c4878aSAndroid Build Coastguard Worker   const auto start_time = pw::chrono::SystemClock::now();
182*61c4878aSAndroid Build Coastguard Worker   auto status = SendResponseIfCalled<pw_rpc::pwpb::EchoService::Echo>(
183*61c4878aSAndroid Build Coastguard Worker       client_context,
184*61c4878aSAndroid Build Coastguard Worker       {.msg = "Hello"},
185*61c4878aSAndroid Build Coastguard Worker       pw::OkStatus(),
186*61c4878aSAndroid Build Coastguard Worker       /*timeout=*/pw::chrono::SystemClock::for_at_least(10ms));
187*61c4878aSAndroid Build Coastguard Worker 
188*61c4878aSAndroid Build Coastguard Worker   // We set our timeout for SendResponseIfCalled to 10ms, so it should be at
189*61c4878aSAndroid Build Coastguard Worker   // least 10ms since we called the SendResponseIfCalled.
190*61c4878aSAndroid Build Coastguard Worker   EXPECT_GE(pw::chrono::SystemClock::now() - start_time,
191*61c4878aSAndroid Build Coastguard Worker             pw::chrono::SystemClock::for_at_least(10ms));
192*61c4878aSAndroid Build Coastguard Worker 
193*61c4878aSAndroid Build Coastguard Worker   // We expect SendResponseIfCalled to fail, because there were no request sent
194*61c4878aSAndroid Build Coastguard Worker   // for the given method.
195*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(status, Status::DeadlineExceeded());
196*61c4878aSAndroid Build Coastguard Worker }
197*61c4878aSAndroid Build Coastguard Worker 
198*61c4878aSAndroid Build Coastguard Worker }  // namespace
199*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::rpc::test
200