xref: /aosp_15_r20/external/perfetto/src/ipc/client_impl_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/ipc/client_impl.h"
18 
19 #include <stdio.h>
20 
21 #include <string>
22 
23 #include "perfetto/ext/base/file_utils.h"
24 #include "perfetto/ext/base/temp_file.h"
25 #include "perfetto/ext/base/unix_socket.h"
26 #include "perfetto/ext/base/utils.h"
27 #include "perfetto/ext/ipc/service_descriptor.h"
28 #include "perfetto/ext/ipc/service_proxy.h"
29 #include "src/base/test/test_task_runner.h"
30 #include "src/ipc/buffered_frame_deserializer.h"
31 #include "src/ipc/test/test_socket.h"
32 #include "test/gtest_and_gmock.h"
33 
34 #include "protos/perfetto/ipc/wire_protocol.gen.h"
35 #include "src/ipc/test/client_unittest_messages.gen.h"
36 
37 namespace perfetto {
38 namespace ipc {
39 namespace {
40 
41 using ::perfetto::ipc::gen::ReplyProto;
42 using ::perfetto::ipc::gen::RequestProto;
43 using ::testing::_;
44 using ::testing::InSequence;
45 using ::testing::Invoke;
46 using ::testing::Mock;
47 
48 ipc::TestSocket kTestSocket{"client_impl_unittest"};
49 
50 // A fake ServiceProxy. This fakes the client-side class that would be
51 // auto-generated from .proto-files.
52 class FakeProxy : public ServiceProxy {
53  public:
FakeProxy(const char * service_name,ServiceProxy::EventListener * el)54   FakeProxy(const char* service_name, ServiceProxy::EventListener* el)
55       : ServiceProxy(el), service_name_(service_name) {}
56 
GetDescriptor()57   const ServiceDescriptor& GetDescriptor() override {
58     auto reply_decoder = [](const std::string& proto) {
59       std::unique_ptr<ProtoMessage> reply(new ReplyProto());
60       EXPECT_TRUE(reply->ParseFromString(proto));
61       return reply;
62     };
63     if (!descriptor_.service_name) {
64       descriptor_.service_name = service_name_;
65       descriptor_.methods.push_back(
66           {"FakeMethod1", nullptr, reply_decoder, nullptr});
67     }
68     return descriptor_;
69   }
70 
71   const char* service_name_;
72   ServiceDescriptor descriptor_;
73 };
74 
75 class MockEventListener : public ServiceProxy::EventListener {
76  public:
77   MOCK_METHOD(void, OnConnect, (), (override));
78   MOCK_METHOD(void, OnDisconnect, (), (override));
79 };
80 
81 // A fake host implementation. Listens on |kTestSocket.name()| and replies to
82 // IPC metohds like a real one.
83 class FakeHost : public base::UnixSocket::EventListener {
84  public:
85   struct FakeMethod {
86     MethodID id;
87     MOCK_METHOD(void,
88                 OnInvoke,
89                 (const Frame::InvokeMethod&, Frame::InvokeMethodReply*));
90   };  // FakeMethod.
91 
92   struct FakeService {
AddFakeMethodperfetto::ipc::__anon03c600040111::FakeHost::FakeService93     FakeMethod* AddFakeMethod(const std::string& name) {
94       auto it_and_inserted =
95           methods.emplace(name, std::unique_ptr<FakeMethod>(new FakeMethod()));
96       EXPECT_TRUE(it_and_inserted.second);
97       FakeMethod* method = it_and_inserted.first->second.get();
98       method->id = ++last_method_id;
99       return method;
100     }
101 
102     ServiceID id;
103     std::map<std::string, std::unique_ptr<FakeMethod>> methods;
104     MethodID last_method_id = 0;
105   };  // FakeService.
106 
107   // If |should_listen| is set, then a listening socket is created.
108   // Otherwise, incoming connections should be delivered via
109   // OnNewIncomingConnection().
FakeHost(bool should_listen,base::TaskRunner * task_runner)110   FakeHost(bool should_listen, base::TaskRunner* task_runner) {
111     if (should_listen) {
112       kTestSocket.Destroy();
113       listening_sock_ = base::UnixSocket::Listen(
114           kTestSocket.name(), this, task_runner, kTestSocket.family(),
115           base::SockType::kStream);
116       EXPECT_TRUE(listening_sock_->is_listening());
117     }
118   }
119 
~FakeHost()120   ~FakeHost() override { kTestSocket.Destroy(); }
121 
AddFakeService(const std::string & name)122   FakeService* AddFakeService(const std::string& name) {
123     auto it_and_inserted = services_.emplace(
124         name, std::unique_ptr<FakeService>(new FakeService()));
125     EXPECT_TRUE(it_and_inserted.second);
126     FakeService* svc = it_and_inserted.first->second.get();
127     svc->id = ++last_service_id_;
128     return svc;
129   }
130 
131   // base::UnixSocket::EventListener implementation.
OnNewIncomingConnection(base::UnixSocket *,std::unique_ptr<base::UnixSocket> new_connection)132   void OnNewIncomingConnection(
133       base::UnixSocket*,
134       std::unique_ptr<base::UnixSocket> new_connection) override {
135     ASSERT_FALSE(client_sock_);
136     client_sock_ = std::move(new_connection);
137   }
138 
OnDataAvailable(base::UnixSocket * sock)139   void OnDataAvailable(base::UnixSocket* sock) override {
140     if (sock != client_sock_.get())
141       return;
142     auto buf = frame_deserializer_.BeginReceive();
143     base::ScopedFile fd;
144     size_t rsize = client_sock_->Receive(buf.data, buf.size, &fd);
145     if (fd)
146       received_fd_ = std::move(fd);
147     EXPECT_TRUE(frame_deserializer_.EndReceive(rsize));
148     while (std::unique_ptr<Frame> frame = frame_deserializer_.PopNextFrame())
149       OnFrameReceived(*frame);
150   }
151 
OnFrameReceived(const Frame & req)152   void OnFrameReceived(const Frame& req) {
153     if (req.has_msg_bind_service()) {
154       auto svc_it = services_.find(req.msg_bind_service().service_name());
155       ASSERT_NE(services_.end(), svc_it);
156       const FakeService& svc = *svc_it->second;
157       Frame reply;
158       reply.set_request_id(req.request_id());
159       reply.mutable_msg_bind_service_reply()->set_success(true);
160       reply.mutable_msg_bind_service_reply()->set_service_id(svc.id);
161       for (const auto& method_it : svc.methods) {
162         auto* method = reply.mutable_msg_bind_service_reply()->add_methods();
163         method->set_name(method_it.first);
164         method->set_id(method_it.second->id);
165       }
166       Reply(reply);
167     } else if (req.has_msg_invoke_method()) {
168       // Lookup the service and method.
169       bool has_more = false;
170       do {
171         Frame reply;
172         reply.set_request_id(req.request_id());
173         for (const auto& svc : services_) {
174           if (svc.second->id != req.msg_invoke_method().service_id())
175             continue;
176           for (const auto& method : svc.second->methods) {
177             if (method.second->id != req.msg_invoke_method().method_id())
178               continue;
179             method.second->OnInvoke(req.msg_invoke_method(),
180                                     reply.mutable_msg_invoke_method_reply());
181             has_more = reply.mutable_msg_invoke_method_reply()->has_more();
182           }
183         }
184         // If either the method or the service are not found, |success| will be
185         // false by default.
186         Reply(reply);
187       } while (has_more);
188     } else {
189       FAIL() << "Unknown request";
190     }
191   }
192 
Reply(const Frame & frame)193   void Reply(const Frame& frame) {
194     if (suppress_replies_)
195       return;
196     auto buf = BufferedFrameDeserializer::Serialize(frame);
197     ASSERT_TRUE(client_sock_->is_connected());
198     EXPECT_TRUE(client_sock_->Send(buf.data(), buf.size(), next_reply_fd_));
199     next_reply_fd_ = -1;
200   }
201 
202   BufferedFrameDeserializer frame_deserializer_;
203   std::unique_ptr<base::UnixSocket> listening_sock_;
204   std::unique_ptr<base::UnixSocket> client_sock_;
205   std::map<std::string, std::unique_ptr<FakeService>> services_;
206   ServiceID last_service_id_ = 0;
207   int next_reply_fd_ = -1;
208   base::ScopedFile received_fd_;
209   // This flag can be set to true to prevent the host from sending replies.
210   // Useful to test race conditions in ClientImpl.
211   bool suppress_replies_ = false;
212 };  // FakeHost.
213 
214 class ClientImplTest : public ::testing::Test {
215  public:
SetUp()216   void SetUp() override {
217     task_runner_.reset(new base::TestTaskRunner());
218 
219 #if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
220     auto socket_pair = base::UnixSocketRaw::CreatePairPosix(
221         base::SockFamily::kUnix, base::SockType::kStream);
222     host_.reset(new FakeHost(false, task_runner_.get()));
223     host_->OnNewIncomingConnection(
224         nullptr,
225         base::UnixSocket::AdoptConnected(
226             socket_pair.first.ReleaseFd(), host_.get(), task_runner_.get(),
227             kTestSocket.family(), base::SockType::kStream));
228     cli_ = Client::CreateInstance(Client::ConnArgs(base::ScopedSocketHandle(
229                                       socket_pair.second.ReleaseFd())),
230                                   task_runner_.get());
231 #else
232     host_.reset(new FakeHost(true, task_runner_.get()));
233     cli_ = Client::CreateInstance({kTestSocket.name(), /*retry=*/false},
234                                   task_runner_.get());
235 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
236   }
237 
TearDown()238   void TearDown() override {
239     cli_.reset();
240     host_.reset();
241     task_runner_->RunUntilIdle();
242     task_runner_.reset();
243   }
244 
245   ::testing::StrictMock<MockEventListener> proxy_events_;
246   std::unique_ptr<base::TestTaskRunner> task_runner_;
247   std::unique_ptr<FakeHost> host_;
248   std::unique_ptr<Client> cli_;
249 };
250 
TEST_F(ClientImplTest,BindAndInvokeMethod)251 TEST_F(ClientImplTest, BindAndInvokeMethod) {
252   auto* host_svc = host_->AddFakeService("FakeSvc");
253   auto* host_method = host_svc->AddFakeMethod("FakeMethod1");
254 
255   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
256 
257   // Bind |proxy| to the fake host.
258   cli_->BindService(proxy->GetWeakPtr());
259   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
260   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
261   task_runner_->RunUntilCheckpoint("on_connect");
262 
263   // Invoke a valid method.
264   EXPECT_CALL(*host_method, OnInvoke(_, _))
265       .WillOnce(Invoke(
266           [](const Frame::InvokeMethod& req, Frame::InvokeMethodReply* reply) {
267             RequestProto req_args;
268             EXPECT_TRUE(req_args.ParseFromString(req.args_proto()));
269             EXPECT_EQ("req_data", req_args.data());
270             ReplyProto reply_args;
271             reply->set_reply_proto(reply_args.SerializeAsString());
272             reply->set_success(true);
273           }));
274 
275   RequestProto req;
276   req.set_data("req_data");
277   auto on_invoke_reply = task_runner_->CreateCheckpoint("on_invoke_reply");
278   Deferred<ProtoMessage> deferred_reply(
279       [on_invoke_reply](AsyncResult<ProtoMessage> reply) {
280         EXPECT_TRUE(reply.success());
281         on_invoke_reply();
282       });
283   proxy->BeginInvoke("FakeMethod1", req, std::move(deferred_reply));
284   task_runner_->RunUntilCheckpoint("on_invoke_reply");
285 
286   // Invoke an invalid method.
287   auto on_invalid_invoke = task_runner_->CreateCheckpoint("on_invalid_invoke");
288   Deferred<ProtoMessage> deferred_reply2(
289       [on_invalid_invoke](AsyncResult<ProtoMessage> reply) {
290         EXPECT_FALSE(reply.success());
291         on_invalid_invoke();
292       });
293   RequestProto empty_req;
294   proxy->BeginInvoke("InvalidMethod", empty_req, std::move(deferred_reply2));
295   task_runner_->RunUntilCheckpoint("on_invalid_invoke");
296 }
297 
298 // Tests that when invoking a method without binding a callback, the resulting
299 // request has the |drop_reply| flag set.
TEST_F(ClientImplTest,InvokeMethodDropReply)300 TEST_F(ClientImplTest, InvokeMethodDropReply) {
301   auto* host_svc = host_->AddFakeService("FakeSvc");
302   auto* host_method = host_svc->AddFakeMethod("FakeMethod1");
303 
304   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
305 
306   // Bind |proxy| to the fake host.
307   cli_->BindService(proxy->GetWeakPtr());
308   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
309   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
310   task_runner_->RunUntilCheckpoint("on_connect");
311 
312   auto on_req_received = task_runner_->CreateCheckpoint("on_req_received");
313   EXPECT_CALL(*host_method, OnInvoke(_, _))
314       .WillOnce(Invoke([on_req_received](const Frame::InvokeMethod& req,
315                                          Frame::InvokeMethodReply*) {
316         RequestProto req_args;
317         EXPECT_TRUE(req.drop_reply());
318         on_req_received();
319       }));
320 
321   // Invoke a method without binding any callback to the Deferred object.
322   Deferred<ProtoMessage> no_callback;
323   proxy->BeginInvoke("FakeMethod1", RequestProto(), std::move(no_callback));
324   task_runner_->RunUntilCheckpoint("on_req_received");
325 }
326 
327 // Like BindAndInvokeMethod, but this time invoke a streaming method that
328 // provides > 1 reply per invocation.
TEST_F(ClientImplTest,BindAndInvokeStreamingMethod)329 TEST_F(ClientImplTest, BindAndInvokeStreamingMethod) {
330   auto* host_svc = host_->AddFakeService("FakeSvc");
331   auto* host_method = host_svc->AddFakeMethod("FakeMethod1");
332   const int kNumReplies = 3;
333 
334   // Create and bind |proxy| to the fake host.
335   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
336   cli_->BindService(proxy->GetWeakPtr());
337   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
338   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
339   task_runner_->RunUntilCheckpoint("on_connect");
340 
341   // Invoke a valid method, reply kNumReplies times.
342   int replies_left = kNumReplies;
343   EXPECT_CALL(*host_method, OnInvoke(_, _))
344       .Times(kNumReplies)
345       .WillRepeatedly(Invoke([&replies_left](const Frame::InvokeMethod& req,
346                                              Frame::InvokeMethodReply* reply) {
347         RequestProto req_args;
348         EXPECT_TRUE(req_args.ParseFromString(req.args_proto()));
349         reply->set_reply_proto(ReplyProto().SerializeAsString());
350         reply->set_success(true);
351         reply->set_has_more(--replies_left > 0);
352       }));
353 
354   RequestProto req;
355   req.set_data("req_data");
356   auto on_last_reply = task_runner_->CreateCheckpoint("on_last_reply");
357   int replies_seen = 0;
358   Deferred<ProtoMessage> deferred_reply(
359       [on_last_reply, &replies_seen](AsyncResult<ProtoMessage> reply) {
360         EXPECT_TRUE(reply.success());
361         replies_seen++;
362         if (!reply.has_more())
363           on_last_reply();
364       });
365   proxy->BeginInvoke("FakeMethod1", req, std::move(deferred_reply));
366   task_runner_->RunUntilCheckpoint("on_last_reply");
367   ASSERT_EQ(kNumReplies, replies_seen);
368 }
369 
370 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
371     !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
372 // File descriptor sending over IPC is not supported on Windows or Fuchsia.
TEST_F(ClientImplTest,ReceiveFileDescriptor)373 TEST_F(ClientImplTest, ReceiveFileDescriptor) {
374   auto* host_svc = host_->AddFakeService("FakeSvc");
375   auto* host_method = host_svc->AddFakeMethod("FakeMethod1");
376 
377   // Create and bind |proxy| to the fake host.
378   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
379   cli_->BindService(proxy->GetWeakPtr());
380   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
381   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
382   task_runner_->RunUntilCheckpoint("on_connect");
383 
384   base::TempFile tx_file = base::TempFile::CreateUnlinked();
385   static constexpr char kFileContent[] = "shared file";
386   ASSERT_EQ(static_cast<size_t>(base::WriteAll(tx_file.fd(), kFileContent,
387                                                sizeof(kFileContent))),
388             sizeof(kFileContent));
389   host_->next_reply_fd_ = tx_file.fd();
390 
391   EXPECT_CALL(*host_method, OnInvoke(_, _))
392       .WillOnce(Invoke(
393           [](const Frame::InvokeMethod&, Frame::InvokeMethodReply* reply) {
394             RequestProto req_args;
395             reply->set_reply_proto(ReplyProto().SerializeAsString());
396             reply->set_success(true);
397           }));
398 
399   RequestProto req;
400   auto on_reply = task_runner_->CreateCheckpoint("on_reply");
401   Deferred<ProtoMessage> deferred_reply(
402       [on_reply](AsyncResult<ProtoMessage> reply) {
403         EXPECT_TRUE(reply.success());
404         on_reply();
405       });
406   proxy->BeginInvoke("FakeMethod1", req, std::move(deferred_reply));
407   task_runner_->RunUntilCheckpoint("on_reply");
408 
409   tx_file.ReleaseFD();
410   base::ScopedFile rx_fd = cli_->TakeReceivedFD();
411   ASSERT_TRUE(rx_fd);
412   char buf[sizeof(kFileContent)] = {};
413   ASSERT_EQ(0, lseek(*rx_fd, 0, SEEK_SET));
414   ASSERT_EQ(static_cast<long>(sizeof(buf)),
415             PERFETTO_EINTR(read(*rx_fd, buf, sizeof(buf))));
416   ASSERT_STREQ(kFileContent, buf);
417 }
418 
TEST_F(ClientImplTest,SendFileDescriptor)419 TEST_F(ClientImplTest, SendFileDescriptor) {
420   auto* host_svc = host_->AddFakeService("FakeSvc");
421   auto* host_method = host_svc->AddFakeMethod("FakeMethod1");
422 
423   // Create and bind |proxy| to the fake host.
424   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
425   cli_->BindService(proxy->GetWeakPtr());
426   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
427   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
428   task_runner_->RunUntilCheckpoint("on_connect");
429 
430   base::TempFile tx_file = base::TempFile::CreateUnlinked();
431   static constexpr char kFileContent[] = "shared file";
432   ASSERT_EQ(static_cast<size_t>(base::WriteAll(tx_file.fd(), kFileContent,
433                                                sizeof(kFileContent))),
434             sizeof(kFileContent));
435   EXPECT_CALL(*host_method, OnInvoke(_, _))
436       .WillOnce(Invoke(
437           [](const Frame::InvokeMethod&, Frame::InvokeMethodReply* reply) {
438             RequestProto req_args;
439             reply->set_reply_proto(ReplyProto().SerializeAsString());
440             reply->set_success(true);
441           }));
442 
443   RequestProto req;
444   auto on_reply = task_runner_->CreateCheckpoint("on_reply");
445   Deferred<ProtoMessage> deferred_reply(
446       [on_reply](AsyncResult<ProtoMessage> reply) {
447         EXPECT_TRUE(reply.success());
448         on_reply();
449       });
450   proxy->BeginInvoke("FakeMethod1", req, std::move(deferred_reply),
451                      tx_file.fd());
452   task_runner_->RunUntilCheckpoint("on_reply");
453 
454   base::ScopedFile rx_fd = std::move(host_->received_fd_);
455   ASSERT_TRUE(rx_fd);
456   char buf[sizeof(kFileContent)] = {};
457   ASSERT_EQ(0, lseek(*rx_fd, 0, SEEK_SET));
458   ASSERT_EQ(static_cast<long>(sizeof(buf)),
459             PERFETTO_EINTR(read(*rx_fd, buf, sizeof(buf))));
460   ASSERT_STREQ(kFileContent, buf);
461 }
462 #endif  // !OS_WIN
463 
TEST_F(ClientImplTest,BindSameServiceMultipleTimesShouldFail)464 TEST_F(ClientImplTest, BindSameServiceMultipleTimesShouldFail) {
465   host_->AddFakeService("FakeSvc");
466 
467   std::unique_ptr<FakeProxy> proxy[3];
468   for (size_t i = 0; i < base::ArraySize(proxy); i++)
469     proxy[i].reset(new FakeProxy("FakeSvc", &proxy_events_));
470 
471   // Bind to the host.
472   for (size_t i = 0; i < base::ArraySize(proxy); i++) {
473     auto checkpoint_name = "on_connect_or_disconnect" + std::to_string(i);
474     auto closure = task_runner_->CreateCheckpoint(checkpoint_name);
475     if (i == 0) {
476       // Only the first call should succeed.
477       EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(closure));
478     } else {
479       EXPECT_CALL(proxy_events_, OnDisconnect()).WillOnce(Invoke(closure));
480     }
481     cli_->BindService(proxy[i]->GetWeakPtr());
482     task_runner_->RunUntilCheckpoint(checkpoint_name);
483   }
484 }
485 
TEST_F(ClientImplTest,BindRequestsAreQueuedIfNotConnected)486 TEST_F(ClientImplTest, BindRequestsAreQueuedIfNotConnected) {
487   host_->AddFakeService("FakeSvc1");
488   host_->AddFakeService("FakeSvc2");
489 
490   std::unique_ptr<FakeProxy> proxy1(new FakeProxy("FakeSvc1", &proxy_events_));
491   std::unique_ptr<FakeProxy> proxy2(new FakeProxy("FakeSvc2", &proxy_events_));
492 
493   // Bind the services (in opposite order of creation) before running any task.
494   cli_->BindService(proxy2->GetWeakPtr());
495   cli_->BindService(proxy1->GetWeakPtr());
496 
497   InSequence seq;
498   auto on_connect1 = task_runner_->CreateCheckpoint("on_connect1");
499   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect1));
500 
501   auto on_connect2 = task_runner_->CreateCheckpoint("on_connect2");
502   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect2));
503 
504   task_runner_->RunUntilCheckpoint("on_connect1");
505   task_runner_->RunUntilCheckpoint("on_connect2");
506 }
507 
508 // The deferred callbacks for both binding a service and invoking a method
509 // should be dropped if the ServiceProxy object is destroyed prematurely.
TEST_F(ClientImplTest,DropCallbacksIfServiceProxyIsDestroyed)510 TEST_F(ClientImplTest, DropCallbacksIfServiceProxyIsDestroyed) {
511   auto* host_svc = host_->AddFakeService("FakeSvc");
512   auto* host_method = host_svc->AddFakeMethod("FakeMethod1");
513 
514   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
515 
516   // First bind the service but destroy it before ClientImpl manages to run any
517   // tasks. No OnConnect() should be called.
518   cli_->BindService(proxy->GetWeakPtr());
519   proxy.reset();
520   task_runner_->RunUntilIdle();
521   ASSERT_TRUE(Mock::VerifyAndClearExpectations(&proxy_events_));
522 
523   // Now bind it successfully, invoke a method but destroy the proxy before
524   // the method reply is dispatched. The DeferredReply should be rejected,
525   // despite the fact that the host gave a successful reply.
526   proxy.reset(new FakeProxy("FakeSvc", &proxy_events_));
527   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
528   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
529   cli_->BindService(proxy->GetWeakPtr());
530   task_runner_->RunUntilCheckpoint("on_connect");
531 
532   RequestProto req;
533   auto on_reply_sent = task_runner_->CreateCheckpoint("on_reply_sent");
534   EXPECT_CALL(*host_method, OnInvoke(_, _))
535       .WillOnce(Invoke([on_reply_sent](const Frame::InvokeMethod&,
536                                        Frame::InvokeMethodReply* reply) {
537         ReplyProto reply_args;
538         reply->set_success(true);
539         on_reply_sent();
540       }));
541 
542   auto on_reject = task_runner_->CreateCheckpoint("on_reject");
543   Deferred<ProtoMessage> deferred_reply(
544       [on_reject](AsyncResult<ProtoMessage> res) {
545         ASSERT_FALSE(res.success());
546         on_reject();
547       });
548   proxy->BeginInvoke("FakeMethod1", req, std::move(deferred_reply));
549   proxy.reset();
550   task_runner_->RunUntilCheckpoint("on_reject");
551   task_runner_->RunUntilCheckpoint("on_reply_sent");
552 }
553 
554 // If the Client object is destroyed before the ServiceProxy, the ServiceProxy
555 // should see a Disconnect() call and any pending callback should be rejected.
TEST_F(ClientImplTest,ClientDestroyedBeforeProxy)556 TEST_F(ClientImplTest, ClientDestroyedBeforeProxy) {
557   auto* host_svc = host_->AddFakeService("FakeSvc");
558   host_svc->AddFakeMethod("FakeMethod1");
559 
560   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
561   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
562   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
563   cli_->BindService(proxy->GetWeakPtr());
564   task_runner_->RunUntilCheckpoint("on_connect");
565 
566   auto on_reject = task_runner_->CreateCheckpoint("on_reject");
567   DeferredBase deferred_reply([on_reject](AsyncResult<ProtoMessage> res) {
568     ASSERT_FALSE(res.success());
569     on_reject();
570   });
571   RequestProto req;
572   proxy->BeginInvoke("FakeMethod1", req, std::move(deferred_reply));
573   EXPECT_CALL(proxy_events_, OnDisconnect());
574   cli_.reset();
575   host_.reset();  // Prevent spurious OnInvoke callbacks on the fake host.
576   task_runner_->RunUntilCheckpoint("on_reject");
577 }
578 
579 // Test that OnDisconnect() is invoked if the host is not reachable.
TEST_F(ClientImplTest,HostNotReachable)580 TEST_F(ClientImplTest, HostNotReachable) {
581   host_.reset();
582 
583   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
584 
585   auto on_disconnect = task_runner_->CreateCheckpoint("on_disconnect");
586   EXPECT_CALL(proxy_events_, OnDisconnect()).WillOnce(Invoke(on_disconnect));
587   cli_->BindService(proxy->GetWeakPtr());
588   task_runner_->RunUntilCheckpoint("on_disconnect");
589 }
590 
591 // Test that OnDisconnect() is invoked if the host shuts down prematurely.
TEST_F(ClientImplTest,HostDisconnection)592 TEST_F(ClientImplTest, HostDisconnection) {
593   host_->AddFakeService("FakeSvc");
594 
595   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
596 
597   // Bind |proxy| to the fake host.
598   cli_->BindService(proxy->GetWeakPtr());
599   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
600   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
601   task_runner_->RunUntilCheckpoint("on_connect");
602 
603   auto on_disconnect = task_runner_->CreateCheckpoint("on_disconnect");
604   EXPECT_CALL(proxy_events_, OnDisconnect()).WillOnce(Invoke(on_disconnect));
605   host_.reset();
606   task_runner_->RunUntilCheckpoint("on_disconnect");
607 }
608 
609 // Test that OnDisconnect() is invoked if the host shuts down prematurely.
TEST_F(ClientImplTest,HostDisconnectionBeforeBindReply)610 TEST_F(ClientImplTest, HostDisconnectionBeforeBindReply) {
611   host_->AddFakeService("FakeSvc");
612 
613   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
614 
615   // Prevent the host from replying
616   host_->suppress_replies_ = true;
617 
618   // Bind |proxy| to the fake host. The host will receive the request, but it
619   // will not reply.
620   cli_->BindService(proxy->GetWeakPtr());
621   task_runner_->RunUntilIdle();
622   auto on_disconnect = task_runner_->CreateCheckpoint("on_disconnect");
623   EXPECT_CALL(proxy_events_, OnDisconnect()).WillOnce(Invoke(on_disconnect));
624   // Shutdown the host before it's able to send the Bind reply. The ClientImpl
625   // should receive an OnDisconnect() callback and propagate that to the service
626   // proxy.
627   host_.reset();
628   task_runner_->RunUntilCheckpoint("on_disconnect");
629 }
630 
631 // Disabled on Fuchsia because Fuchsia kernel sockets are non-addressable
632 // so there is no connect() step which may fail.
633 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
TEST_F(ClientImplTest,HostConnectionFailure)634 TEST_F(ClientImplTest, HostConnectionFailure) {
635   ipc::TestSocket kNonexistentSock{"client_impl_unittest_nonexistent"};
636   std::unique_ptr<Client> client = Client::CreateInstance(
637       {kNonexistentSock.name(), /*retry=*/false}, task_runner_.get());
638 
639   // Connect a client to a non-existent socket, which will always fail. The
640   // client will notify the proxy of disconnection.
641   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
642   client->BindService(proxy->GetWeakPtr());
643 
644   // Make sure the client copes with being deleted by the disconnection
645   // callback.
646   auto on_disconnect_reached = task_runner_->CreateCheckpoint("on_disconnect");
647   auto on_disconnect = [&] {
648     client.reset();
649     on_disconnect_reached();
650   };
651   EXPECT_CALL(proxy_events_, OnDisconnect()).WillOnce(Invoke(on_disconnect));
652   task_runner_->RunUntilCheckpoint("on_disconnect");
653 }
654 #endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
655 
656 // TODO(primiano): add the tests below.
657 // TEST(ClientImplTest, UnparsableReply) {}
658 
659 }  // namespace
660 }  // namespace ipc
661 }  // namespace perfetto
662