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