• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_METHOD0(OnConnect, void());
78   MOCK_METHOD0(OnDisconnect, void());
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_METHOD2(OnInvoke,
88                  void(const Frame::InvokeMethod&, Frame::InvokeMethodReply*));
89   };  // FakeMethod.
90 
91   struct FakeService {
AddFakeMethodperfetto::ipc::__anon2c0b15d50111::FakeHost::FakeService92     FakeMethod* AddFakeMethod(const std::string& name) {
93       auto it_and_inserted =
94           methods.emplace(name, std::unique_ptr<FakeMethod>(new FakeMethod()));
95       EXPECT_TRUE(it_and_inserted.second);
96       FakeMethod* method = it_and_inserted.first->second.get();
97       method->id = ++last_method_id;
98       return method;
99     }
100 
101     ServiceID id;
102     std::map<std::string, std::unique_ptr<FakeMethod>> methods;
103     MethodID last_method_id = 0;
104   };  // FakeService.
105 
FakeHost(base::TaskRunner * task_runner)106   explicit FakeHost(base::TaskRunner* task_runner) {
107     kTestSocket.Destroy();
108     listening_sock =
109         base::UnixSocket::Listen(kTestSocket.name(), this, task_runner,
110                                  kTestSocket.family(), base::SockType::kStream);
111     EXPECT_TRUE(listening_sock->is_listening());
112   }
~FakeHost()113   ~FakeHost() override { kTestSocket.Destroy(); }
114 
AddFakeService(const std::string & name)115   FakeService* AddFakeService(const std::string& name) {
116     auto it_and_inserted =
117         services.emplace(name, std::unique_ptr<FakeService>(new FakeService()));
118     EXPECT_TRUE(it_and_inserted.second);
119     FakeService* svc = it_and_inserted.first->second.get();
120     svc->id = ++last_service_id;
121     return svc;
122   }
123 
124   // base::UnixSocket::EventListener implementation.
OnNewIncomingConnection(base::UnixSocket *,std::unique_ptr<base::UnixSocket> new_connection)125   void OnNewIncomingConnection(
126       base::UnixSocket*,
127       std::unique_ptr<base::UnixSocket> new_connection) override {
128     ASSERT_FALSE(client_sock);
129     client_sock = std::move(new_connection);
130   }
131 
OnDataAvailable(base::UnixSocket * sock)132   void OnDataAvailable(base::UnixSocket* sock) override {
133     if (sock != client_sock.get())
134       return;
135     auto buf = frame_deserializer.BeginReceive();
136     base::ScopedFile fd;
137     size_t rsize = client_sock->Receive(buf.data, buf.size, &fd);
138     if (fd)
139       received_fd_ = std::move(fd);
140     EXPECT_TRUE(frame_deserializer.EndReceive(rsize));
141     while (std::unique_ptr<Frame> frame = frame_deserializer.PopNextFrame())
142       OnFrameReceived(*frame);
143   }
144 
OnFrameReceived(const Frame & req)145   void OnFrameReceived(const Frame& req) {
146     if (req.has_msg_bind_service()) {
147       auto svc_it = services.find(req.msg_bind_service().service_name());
148       ASSERT_NE(services.end(), svc_it);
149       const FakeService& svc = *svc_it->second;
150       Frame reply;
151       reply.set_request_id(req.request_id());
152       reply.mutable_msg_bind_service_reply()->set_success(true);
153       reply.mutable_msg_bind_service_reply()->set_service_id(svc.id);
154       for (const auto& method_it : svc.methods) {
155         auto* method = reply.mutable_msg_bind_service_reply()->add_methods();
156         method->set_name(method_it.first);
157         method->set_id(method_it.second->id);
158       }
159       Reply(reply);
160     } else if (req.has_msg_invoke_method()) {
161       // Lookup the service and method.
162       bool has_more = false;
163       do {
164         Frame reply;
165         reply.set_request_id(req.request_id());
166         for (const auto& svc : services) {
167           if (svc.second->id != req.msg_invoke_method().service_id())
168             continue;
169           for (const auto& method : svc.second->methods) {
170             if (method.second->id != req.msg_invoke_method().method_id())
171               continue;
172             method.second->OnInvoke(req.msg_invoke_method(),
173                                     reply.mutable_msg_invoke_method_reply());
174             has_more = reply.mutable_msg_invoke_method_reply()->has_more();
175           }
176         }
177         // If either the method or the service are not found, |success| will be
178         // false by default.
179         Reply(reply);
180       } while (has_more);
181     } else {
182       FAIL() << "Unknown request";
183     }
184   }
185 
Reply(const Frame & frame)186   void Reply(const Frame& frame) {
187     auto buf = BufferedFrameDeserializer::Serialize(frame);
188     ASSERT_TRUE(client_sock->is_connected());
189     EXPECT_TRUE(client_sock->Send(buf.data(), buf.size(), next_reply_fd));
190     next_reply_fd = -1;
191   }
192 
193   BufferedFrameDeserializer frame_deserializer;
194   std::unique_ptr<base::UnixSocket> listening_sock;
195   std::unique_ptr<base::UnixSocket> client_sock;
196   std::map<std::string, std::unique_ptr<FakeService>> services;
197   ServiceID last_service_id = 0;
198   int next_reply_fd = -1;
199   base::ScopedFile received_fd_;
200 };  // FakeHost.
201 
202 class ClientImplTest : public ::testing::Test {
203  public:
SetUp()204   void SetUp() override {
205     task_runner_.reset(new base::TestTaskRunner());
206     host_.reset(new FakeHost(task_runner_.get()));
207     cli_ = Client::CreateInstance({kTestSocket.name(), /*retry=*/false},
208                                   task_runner_.get());
209   }
210 
TearDown()211   void TearDown() override {
212     cli_.reset();
213     host_.reset();
214     task_runner_->RunUntilIdle();
215     task_runner_.reset();
216   }
217 
218   ::testing::StrictMock<MockEventListener> proxy_events_;
219   std::unique_ptr<base::TestTaskRunner> task_runner_;
220   std::unique_ptr<FakeHost> host_;
221   std::unique_ptr<Client> cli_;
222 };
223 
TEST_F(ClientImplTest,BindAndInvokeMethod)224 TEST_F(ClientImplTest, BindAndInvokeMethod) {
225   auto* host_svc = host_->AddFakeService("FakeSvc");
226   auto* host_method = host_svc->AddFakeMethod("FakeMethod1");
227 
228   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
229 
230   // Bind |proxy| to the fake host.
231   cli_->BindService(proxy->GetWeakPtr());
232   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
233   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
234   task_runner_->RunUntilCheckpoint("on_connect");
235 
236   // Invoke a valid method.
237   EXPECT_CALL(*host_method, OnInvoke(_, _))
238       .WillOnce(Invoke(
239           [](const Frame::InvokeMethod& req, Frame::InvokeMethodReply* reply) {
240             RequestProto req_args;
241             EXPECT_TRUE(req_args.ParseFromString(req.args_proto()));
242             EXPECT_EQ("req_data", req_args.data());
243             ReplyProto reply_args;
244             reply->set_reply_proto(reply_args.SerializeAsString());
245             reply->set_success(true);
246           }));
247 
248   RequestProto req;
249   req.set_data("req_data");
250   auto on_invoke_reply = task_runner_->CreateCheckpoint("on_invoke_reply");
251   Deferred<ProtoMessage> deferred_reply(
252       [on_invoke_reply](AsyncResult<ProtoMessage> reply) {
253         EXPECT_TRUE(reply.success());
254         on_invoke_reply();
255       });
256   proxy->BeginInvoke("FakeMethod1", req, std::move(deferred_reply));
257   task_runner_->RunUntilCheckpoint("on_invoke_reply");
258 
259   // Invoke an invalid method.
260   auto on_invalid_invoke = task_runner_->CreateCheckpoint("on_invalid_invoke");
261   Deferred<ProtoMessage> deferred_reply2(
262       [on_invalid_invoke](AsyncResult<ProtoMessage> reply) {
263         EXPECT_FALSE(reply.success());
264         on_invalid_invoke();
265       });
266   RequestProto empty_req;
267   proxy->BeginInvoke("InvalidMethod", empty_req, std::move(deferred_reply2));
268   task_runner_->RunUntilCheckpoint("on_invalid_invoke");
269 }
270 
271 // Tests that when invoking a method without binding a callback, the resulting
272 // request has the |drop_reply| flag set.
TEST_F(ClientImplTest,InvokeMethodDropReply)273 TEST_F(ClientImplTest, InvokeMethodDropReply) {
274   auto* host_svc = host_->AddFakeService("FakeSvc");
275   auto* host_method = host_svc->AddFakeMethod("FakeMethod1");
276 
277   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
278 
279   // Bind |proxy| to the fake host.
280   cli_->BindService(proxy->GetWeakPtr());
281   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
282   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
283   task_runner_->RunUntilCheckpoint("on_connect");
284 
285   auto on_req_received = task_runner_->CreateCheckpoint("on_req_received");
286   EXPECT_CALL(*host_method, OnInvoke(_, _))
287       .WillOnce(Invoke([on_req_received](const Frame::InvokeMethod& req,
288                                          Frame::InvokeMethodReply*) {
289         RequestProto req_args;
290         EXPECT_TRUE(req.drop_reply());
291         on_req_received();
292       }));
293 
294   // Invoke a method without binding any callback to the Deferred object.
295   Deferred<ProtoMessage> no_callback;
296   proxy->BeginInvoke("FakeMethod1", RequestProto(), std::move(no_callback));
297   task_runner_->RunUntilCheckpoint("on_req_received");
298 }
299 
300 // Like BindAndInvokeMethod, but this time invoke a streaming method that
301 // provides > 1 reply per invocation.
TEST_F(ClientImplTest,BindAndInvokeStreamingMethod)302 TEST_F(ClientImplTest, BindAndInvokeStreamingMethod) {
303   auto* host_svc = host_->AddFakeService("FakeSvc");
304   auto* host_method = host_svc->AddFakeMethod("FakeMethod1");
305   const int kNumReplies = 3;
306 
307   // Create and bind |proxy| to the fake host.
308   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
309   cli_->BindService(proxy->GetWeakPtr());
310   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
311   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
312   task_runner_->RunUntilCheckpoint("on_connect");
313 
314   // Invoke a valid method, reply kNumReplies times.
315   int replies_left = kNumReplies;
316   EXPECT_CALL(*host_method, OnInvoke(_, _))
317       .Times(kNumReplies)
318       .WillRepeatedly(Invoke([&replies_left](const Frame::InvokeMethod& req,
319                                              Frame::InvokeMethodReply* reply) {
320         RequestProto req_args;
321         EXPECT_TRUE(req_args.ParseFromString(req.args_proto()));
322         reply->set_reply_proto(ReplyProto().SerializeAsString());
323         reply->set_success(true);
324         reply->set_has_more(--replies_left > 0);
325       }));
326 
327   RequestProto req;
328   req.set_data("req_data");
329   auto on_last_reply = task_runner_->CreateCheckpoint("on_last_reply");
330   int replies_seen = 0;
331   Deferred<ProtoMessage> deferred_reply(
332       [on_last_reply, &replies_seen](AsyncResult<ProtoMessage> reply) {
333         EXPECT_TRUE(reply.success());
334         replies_seen++;
335         if (!reply.has_more())
336           on_last_reply();
337       });
338   proxy->BeginInvoke("FakeMethod1", req, std::move(deferred_reply));
339   task_runner_->RunUntilCheckpoint("on_last_reply");
340   ASSERT_EQ(kNumReplies, replies_seen);
341 }
342 
343 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
344 // File descriptor sending over IPC is not supported on Windows.
TEST_F(ClientImplTest,ReceiveFileDescriptor)345 TEST_F(ClientImplTest, ReceiveFileDescriptor) {
346   auto* host_svc = host_->AddFakeService("FakeSvc");
347   auto* host_method = host_svc->AddFakeMethod("FakeMethod1");
348 
349   // Create and bind |proxy| to the fake host.
350   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
351   cli_->BindService(proxy->GetWeakPtr());
352   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
353   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
354   task_runner_->RunUntilCheckpoint("on_connect");
355 
356   base::TempFile tx_file = base::TempFile::CreateUnlinked();
357   static constexpr char kFileContent[] = "shared file";
358   ASSERT_EQ(static_cast<size_t>(base::WriteAll(tx_file.fd(), kFileContent,
359                                                sizeof(kFileContent))),
360             sizeof(kFileContent));
361   host_->next_reply_fd = tx_file.fd();
362 
363   EXPECT_CALL(*host_method, OnInvoke(_, _))
364       .WillOnce(Invoke(
365           [](const Frame::InvokeMethod&, Frame::InvokeMethodReply* reply) {
366             RequestProto req_args;
367             reply->set_reply_proto(ReplyProto().SerializeAsString());
368             reply->set_success(true);
369           }));
370 
371   RequestProto req;
372   auto on_reply = task_runner_->CreateCheckpoint("on_reply");
373   Deferred<ProtoMessage> deferred_reply(
374       [on_reply](AsyncResult<ProtoMessage> reply) {
375         EXPECT_TRUE(reply.success());
376         on_reply();
377       });
378   proxy->BeginInvoke("FakeMethod1", req, std::move(deferred_reply));
379   task_runner_->RunUntilCheckpoint("on_reply");
380 
381   tx_file.ReleaseFD();
382   base::ScopedFile rx_fd = cli_->TakeReceivedFD();
383   ASSERT_TRUE(rx_fd);
384   char buf[sizeof(kFileContent)] = {};
385   ASSERT_EQ(0, lseek(*rx_fd, 0, SEEK_SET));
386   ASSERT_EQ(static_cast<long>(sizeof(buf)),
387             PERFETTO_EINTR(read(*rx_fd, buf, sizeof(buf))));
388   ASSERT_STREQ(kFileContent, buf);
389 }
390 
TEST_F(ClientImplTest,SendFileDescriptor)391 TEST_F(ClientImplTest, SendFileDescriptor) {
392   auto* host_svc = host_->AddFakeService("FakeSvc");
393   auto* host_method = host_svc->AddFakeMethod("FakeMethod1");
394 
395   // Create and bind |proxy| to the fake host.
396   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
397   cli_->BindService(proxy->GetWeakPtr());
398   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
399   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
400   task_runner_->RunUntilCheckpoint("on_connect");
401 
402   base::TempFile tx_file = base::TempFile::CreateUnlinked();
403   static constexpr char kFileContent[] = "shared file";
404   ASSERT_EQ(static_cast<size_t>(base::WriteAll(tx_file.fd(), kFileContent,
405                                                sizeof(kFileContent))),
406             sizeof(kFileContent));
407   EXPECT_CALL(*host_method, OnInvoke(_, _))
408       .WillOnce(Invoke(
409           [](const Frame::InvokeMethod&, Frame::InvokeMethodReply* reply) {
410             RequestProto req_args;
411             reply->set_reply_proto(ReplyProto().SerializeAsString());
412             reply->set_success(true);
413           }));
414 
415   RequestProto req;
416   auto on_reply = task_runner_->CreateCheckpoint("on_reply");
417   Deferred<ProtoMessage> deferred_reply(
418       [on_reply](AsyncResult<ProtoMessage> reply) {
419         EXPECT_TRUE(reply.success());
420         on_reply();
421       });
422   proxy->BeginInvoke("FakeMethod1", req, std::move(deferred_reply),
423                      tx_file.fd());
424   task_runner_->RunUntilCheckpoint("on_reply");
425 
426   base::ScopedFile rx_fd = std::move(host_->received_fd_);
427   ASSERT_TRUE(rx_fd);
428   char buf[sizeof(kFileContent)] = {};
429   ASSERT_EQ(0, lseek(*rx_fd, 0, SEEK_SET));
430   ASSERT_EQ(static_cast<long>(sizeof(buf)),
431             PERFETTO_EINTR(read(*rx_fd, buf, sizeof(buf))));
432   ASSERT_STREQ(kFileContent, buf);
433 }
434 #endif  // !OS_WIN
435 
TEST_F(ClientImplTest,BindSameServiceMultipleTimesShouldFail)436 TEST_F(ClientImplTest, BindSameServiceMultipleTimesShouldFail) {
437   host_->AddFakeService("FakeSvc");
438 
439   std::unique_ptr<FakeProxy> proxy[3];
440   for (size_t i = 0; i < base::ArraySize(proxy); i++)
441     proxy[i].reset(new FakeProxy("FakeSvc", &proxy_events_));
442 
443   // Bind to the host.
444   for (size_t i = 0; i < base::ArraySize(proxy); i++) {
445     auto checkpoint_name = "on_connect_or_disconnect" + std::to_string(i);
446     auto closure = task_runner_->CreateCheckpoint(checkpoint_name);
447     if (i == 0) {
448       // Only the first call should succeed.
449       EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(closure));
450     } else {
451       EXPECT_CALL(proxy_events_, OnDisconnect()).WillOnce(Invoke(closure));
452     }
453     cli_->BindService(proxy[i]->GetWeakPtr());
454     task_runner_->RunUntilCheckpoint(checkpoint_name);
455   }
456 }
457 
TEST_F(ClientImplTest,BindRequestsAreQueuedIfNotConnected)458 TEST_F(ClientImplTest, BindRequestsAreQueuedIfNotConnected) {
459   host_->AddFakeService("FakeSvc1");
460   host_->AddFakeService("FakeSvc2");
461 
462   std::unique_ptr<FakeProxy> proxy1(new FakeProxy("FakeSvc1", &proxy_events_));
463   std::unique_ptr<FakeProxy> proxy2(new FakeProxy("FakeSvc2", &proxy_events_));
464 
465   // Bind the services (in opposite order of creation) before running any task.
466   cli_->BindService(proxy2->GetWeakPtr());
467   cli_->BindService(proxy1->GetWeakPtr());
468 
469   InSequence seq;
470   auto on_connect1 = task_runner_->CreateCheckpoint("on_connect1");
471   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect1));
472 
473   auto on_connect2 = task_runner_->CreateCheckpoint("on_connect2");
474   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect2));
475 
476   task_runner_->RunUntilCheckpoint("on_connect1");
477   task_runner_->RunUntilCheckpoint("on_connect2");
478 }
479 
480 // The deferred callbacks for both binding a service and invoking a method
481 // should be dropped if the ServiceProxy object is destroyed prematurely.
TEST_F(ClientImplTest,DropCallbacksIfServiceProxyIsDestroyed)482 TEST_F(ClientImplTest, DropCallbacksIfServiceProxyIsDestroyed) {
483   auto* host_svc = host_->AddFakeService("FakeSvc");
484   auto* host_method = host_svc->AddFakeMethod("FakeMethod1");
485 
486   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
487 
488   // First bind the service but destroy it before ClientImpl manages to run any
489   // tasks. No OnConnect() should be called.
490   cli_->BindService(proxy->GetWeakPtr());
491   proxy.reset();
492   task_runner_->RunUntilIdle();
493   ASSERT_TRUE(Mock::VerifyAndClearExpectations(&proxy_events_));
494 
495   // Now bind it successfully, invoke a method but destroy the proxy before
496   // the method reply is dispatched. The DeferredReply should be rejected,
497   // despite the fact that the host gave a successful reply.
498   proxy.reset(new FakeProxy("FakeSvc", &proxy_events_));
499   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
500   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
501   cli_->BindService(proxy->GetWeakPtr());
502   task_runner_->RunUntilCheckpoint("on_connect");
503 
504   RequestProto req;
505   auto on_reply_sent = task_runner_->CreateCheckpoint("on_reply_sent");
506   EXPECT_CALL(*host_method, OnInvoke(_, _))
507       .WillOnce(Invoke([on_reply_sent](const Frame::InvokeMethod&,
508                                        Frame::InvokeMethodReply* reply) {
509         ReplyProto reply_args;
510         reply->set_success(true);
511         on_reply_sent();
512       }));
513 
514   auto on_reject = task_runner_->CreateCheckpoint("on_reject");
515   Deferred<ProtoMessage> deferred_reply(
516       [on_reject](AsyncResult<ProtoMessage> res) {
517         ASSERT_FALSE(res.success());
518         on_reject();
519       });
520   proxy->BeginInvoke("FakeMethod1", req, std::move(deferred_reply));
521   proxy.reset();
522   task_runner_->RunUntilCheckpoint("on_reject");
523   task_runner_->RunUntilCheckpoint("on_reply_sent");
524 }
525 
526 // If the Client object is destroyed before the ServiceProxy, the ServiceProxy
527 // should see a Disconnect() call and any pending callback should be rejected.
TEST_F(ClientImplTest,ClientDestroyedBeforeProxy)528 TEST_F(ClientImplTest, ClientDestroyedBeforeProxy) {
529   auto* host_svc = host_->AddFakeService("FakeSvc");
530   host_svc->AddFakeMethod("FakeMethod1");
531 
532   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
533   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
534   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
535   cli_->BindService(proxy->GetWeakPtr());
536   task_runner_->RunUntilCheckpoint("on_connect");
537 
538   auto on_reject = task_runner_->CreateCheckpoint("on_reject");
539   DeferredBase deferred_reply([on_reject](AsyncResult<ProtoMessage> res) {
540     ASSERT_FALSE(res.success());
541     on_reject();
542   });
543   RequestProto req;
544   proxy->BeginInvoke("FakeMethod1", req, std::move(deferred_reply));
545   EXPECT_CALL(proxy_events_, OnDisconnect());
546   cli_.reset();
547   host_.reset();  // Prevent spurious OnInvoke callbacks on the fake host.
548   task_runner_->RunUntilCheckpoint("on_reject");
549 }
550 
551 // Test that OnDisconnect() is invoked if the host is not reachable.
TEST_F(ClientImplTest,HostNotReachable)552 TEST_F(ClientImplTest, HostNotReachable) {
553   host_.reset();
554 
555   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
556 
557   auto on_disconnect = task_runner_->CreateCheckpoint("on_disconnect");
558   EXPECT_CALL(proxy_events_, OnDisconnect()).WillOnce(Invoke(on_disconnect));
559   cli_->BindService(proxy->GetWeakPtr());
560   task_runner_->RunUntilCheckpoint("on_disconnect");
561 }
562 
563 // Test that OnDisconnect() is invoked if the host shuts down prematurely.
TEST_F(ClientImplTest,HostDisconnection)564 TEST_F(ClientImplTest, HostDisconnection) {
565   host_->AddFakeService("FakeSvc");
566 
567   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
568 
569   // Bind |proxy| to the fake host.
570   cli_->BindService(proxy->GetWeakPtr());
571   auto on_connect = task_runner_->CreateCheckpoint("on_connect");
572   EXPECT_CALL(proxy_events_, OnConnect()).WillOnce(Invoke(on_connect));
573   task_runner_->RunUntilCheckpoint("on_connect");
574 
575   auto on_disconnect = task_runner_->CreateCheckpoint("on_disconnect");
576   EXPECT_CALL(proxy_events_, OnDisconnect()).WillOnce(Invoke(on_disconnect));
577   host_.reset();
578   task_runner_->RunUntilCheckpoint("on_disconnect");
579 }
580 
TEST_F(ClientImplTest,HostConnectionFailure)581 TEST_F(ClientImplTest, HostConnectionFailure) {
582   ipc::TestSocket kNonexistentSock{"client_impl_unittest_nonexistent"};
583   std::unique_ptr<Client> client = Client::CreateInstance(
584       {kNonexistentSock.name(), /*retry=*/false}, task_runner_.get());
585 
586   // Connect a client to a non-existent socket, which will always fail. The
587   // client will notify the proxy of disconnection.
588   std::unique_ptr<FakeProxy> proxy(new FakeProxy("FakeSvc", &proxy_events_));
589   client->BindService(proxy->GetWeakPtr());
590 
591   // Make sure the client copes with being deleted by the disconnection
592   // callback.
593   auto on_disconnect_reached = task_runner_->CreateCheckpoint("on_disconnect");
594   auto on_disconnect = [&] {
595     client.reset();
596     on_disconnect_reached();
597   };
598   EXPECT_CALL(proxy_events_, OnDisconnect()).WillOnce(Invoke(on_disconnect));
599   task_runner_->RunUntilCheckpoint("on_disconnect");
600 }
601 
602 // TODO(primiano): add the tests below.
603 // TEST(ClientImplTest, UnparsableReply) {}
604 
605 }  // namespace
606 }  // namespace ipc
607 }  // namespace perfetto
608