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