blob: a98efb78c766e817d0382e9274147026433a6ea9 [file] [log] [blame]
Primiano Tucci420e47e2017-11-14 11:31:49 +00001/*
2 * Copyright (C) 2017 The Android Open foo 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 "ipc/src/host_impl.h"
18
19#include <memory>
20
Primiano Tucci420e47e2017-11-14 11:31:49 +000021#include "gmock/gmock.h"
22#include "gtest/gtest.h"
23#include "ipc/service.h"
24#include "ipc/service_descriptor.h"
25#include "ipc/src/buffered_frame_deserializer.h"
26#include "ipc/src/unix_socket.h"
Oystein Eftevaagdd727e42017-12-05 08:49:55 -080027#include "perfetto_base/scoped_file.h"
28#include "perfetto_base/test/test_task_runner.h"
Primiano Tucci420e47e2017-11-14 11:31:49 +000029
Primiano Tucci2ee254a2017-11-15 00:38:48 +000030#include "ipc/src/test/client_unittest_messages.pb.h"
31#include "ipc/src/wire_protocol.pb.h"
Primiano Tucci420e47e2017-11-14 11:31:49 +000032
33namespace perfetto {
34namespace ipc {
35namespace {
36
37using ::testing::_;
38using ::testing::Invoke;
39using ::testing::InvokeWithoutArgs;
40
41constexpr char kSockName[] = "/tmp/perfetto_host_impl_unittest.sock";
42
43// RequestProto and ReplyProto are defined in client_unittest_messages.proto.
44
45class FakeService : public Service {
46 public:
47 MOCK_METHOD0(Destroyed, void());
48 MOCK_METHOD2(OnFakeMethod1, void(const RequestProto&, DeferredBase*));
49
50 static void Invoker(Service* service,
51 const ProtoMessage& req,
52 DeferredBase deferred_reply) {
53 static_cast<FakeService*>(service)->OnFakeMethod1(
54 static_cast<const RequestProto&>(req), &deferred_reply);
55 }
56
57 static std::unique_ptr<ProtoMessage> RequestDecoder(
58 const std::string& proto) {
59 std::unique_ptr<ProtoMessage> reply(new RequestProto());
60 EXPECT_TRUE(reply->ParseFromString(proto));
61 return reply;
62 }
63
64 FakeService(const char* service_name) {
65 descriptor_.service_name = service_name;
66 descriptor_.methods.push_back(
67 {"FakeMethod1", &RequestDecoder, nullptr, &Invoker});
68 }
69
70 const ServiceDescriptor& GetDescriptor() override { return descriptor_; }
71
72 ServiceDescriptor descriptor_;
73};
74
75class FakeClient : public UnixSocket::EventListener {
76 public:
77 MOCK_METHOD0(OnConnect, void());
78 MOCK_METHOD0(OnDisconnect, void());
79 MOCK_METHOD1(OnServiceBound, void(const Frame::BindServiceReply&));
80 MOCK_METHOD1(OnInvokeMethodReply, void(const Frame::InvokeMethodReply&));
Primiano Tuccif54cae42017-11-21 19:37:13 +000081 MOCK_METHOD1(OnFileDescriptorReceived, void(int));
Primiano Tucci420e47e2017-11-14 11:31:49 +000082 MOCK_METHOD0(OnRequestError, void());
83
84 explicit FakeClient(base::TaskRunner* task_runner) {
85 sock_ = UnixSocket::Connect(kSockName, this, task_runner);
86 }
87
88 ~FakeClient() override = default;
89
90 void BindService(const std::string& service_name) {
91 Frame frame;
92 uint64_t request_id = requests_.empty() ? 1 : requests_.rbegin()->first + 1;
93 requests_.emplace(request_id, 0);
94 frame.set_request_id(request_id);
95 frame.mutable_msg_bind_service()->set_service_name(service_name);
96 SendFrame(frame);
97 }
98
99 void InvokeMethod(ServiceID service_id,
100 MethodID method_id,
101 const ProtoMessage& args) {
102 Frame frame;
103 uint64_t request_id = requests_.empty() ? 1 : requests_.rbegin()->first + 1;
104 requests_.emplace(request_id, 0);
105 frame.set_request_id(request_id);
106 frame.mutable_msg_invoke_method()->set_service_id(service_id);
107 frame.mutable_msg_invoke_method()->set_method_id(method_id);
108 frame.mutable_msg_invoke_method()->set_args_proto(args.SerializeAsString());
109 SendFrame(frame);
110 }
111
112 // UnixSocket::EventListener implementation.
113 void OnConnect(UnixSocket*, bool success) override {
114 ASSERT_TRUE(success);
115 OnConnect();
116 }
117
118 void OnDisconnect(UnixSocket*) override { OnDisconnect(); }
119
120 void OnDataAvailable(UnixSocket* sock) override {
121 ASSERT_EQ(sock_.get(), sock);
122 auto buf = frame_deserializer_.BeginReceive();
Primiano Tuccif54cae42017-11-21 19:37:13 +0000123 base::ScopedFile fd;
124 size_t rsize = sock->Receive(buf.data, buf.size, &fd);
Primiano Tucci420e47e2017-11-14 11:31:49 +0000125 ASSERT_TRUE(frame_deserializer_.EndReceive(rsize));
Primiano Tuccif54cae42017-11-21 19:37:13 +0000126 if (fd)
127 OnFileDescriptorReceived(*fd);
Primiano Tucci420e47e2017-11-14 11:31:49 +0000128 while (std::unique_ptr<Frame> frame = frame_deserializer_.PopNextFrame()) {
129 ASSERT_EQ(1u, requests_.count(frame->request_id()));
130 EXPECT_EQ(0, requests_[frame->request_id()]++);
131 if (frame->msg_case() == Frame::kMsgBindServiceReply) {
132 if (frame->msg_bind_service_reply().success())
133 last_bound_service_id_ = frame->msg_bind_service_reply().service_id();
134 return OnServiceBound(frame->msg_bind_service_reply());
135 }
136 if (frame->msg_case() == Frame::kMsgInvokeMethodReply)
137 return OnInvokeMethodReply(frame->msg_invoke_method_reply());
138 if (frame->msg_case() == Frame::kMsgRequestError)
139 return OnRequestError();
140 FAIL() << "Unexpected frame received from host " << frame->msg_case();
141 }
142 }
143
144 void SendFrame(const Frame& frame) {
145 std::string buf = BufferedFrameDeserializer::Serialize(frame);
146 ASSERT_TRUE(sock_->Send(buf.data(), buf.size()));
147 }
148
149 BufferedFrameDeserializer frame_deserializer_;
150 std::unique_ptr<UnixSocket> sock_;
151 std::map<uint64_t /* request_id */, int /* num_replies_received */> requests_;
152 ServiceID last_bound_service_id_;
153};
154
155class HostImplTest : public ::testing::Test {
156 public:
157 void SetUp() override {
158 unlink(kSockName);
159 task_runner_.reset(new base::TestTaskRunner());
160 Host* host = Host::CreateInstance(kSockName, task_runner_.get()).release();
161 ASSERT_NE(nullptr, host);
162 host_.reset(static_cast<HostImpl*>(host));
163 cli_.reset(new FakeClient(task_runner_.get()));
164 auto on_connect = task_runner_->CreateCheckpoint("on_connect");
165 EXPECT_CALL(*cli_, OnConnect()).WillOnce(Invoke(on_connect));
166 task_runner_->RunUntilCheckpoint("on_connect");
167 }
168
169 void TearDown() override {
170 task_runner_->RunUntilIdle();
171 cli_.reset();
172 host_.reset();
173 task_runner_->RunUntilIdle();
174 task_runner_.reset();
175 unlink(kSockName);
176 }
177
178 // ::testing::StrictMock<MockEventListener> proxy_events_;
179 std::unique_ptr<base::TestTaskRunner> task_runner_;
180 std::unique_ptr<HostImpl> host_;
181 std::unique_ptr<FakeClient> cli_;
182};
183
184TEST_F(HostImplTest, BindService) {
185 // First bind the service when it doesn't exists yet and check that the
186 // BindService() request fails.
187 cli_->BindService("FakeService"); // FakeService does not exist yet.
188 auto on_bind_failure = task_runner_->CreateCheckpoint("on_bind_failure");
189 EXPECT_CALL(*cli_, OnServiceBound(_))
190 .WillOnce(Invoke([on_bind_failure](const Frame::BindServiceReply& reply) {
191 ASSERT_FALSE(reply.success());
192 on_bind_failure();
193 }));
194 task_runner_->RunUntilCheckpoint("on_bind_failure");
195
196 // Now expose the service and bind it.
197 ASSERT_TRUE(host_->ExposeService(
198 std::unique_ptr<Service>(new FakeService("FakeService"))));
199 auto on_bind_success = task_runner_->CreateCheckpoint("on_bind_success");
200 cli_->BindService("FakeService");
201 EXPECT_CALL(*cli_, OnServiceBound(_))
202 .WillOnce(Invoke([on_bind_success](const Frame::BindServiceReply& reply) {
203 ASSERT_TRUE(reply.success());
204 on_bind_success();
205 }));
206 task_runner_->RunUntilCheckpoint("on_bind_success");
207}
208
209TEST_F(HostImplTest, InvokeNonExistingMethod) {
210 FakeService* fake_service = new FakeService("FakeService");
211 ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
212 auto on_bind = task_runner_->CreateCheckpoint("on_bind");
213 cli_->BindService("FakeService");
214 EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
215 task_runner_->RunUntilCheckpoint("on_bind");
216
217 auto on_invoke_failure = task_runner_->CreateCheckpoint("on_invoke_failure");
218 cli_->InvokeMethod(cli_->last_bound_service_id_, 42, RequestProto());
219 EXPECT_CALL(*cli_, OnInvokeMethodReply(_))
220 .WillOnce(
221 Invoke([on_invoke_failure](const Frame::InvokeMethodReply& reply) {
222 ASSERT_FALSE(reply.success());
223 ASSERT_FALSE(reply.has_more());
224 on_invoke_failure();
225 }));
226 task_runner_->RunUntilCheckpoint("on_invoke_failure");
227}
228
229TEST_F(HostImplTest, InvokeMethod) {
230 FakeService* fake_service = new FakeService("FakeService");
231 ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
232 auto on_bind = task_runner_->CreateCheckpoint("on_bind");
233 cli_->BindService("FakeService");
234 EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
235 task_runner_->RunUntilCheckpoint("on_bind");
236
237 RequestProto req_args;
238 req_args.set_data("foo");
239 cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args);
240 auto on_reply_sent = task_runner_->CreateCheckpoint("on_reply_sent");
241 EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
242 .WillOnce(
243 Invoke([on_reply_sent](const RequestProto& req, DeferredBase* reply) {
244 ASSERT_EQ("foo", req.data());
245 std::unique_ptr<ReplyProto> reply_args(new ReplyProto());
246 reply_args->set_data("bar");
247 reply->Resolve(AsyncResult<ProtoMessage>(
248 std::unique_ptr<ProtoMessage>(reply_args.release())));
249 on_reply_sent();
250 }));
251 task_runner_->RunUntilCheckpoint("on_reply_sent");
252
253 auto on_reply_received = task_runner_->CreateCheckpoint("on_reply_received");
254 EXPECT_CALL(*cli_, OnInvokeMethodReply(_))
255 .WillOnce(
256 Invoke([on_reply_received](const Frame::InvokeMethodReply& reply) {
257 ASSERT_TRUE(reply.success());
258 ASSERT_FALSE(reply.has_more());
259 ReplyProto reply_args;
260 reply_args.ParseFromString(reply.reply_proto());
261 ASSERT_EQ("bar", reply_args.data());
262 on_reply_received();
263 }));
264 task_runner_->RunUntilCheckpoint("on_reply_received");
265}
266
Primiano Tuccif54cae42017-11-21 19:37:13 +0000267TEST_F(HostImplTest, SendFileDescriptor) {
268 FakeService* fake_service = new FakeService("FakeService");
269 ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
270 auto on_bind = task_runner_->CreateCheckpoint("on_bind");
271 cli_->BindService("FakeService");
272 EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
273 task_runner_->RunUntilCheckpoint("on_bind");
274
275 static constexpr char kFileContent[] = "shared file";
276 RequestProto req_args;
277 cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args);
278 auto on_reply_sent = task_runner_->CreateCheckpoint("on_reply_sent");
279 FILE* tx_file = tmpfile();
280 fwrite(kFileContent, sizeof(kFileContent), 1, tx_file);
281 fflush(tx_file);
282 EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
283 .WillOnce(Invoke([on_reply_sent, tx_file](const RequestProto& req,
284 DeferredBase* reply) {
285 std::unique_ptr<ReplyProto> reply_args(new ReplyProto());
286 auto async_res = AsyncResult<ProtoMessage>(
287 std::unique_ptr<ProtoMessage>(reply_args.release()));
288 async_res.set_fd(fileno(tx_file));
289 reply->Resolve(std::move(async_res));
290 on_reply_sent();
291 }));
292 task_runner_->RunUntilCheckpoint("on_reply_sent");
293 fclose(tx_file);
294
295 auto on_fd_received = task_runner_->CreateCheckpoint("on_fd_received");
296 EXPECT_CALL(*cli_, OnFileDescriptorReceived(_))
297 .WillOnce(Invoke([on_fd_received](int fd) {
298 char buf[sizeof(kFileContent)] = {};
299 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
Oystein Eftevaagdd727e42017-12-05 08:49:55 -0800300 ASSERT_EQ(static_cast<int32_t>(sizeof(buf)),
301 PERFETTO_EINTR(read(fd, buf, sizeof(buf))));
Primiano Tuccif54cae42017-11-21 19:37:13 +0000302 ASSERT_STREQ(kFileContent, buf);
303 on_fd_received();
304 }));
305 EXPECT_CALL(*cli_, OnInvokeMethodReply(_));
306 task_runner_->RunUntilCheckpoint("on_fd_received");
307}
308
Primiano Tucci420e47e2017-11-14 11:31:49 +0000309// Invoke a method and immediately after disconnect the client.
310TEST_F(HostImplTest, OnClientDisconnect) {
311 FakeService* fake_service = new FakeService("FakeService");
312 ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
313 auto on_bind = task_runner_->CreateCheckpoint("on_bind");
314 cli_->BindService("FakeService");
315 EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
316 task_runner_->RunUntilCheckpoint("on_bind");
317
318 RequestProto req_args;
319 req_args.set_data("foo");
320 cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args);
321 EXPECT_CALL(*cli_, OnInvokeMethodReply(_)).Times(0);
Primiano Tuccic2025af2017-11-20 12:47:49 +0000322 cli_.reset(); // Disconnect the client.
Primiano Tucci420e47e2017-11-14 11:31:49 +0000323 auto on_host_method = task_runner_->CreateCheckpoint("on_host_method");
324 EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
325 .WillOnce(Invoke(
326 [on_host_method](const RequestProto& req, DeferredBase* reply) {
327 ASSERT_EQ("foo", req.data());
328 on_host_method();
329 }));
330 task_runner_->RunUntilCheckpoint("on_host_method");
331}
332
333// Like InvokeMethod, but instead of resolving the Deferred reply within the
334// call stack, std::move()-s it outside an replies
335TEST_F(HostImplTest, MoveReplyObjectAndReplyAsynchronously) {
336 FakeService* fake_service = new FakeService("FakeService");
337 ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
338 auto on_bind = task_runner_->CreateCheckpoint("on_bind");
339 cli_->BindService("FakeService");
340 EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
341 task_runner_->RunUntilCheckpoint("on_bind");
342
343 // Invokes the remote method and waits that the FakeService sees it. The reply
344 // is not resolved but just moved into |moved_reply|.
345 RequestProto req_args;
346 cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args);
347 auto on_invoke = task_runner_->CreateCheckpoint("on_invoke");
348 DeferredBase moved_reply;
349 EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
350 .WillOnce(Invoke([on_invoke, &moved_reply](const RequestProto& req,
351 DeferredBase* reply) {
352 moved_reply = std::move(*reply);
353 on_invoke();
354 }));
355 task_runner_->RunUntilCheckpoint("on_invoke");
356
357 // Check that the FakeClient doesn't see any reply yet.
358 EXPECT_CALL(*cli_, OnInvokeMethodReply(_)).Times(0);
359 task_runner_->RunUntilIdle();
360 ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(cli_.get()));
361
362 // Resolve the reply asynchronously in a deferred task.
363 task_runner_->PostTask([&moved_reply] {
364 std::unique_ptr<ReplyProto> reply_args(new ReplyProto());
365 reply_args->set_data("bar");
366 moved_reply.Resolve(AsyncResult<ProtoMessage>(
367 std::unique_ptr<ProtoMessage>(reply_args.release())));
368 });
369
370 auto on_reply_received = task_runner_->CreateCheckpoint("on_reply_received");
371 EXPECT_CALL(*cli_, OnInvokeMethodReply(_))
372 .WillOnce(
373 Invoke([on_reply_received](const Frame::InvokeMethodReply& reply) {
374 ASSERT_TRUE(reply.success());
375 ASSERT_FALSE(reply.has_more());
376 ReplyProto reply_args;
377 reply_args.ParseFromString(reply.reply_proto());
378 ASSERT_EQ("bar", reply_args.data());
379 on_reply_received();
380 }));
381 task_runner_->RunUntilCheckpoint("on_reply_received");
382}
383
384// TODO(primiano): add the tests below in next CLs.
385// TEST(HostImplTest, ManyClients) {}
386// TEST(HostImplTest, OverlappingRequstsOutOfOrder) {}
387// TEST(HostImplTest, StreamingRequest) {}
388
389} // namespace
390} // namespace ipc
391} // namespace perfetto