| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include <stdlib.h> |
| #include <memory> |
| #include <string> |
| #include <thread> |
| |
| #include <glog/logging.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "common/libs/fs/shared_fd.h" |
| #include "host/libs/ivserver/hald_client.h" |
| #include "host/libs/ivserver/vsocsharedmem_mock.h" |
| |
| using ::testing::_; |
| using ::testing::DoAll; |
| using ::testing::Return; |
| using ::testing::ReturnRef; |
| using ::testing::SetArgPointee; |
| |
| namespace ivserver { |
| namespace test { |
| |
| class HaldClientTest : public ::testing::Test { |
| public: |
| void SetUp() override { |
| std::string socket_location; |
| ASSERT_TRUE(GetTempLocation(&socket_location)) |
| << "Could not create temp file"; |
| LOG(INFO) << "Temp file location: " << socket_location; |
| |
| hald_server_socket_ = cvd::SharedFD::SocketLocalServer( |
| socket_location.c_str(), false, SOCK_STREAM, 0666); |
| test_socket_ = cvd::SharedFD::SocketLocalClient(socket_location.c_str(), |
| false, SOCK_STREAM); |
| hald_socket_ = |
| cvd::SharedFD::Accept(*hald_server_socket_, nullptr, nullptr); |
| |
| EXPECT_TRUE(hald_server_socket_->IsOpen()); |
| EXPECT_TRUE(test_socket_->IsOpen()); |
| EXPECT_TRUE(hald_socket_->IsOpen()); |
| } |
| |
| bool GetTempLocation(std::string* target) { |
| char temp_location[] = "/tmp/hald-client-test-XXXXXX"; |
| mktemp(temp_location); |
| *target = temp_location; |
| if (target->size()) { |
| cleanup_files_.emplace_back(*target); |
| return true; |
| } |
| return false; |
| } |
| |
| void TearDown() override { |
| hald_socket_->Close(); |
| test_socket_->Close(); |
| hald_server_socket_->Close(); |
| |
| for (const std::string& file : cleanup_files_) { |
| unlink(file.c_str()); |
| } |
| } |
| |
| protected: |
| ::testing::NiceMock<VSoCSharedMemoryMock> vsoc_; |
| |
| cvd::SharedFD hald_server_socket_; |
| cvd::SharedFD hald_socket_; |
| cvd::SharedFD test_socket_; |
| |
| private: |
| std::vector<std::string> cleanup_files_; |
| }; |
| |
| TEST_F(HaldClientTest, HandshakeTerminatedByHald) { |
| std::thread thread( |
| [this]() { |
| auto client(HaldClient::New(vsoc_, hald_socket_)); |
| EXPECT_FALSE(client) |
| << "Handshake should not complete when client terminates early."; |
| }); |
| |
| test_socket_->Close(); |
| thread.join(); |
| } |
| |
| TEST_F(HaldClientTest, HandshakeTerminatedByInvalidRegionSize) { |
| uint16_t sizes[] = {0, VSoCSharedMemory::kMaxRegionNameLength + 1, 0xffff}; |
| |
| for (uint16_t size : sizes) { |
| std::thread thread([this, size]() { |
| auto client(HaldClient::New(vsoc_, hald_socket_)); |
| EXPECT_FALSE(client) << "Handle should not be created when size is " |
| << size; |
| }); |
| |
| int32_t proto_version; |
| EXPECT_EQ(sizeof(proto_version), |
| test_socket_->Recv(&proto_version, sizeof(proto_version), |
| MSG_NOSIGNAL)); |
| |
| test_socket_->Send(&size, sizeof(size), MSG_NOSIGNAL); |
| thread.join(); |
| } |
| } |
| |
| TEST_F(HaldClientTest, FullSaneHandshake) { |
| std::string temp; |
| ASSERT_TRUE(GetTempLocation(&temp)); |
| cvd::SharedFD host_fd( |
| cvd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666)); |
| EXPECT_TRUE(host_fd->IsOpen()); |
| |
| ASSERT_TRUE(GetTempLocation(&temp)); |
| cvd::SharedFD guest_fd( |
| cvd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666)); |
| EXPECT_TRUE(guest_fd->IsOpen()); |
| |
| ASSERT_TRUE(GetTempLocation(&temp)); |
| cvd::SharedFD shmem_fd( |
| cvd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666)); |
| EXPECT_TRUE(shmem_fd->IsOpen()); |
| |
| const std::string test_location("testing"); |
| EXPECT_CALL(vsoc_, GetEventFdPairForRegion(test_location, _, _)) |
| .WillOnce(DoAll(SetArgPointee<1>(host_fd), SetArgPointee<2>(guest_fd), |
| Return(true))); |
| EXPECT_CALL(vsoc_, SharedMemFD()).WillOnce(ReturnRef(shmem_fd)); |
| |
| std::thread thread([this]() { |
| auto client(HaldClient::New(vsoc_, hald_socket_)); |
| EXPECT_TRUE(client); |
| }); |
| |
| int32_t proto_version; |
| EXPECT_EQ( |
| sizeof(proto_version), |
| test_socket_->Recv(&proto_version, sizeof(proto_version), MSG_NOSIGNAL)); |
| |
| uint16_t size = test_location.size(); |
| EXPECT_EQ(sizeof(size), |
| test_socket_->Send(&size, sizeof(size), MSG_NOSIGNAL)); |
| EXPECT_EQ(size, test_socket_->Send(test_location.data(), size, MSG_NOSIGNAL)); |
| |
| // TODO(ender): delete this once no longer necessary. Currently, absence of |
| // payload makes RecvMsgAndFDs hang forever. |
| uint64_t control_data; |
| struct iovec vec { |
| &control_data, sizeof(control_data) |
| }; |
| cvd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0}; |
| cvd::SharedFD fds[3]; |
| |
| EXPECT_GT(test_socket_->RecvMsgAndFDs<3>(hdr, MSG_NOSIGNAL, &fds), 0); |
| EXPECT_TRUE(fds[0]->IsOpen()); |
| EXPECT_TRUE(fds[1]->IsOpen()); |
| EXPECT_TRUE(fds[2]->IsOpen()); |
| |
| thread.join(); |
| } |
| |
| } // namespace test |
| } // namespace ivserver |