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/host_impl.h"
18
19 #include <memory>
20
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include "perfetto/base/file_utils.h"
24 #include "perfetto/base/scoped_file.h"
25 #include "perfetto/base/temp_file.h"
26 #include "perfetto/base/unix_socket.h"
27 #include "perfetto/base/utils.h"
28 #include "perfetto/ipc/service.h"
29 #include "perfetto/ipc/service_descriptor.h"
30 #include "src/base/test/test_task_runner.h"
31 #include "src/ipc/buffered_frame_deserializer.h"
32 #include "src/ipc/test/test_socket.h"
33
34 #include "src/ipc/test/client_unittest_messages.pb.h"
35 #include "src/ipc/wire_protocol.pb.h"
36
37 namespace perfetto {
38 namespace ipc {
39 namespace {
40
41 using ::testing::_;
42 using ::testing::Invoke;
43 using ::testing::InvokeWithoutArgs;
44 using ::testing::Return;
45
46 constexpr char kSockName[] = TEST_SOCK_NAME("host_impl_unittest.sock");
47
48 // RequestProto and ReplyProto are defined in client_unittest_messages.proto.
49
50 class FakeService : public Service {
51 public:
52 MOCK_METHOD2(OnFakeMethod1, void(const RequestProto&, DeferredBase*));
53
Invoker(Service * service,const ProtoMessage & req,DeferredBase deferred_reply)54 static void Invoker(Service* service,
55 const ProtoMessage& req,
56 DeferredBase deferred_reply) {
57 static_cast<FakeService*>(service)->OnFakeMethod1(
58 static_cast<const RequestProto&>(req), &deferred_reply);
59 }
60
RequestDecoder(const std::string & proto)61 static std::unique_ptr<ProtoMessage> RequestDecoder(
62 const std::string& proto) {
63 std::unique_ptr<ProtoMessage> reply(new RequestProto());
64 EXPECT_TRUE(reply->ParseFromString(proto));
65 return reply;
66 }
67
FakeService(const char * service_name)68 explicit FakeService(const char* service_name) {
69 descriptor_.service_name = service_name;
70 descriptor_.methods.push_back(
71 {"FakeMethod1", &RequestDecoder, nullptr, &Invoker});
72 }
73
GetDescriptor()74 const ServiceDescriptor& GetDescriptor() override { return descriptor_; }
75
TakeReceivedFD()76 base::ScopedFile TakeReceivedFD() { return ipc::Service::TakeReceivedFD(); }
77
78 base::ScopedFile received_fd_;
79 ServiceDescriptor descriptor_;
80 };
81
82 class FakeClient : public base::UnixSocket::EventListener {
83 public:
84 MOCK_METHOD0(OnConnect, void());
85 MOCK_METHOD0(OnDisconnect, void());
86 MOCK_METHOD1(OnServiceBound, void(const Frame::BindServiceReply&));
87 MOCK_METHOD1(OnInvokeMethodReply, void(const Frame::InvokeMethodReply&));
88 MOCK_METHOD1(OnFileDescriptorReceived, void(int));
89 MOCK_METHOD0(OnRequestError, void());
90
FakeClient(base::TaskRunner * task_runner)91 explicit FakeClient(base::TaskRunner* task_runner) {
92 sock_ = base::UnixSocket::Connect(kSockName, this, task_runner);
93 }
94
95 ~FakeClient() override = default;
96
BindService(const std::string & service_name)97 void BindService(const std::string& service_name) {
98 Frame frame;
99 uint64_t request_id = requests_.empty() ? 1 : requests_.rbegin()->first + 1;
100 requests_.emplace(request_id, 0);
101 frame.set_request_id(request_id);
102 frame.mutable_msg_bind_service()->set_service_name(service_name);
103 SendFrame(frame);
104 }
105
InvokeMethod(ServiceID service_id,MethodID method_id,const ProtoMessage & args,bool drop_reply=false,int fd=-1)106 void InvokeMethod(ServiceID service_id,
107 MethodID method_id,
108 const ProtoMessage& args,
109 bool drop_reply = false,
110 int fd = -1) {
111 Frame frame;
112 uint64_t request_id = requests_.empty() ? 1 : requests_.rbegin()->first + 1;
113 requests_.emplace(request_id, 0);
114 frame.set_request_id(request_id);
115 frame.mutable_msg_invoke_method()->set_service_id(service_id);
116 frame.mutable_msg_invoke_method()->set_method_id(method_id);
117 frame.mutable_msg_invoke_method()->set_drop_reply(drop_reply);
118 frame.mutable_msg_invoke_method()->set_args_proto(args.SerializeAsString());
119 SendFrame(frame, fd);
120 }
121
122 // base::UnixSocket::EventListener implementation.
OnConnect(base::UnixSocket *,bool success)123 void OnConnect(base::UnixSocket*, bool success) override {
124 ASSERT_TRUE(success);
125 OnConnect();
126 }
127
OnDisconnect(base::UnixSocket *)128 void OnDisconnect(base::UnixSocket*) override { OnDisconnect(); }
129
OnDataAvailable(base::UnixSocket * sock)130 void OnDataAvailable(base::UnixSocket* sock) override {
131 ASSERT_EQ(sock_.get(), sock);
132 auto buf = frame_deserializer_.BeginReceive();
133 base::ScopedFile fd;
134 size_t rsize = sock->Receive(buf.data, buf.size, &fd);
135 ASSERT_TRUE(frame_deserializer_.EndReceive(rsize));
136 if (fd)
137 OnFileDescriptorReceived(*fd);
138 while (std::unique_ptr<Frame> frame = frame_deserializer_.PopNextFrame()) {
139 ASSERT_EQ(1u, requests_.count(frame->request_id()));
140 EXPECT_EQ(0, requests_[frame->request_id()]++);
141 if (frame->msg_case() == Frame::kMsgBindServiceReply) {
142 if (frame->msg_bind_service_reply().success())
143 last_bound_service_id_ = frame->msg_bind_service_reply().service_id();
144 return OnServiceBound(frame->msg_bind_service_reply());
145 }
146 if (frame->msg_case() == Frame::kMsgInvokeMethodReply)
147 return OnInvokeMethodReply(frame->msg_invoke_method_reply());
148 if (frame->msg_case() == Frame::kMsgRequestError)
149 return OnRequestError();
150 FAIL() << "Unexpected frame received from host " << frame->msg_case();
151 }
152 }
153
SendFrame(const Frame & frame,int fd=-1)154 void SendFrame(const Frame& frame, int fd = -1) {
155 std::string buf = BufferedFrameDeserializer::Serialize(frame);
156 ASSERT_TRUE(sock_->Send(buf.data(), buf.size(), fd,
157 base::UnixSocket::BlockingMode::kBlocking));
158 }
159
160 BufferedFrameDeserializer frame_deserializer_;
161 std::unique_ptr<base::UnixSocket> sock_;
162 std::map<uint64_t /* request_id */, int /* num_replies_received */> requests_;
163 ServiceID last_bound_service_id_;
164 };
165
166 class HostImplTest : public ::testing::Test {
167 public:
SetUp()168 void SetUp() override {
169 DESTROY_TEST_SOCK(kSockName);
170 task_runner_.reset(new base::TestTaskRunner());
171 Host* host = Host::CreateInstance(kSockName, task_runner_.get()).release();
172 ASSERT_NE(nullptr, host);
173 host_.reset(static_cast<HostImpl*>(host));
174 cli_.reset(new FakeClient(task_runner_.get()));
175 auto on_connect = task_runner_->CreateCheckpoint("on_connect");
176 EXPECT_CALL(*cli_, OnConnect()).WillOnce(Invoke(on_connect));
177 task_runner_->RunUntilCheckpoint("on_connect");
178 }
179
TearDown()180 void TearDown() override {
181 task_runner_->RunUntilIdle();
182 cli_.reset();
183 host_.reset();
184 task_runner_->RunUntilIdle();
185 task_runner_.reset();
186 DESTROY_TEST_SOCK(kSockName);
187 }
188
189 // ::testing::StrictMock<MockEventListener> proxy_events_;
190 std::unique_ptr<base::TestTaskRunner> task_runner_;
191 std::unique_ptr<HostImpl> host_;
192 std::unique_ptr<FakeClient> cli_;
193 };
194
TEST_F(HostImplTest,BindService)195 TEST_F(HostImplTest, BindService) {
196 // First bind the service when it doesn't exists yet and check that the
197 // BindService() request fails.
198 cli_->BindService("FakeService"); // FakeService does not exist yet.
199 auto on_bind_failure = task_runner_->CreateCheckpoint("on_bind_failure");
200 EXPECT_CALL(*cli_, OnServiceBound(_))
201 .WillOnce(Invoke([on_bind_failure](const Frame::BindServiceReply& reply) {
202 ASSERT_FALSE(reply.success());
203 on_bind_failure();
204 }));
205 task_runner_->RunUntilCheckpoint("on_bind_failure");
206
207 // Now expose the service and bind it.
208 ASSERT_TRUE(host_->ExposeService(
209 std::unique_ptr<Service>(new FakeService("FakeService"))));
210 auto on_bind_success = task_runner_->CreateCheckpoint("on_bind_success");
211 cli_->BindService("FakeService");
212 EXPECT_CALL(*cli_, OnServiceBound(_))
213 .WillOnce(Invoke([on_bind_success](const Frame::BindServiceReply& reply) {
214 ASSERT_TRUE(reply.success());
215 on_bind_success();
216 }));
217 task_runner_->RunUntilCheckpoint("on_bind_success");
218 }
219
TEST_F(HostImplTest,InvokeNonExistingMethod)220 TEST_F(HostImplTest, InvokeNonExistingMethod) {
221 FakeService* fake_service = new FakeService("FakeService");
222 ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
223 auto on_bind = task_runner_->CreateCheckpoint("on_bind");
224 cli_->BindService("FakeService");
225 EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
226 task_runner_->RunUntilCheckpoint("on_bind");
227
228 auto on_invoke_failure = task_runner_->CreateCheckpoint("on_invoke_failure");
229 cli_->InvokeMethod(cli_->last_bound_service_id_, 42, RequestProto());
230 EXPECT_CALL(*cli_, OnInvokeMethodReply(_))
231 .WillOnce(
232 Invoke([on_invoke_failure](const Frame::InvokeMethodReply& reply) {
233 ASSERT_FALSE(reply.success());
234 ASSERT_FALSE(reply.has_more());
235 on_invoke_failure();
236 }));
237 task_runner_->RunUntilCheckpoint("on_invoke_failure");
238 }
239
TEST_F(HostImplTest,InvokeMethod)240 TEST_F(HostImplTest, InvokeMethod) {
241 FakeService* fake_service = new FakeService("FakeService");
242 ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
243 auto on_bind = task_runner_->CreateCheckpoint("on_bind");
244 cli_->BindService("FakeService");
245 EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
246 task_runner_->RunUntilCheckpoint("on_bind");
247
248 RequestProto req_args;
249 req_args.set_data("foo");
250 cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args);
251 auto on_reply_sent = task_runner_->CreateCheckpoint("on_reply_sent");
252 EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
253 .WillOnce(
254 Invoke([on_reply_sent](const RequestProto& req, DeferredBase* reply) {
255 ASSERT_EQ("foo", req.data());
256 std::unique_ptr<ReplyProto> reply_args(new ReplyProto());
257 reply_args->set_data("bar");
258 reply->Resolve(AsyncResult<ProtoMessage>(
259 std::unique_ptr<ProtoMessage>(reply_args.release())));
260 on_reply_sent();
261 }));
262 task_runner_->RunUntilCheckpoint("on_reply_sent");
263
264 auto on_reply_received = task_runner_->CreateCheckpoint("on_reply_received");
265 EXPECT_CALL(*cli_, OnInvokeMethodReply(_))
266 .WillOnce(
267 Invoke([on_reply_received](const Frame::InvokeMethodReply& reply) {
268 ASSERT_TRUE(reply.success());
269 ASSERT_FALSE(reply.has_more());
270 ReplyProto reply_args;
271 reply_args.ParseFromString(reply.reply_proto());
272 ASSERT_EQ("bar", reply_args.data());
273 on_reply_received();
274 }));
275 task_runner_->RunUntilCheckpoint("on_reply_received");
276 }
277
TEST_F(HostImplTest,InvokeMethodDropReply)278 TEST_F(HostImplTest, InvokeMethodDropReply) {
279 FakeService* fake_service = new FakeService("FakeService");
280 ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
281 auto on_bind = task_runner_->CreateCheckpoint("on_bind");
282 cli_->BindService("FakeService");
283 EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
284 task_runner_->RunUntilCheckpoint("on_bind");
285
286 // OnFakeMethod1 will:
287 // - Do nothing on the 1st call, when |drop_reply| == true.
288 // - Reply on the the 2nd call, when |drop_reply| == false.
289 EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
290 .Times(2)
291 .WillRepeatedly(Invoke([](const RequestProto& req, DeferredBase* reply) {
292 if (req.data() == "drop_reply")
293 return;
294 std::unique_ptr<ReplyProto> reply_args(new ReplyProto());
295 reply_args->set_data("the_reply");
296 reply->Resolve(AsyncResult<ProtoMessage>(
297 std::unique_ptr<ProtoMessage>(reply_args.release())));
298 }));
299
300 auto on_reply_received = task_runner_->CreateCheckpoint("on_reply_received");
301 EXPECT_CALL(*cli_, OnInvokeMethodReply(_))
302 .WillOnce(
303 Invoke([on_reply_received](const Frame::InvokeMethodReply& reply) {
304 ASSERT_TRUE(reply.success());
305 ReplyProto reply_args;
306 reply_args.ParseFromString(reply.reply_proto());
307 ASSERT_EQ("the_reply", reply_args.data());
308 on_reply_received();
309 }));
310
311 // Invoke the method first with |drop_reply|=true, then |drop_reply|=false.
312 RequestProto rp;
313 rp.set_data("drop_reply");
314 cli_->InvokeMethod(cli_->last_bound_service_id_, 1, rp, true /*drop_reply*/);
315 rp.set_data("do_reply");
316 cli_->InvokeMethod(cli_->last_bound_service_id_, 1, rp, false /*drop_reply*/);
317
318 task_runner_->RunUntilCheckpoint("on_reply_received");
319 }
320
TEST_F(HostImplTest,SendFileDescriptor)321 TEST_F(HostImplTest, SendFileDescriptor) {
322 FakeService* fake_service = new FakeService("FakeService");
323 ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
324 auto on_bind = task_runner_->CreateCheckpoint("on_bind");
325 cli_->BindService("FakeService");
326 EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
327 task_runner_->RunUntilCheckpoint("on_bind");
328
329 static constexpr char kFileContent[] = "shared file";
330 RequestProto req_args;
331 cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args);
332 auto on_reply_sent = task_runner_->CreateCheckpoint("on_reply_sent");
333 base::TempFile tx_file = base::TempFile::CreateUnlinked();
334 ASSERT_EQ(static_cast<size_t>(base::WriteAll(tx_file.fd(), kFileContent,
335 sizeof(kFileContent))),
336 sizeof(kFileContent));
337 EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
338 .WillOnce(Invoke([on_reply_sent, &tx_file](const RequestProto&,
339 DeferredBase* reply) {
340 std::unique_ptr<ReplyProto> reply_args(new ReplyProto());
341 auto async_res = AsyncResult<ProtoMessage>(
342 std::unique_ptr<ProtoMessage>(reply_args.release()));
343 async_res.set_fd(tx_file.fd());
344 reply->Resolve(std::move(async_res));
345 on_reply_sent();
346 }));
347 task_runner_->RunUntilCheckpoint("on_reply_sent");
348 tx_file.ReleaseFD();
349
350 auto on_fd_received = task_runner_->CreateCheckpoint("on_fd_received");
351 EXPECT_CALL(*cli_, OnFileDescriptorReceived(_))
352 .WillOnce(Invoke([on_fd_received](int fd) {
353 char buf[sizeof(kFileContent)] = {};
354 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
355 ASSERT_EQ(static_cast<int32_t>(sizeof(buf)),
356 PERFETTO_EINTR(read(fd, buf, sizeof(buf))));
357 ASSERT_STREQ(kFileContent, buf);
358 on_fd_received();
359 }));
360 EXPECT_CALL(*cli_, OnInvokeMethodReply(_));
361 task_runner_->RunUntilCheckpoint("on_fd_received");
362 }
363
TEST_F(HostImplTest,ReceiveFileDescriptor)364 TEST_F(HostImplTest, ReceiveFileDescriptor) {
365 auto received = task_runner_->CreateCheckpoint("received");
366 FakeService* fake_service = new FakeService("FakeService");
367 ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
368 auto on_bind = task_runner_->CreateCheckpoint("on_bind");
369 cli_->BindService("FakeService");
370 EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
371 task_runner_->RunUntilCheckpoint("on_bind");
372
373 static constexpr char kFileContent[] = "shared file";
374 RequestProto req_args;
375 base::TempFile tx_file = base::TempFile::CreateUnlinked();
376 ASSERT_EQ(static_cast<size_t>(base::WriteAll(tx_file.fd(), kFileContent,
377 sizeof(kFileContent))),
378 sizeof(kFileContent));
379 cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args, false,
380 tx_file.fd());
381 EXPECT_CALL(*cli_, OnInvokeMethodReply(_));
382 base::ScopedFile rx_fd;
383 EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
384 .WillOnce(Invoke([received, &fake_service, &rx_fd](const RequestProto&,
385 DeferredBase*) {
386 rx_fd = fake_service->TakeReceivedFD();
387 received();
388 }));
389
390 task_runner_->RunUntilCheckpoint("received");
391
392 ASSERT_TRUE(rx_fd);
393 char buf[sizeof(kFileContent)] = {};
394 ASSERT_EQ(0, lseek(*rx_fd, 0, SEEK_SET));
395 ASSERT_EQ(static_cast<int32_t>(sizeof(buf)),
396 PERFETTO_EINTR(read(*rx_fd, buf, sizeof(buf))));
397 ASSERT_STREQ(kFileContent, buf);
398 }
399
400 // Invoke a method and immediately after disconnect the client.
TEST_F(HostImplTest,OnClientDisconnect)401 TEST_F(HostImplTest, OnClientDisconnect) {
402 FakeService* fake_service = new FakeService("FakeService");
403 ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
404 auto on_bind = task_runner_->CreateCheckpoint("on_bind");
405 cli_->BindService("FakeService");
406 EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
407 task_runner_->RunUntilCheckpoint("on_bind");
408
409 RequestProto req_args;
410 req_args.set_data("foo");
411 cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args);
412 EXPECT_CALL(*cli_, OnInvokeMethodReply(_)).Times(0);
413 cli_.reset(); // Disconnect the client.
414 auto on_host_method = task_runner_->CreateCheckpoint("on_host_method");
415 EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
416 .WillOnce(
417 Invoke([on_host_method](const RequestProto& req, DeferredBase*) {
418 ASSERT_EQ("foo", req.data());
419 on_host_method();
420 }));
421 task_runner_->RunUntilCheckpoint("on_host_method");
422 }
423
424 // Like InvokeMethod, but instead of resolving the Deferred reply within the
425 // call stack, std::move()-s it outside an replies
TEST_F(HostImplTest,MoveReplyObjectAndReplyAsynchronously)426 TEST_F(HostImplTest, MoveReplyObjectAndReplyAsynchronously) {
427 FakeService* fake_service = new FakeService("FakeService");
428 ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
429 auto on_bind = task_runner_->CreateCheckpoint("on_bind");
430 cli_->BindService("FakeService");
431 EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
432 task_runner_->RunUntilCheckpoint("on_bind");
433
434 // Invokes the remote method and waits that the FakeService sees it. The reply
435 // is not resolved but just moved into |moved_reply|.
436 RequestProto req_args;
437 cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args);
438 auto on_invoke = task_runner_->CreateCheckpoint("on_invoke");
439 DeferredBase moved_reply;
440 EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
441 .WillOnce(Invoke(
442 [on_invoke, &moved_reply](const RequestProto&, DeferredBase* reply) {
443 moved_reply = std::move(*reply);
444 on_invoke();
445 }));
446 task_runner_->RunUntilCheckpoint("on_invoke");
447
448 // Check that the FakeClient doesn't see any reply yet.
449 EXPECT_CALL(*cli_, OnInvokeMethodReply(_)).Times(0);
450 task_runner_->RunUntilIdle();
451 ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(cli_.get()));
452
453 // Resolve the reply asynchronously in a deferred task.
454 task_runner_->PostTask([&moved_reply] {
455 std::unique_ptr<ReplyProto> reply_args(new ReplyProto());
456 reply_args->set_data("bar");
457 moved_reply.Resolve(AsyncResult<ProtoMessage>(
458 std::unique_ptr<ProtoMessage>(reply_args.release())));
459 });
460
461 auto on_reply_received = task_runner_->CreateCheckpoint("on_reply_received");
462 EXPECT_CALL(*cli_, OnInvokeMethodReply(_))
463 .WillOnce(
464 Invoke([on_reply_received](const Frame::InvokeMethodReply& reply) {
465 ASSERT_TRUE(reply.success());
466 ASSERT_FALSE(reply.has_more());
467 ReplyProto reply_args;
468 reply_args.ParseFromString(reply.reply_proto());
469 ASSERT_EQ("bar", reply_args.data());
470 on_reply_received();
471 }));
472 task_runner_->RunUntilCheckpoint("on_reply_received");
473 }
474
475 // TODO(primiano): add the tests below in next CLs.
476 // TEST(HostImplTest, ManyClients) {}
477 // TEST(HostImplTest, OverlappingRequstsOutOfOrder) {}
478 // TEST(HostImplTest, StreamingRequest) {}
479 // TEST(HostImplTest, ManyDropReplyRequestsDontLeakMemory) {}
480
481 } // namespace
482 } // namespace ipc
483 } // namespace perfetto
484