| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <string.h> |
| |
| #include <future> |
| |
| #include "guest_test.h" |
| |
| template <class T> |
| class VsockGuestTest : public GuestTest<T>, public fuchsia::virtualization::HostVsockAcceptor { |
| public: |
| struct IncomingRequest { |
| uint32_t src_cid; |
| uint32_t src_port; |
| uint32_t port; |
| fuchsia::virtualization::HostVsockAcceptor::AcceptCallback callback; |
| }; |
| |
| std::vector<IncomingRequest> requests; |
| |
| void Accept(uint32_t src_cid, uint32_t src_port, uint32_t port, |
| AcceptCallback callback) override { |
| requests.push_back({src_cid, src_port, port, std::move(callback)}); |
| } |
| |
| void Read(zx::socket& socket, size_t amount) { |
| size_t total_read = 0; |
| while (total_read != amount) { |
| size_t read; |
| char buf[1000]; |
| zx_signals_t pending; |
| socket.wait_one(ZX_SOCKET_READABLE, zx::time::infinite(), &pending); |
| ASSERT_TRUE((pending & ZX_SOCKET_READABLE) != 0); |
| size_t read_amount = std::min(amount - total_read, sizeof(buf)); |
| ASSERT_EQ(socket.read(0, &buf, read_amount, &read), ZX_OK); |
| total_read += read; |
| } |
| } |
| |
| void TestReadWrite(zx::socket& socket) { |
| Read(socket, 60000 * 4); |
| uint8_t value = 42; |
| size_t actual; |
| ASSERT_EQ(socket.write(0, &value, 1, &actual), ZX_OK); |
| EXPECT_EQ(static_cast<int>(actual), 1); |
| } |
| |
| void TestThread() { |
| async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); |
| |
| fuchsia::virtualization::HostVsockEndpointSyncPtr vsock_endpoint; |
| this->GetHostVsockEndpoint(vsock_endpoint.NewRequest()); |
| |
| fidl::Binding<fuchsia::virtualization::HostVsockAcceptor> binding{this}; |
| zx_status_t out_status; |
| ASSERT_EQ(vsock_endpoint->Listen(8000, binding.NewBinding(), &out_status), ZX_OK); |
| ASSERT_EQ(out_status, ZX_OK); |
| |
| ASSERT_EQ(binding.WaitForMessage(), ZX_OK); |
| ASSERT_TRUE(requests.size() == 1); |
| ASSERT_EQ(requests[0].src_cid, this->GetGuestCid()); |
| ASSERT_EQ(requests[0].src_port, 49152u); |
| ASSERT_EQ(requests[0].port, 8000u); |
| |
| zx::socket socket1, socket2; |
| ASSERT_EQ(zx::socket::create(ZX_SOCKET_STREAM, &socket1, &socket2), ZX_OK); |
| requests.back().callback(ZX_OK, std::move(socket2)); |
| requests.pop_back(); |
| TestReadWrite(socket1); |
| |
| zx_signals_t pending; |
| // Read the read/write completes we expect the util to close the connection. |
| socket1.wait_one(ZX_SOCKET_PEER_CLOSED, zx::time::infinite(), &pending); |
| ASSERT_TRUE((pending & ZX_SOCKET_PEER_CLOSED) != 0); |
| |
| socket1.reset(); |
| |
| // Attempt to connect into the guest |
| EXPECT_EQ(zx::socket::create(ZX_SOCKET_STREAM, &socket1, &socket2), ZX_OK); |
| ASSERT_EQ(vsock_endpoint->Connect(this->GetGuestCid(), 8001, std::move(socket2), &out_status), |
| ZX_OK); |
| ASSERT_EQ(out_status, ZX_OK); |
| |
| // Wait for all the data |
| TestReadWrite(socket1); |
| // Close the connection by dropping the socket. |
| socket1.reset(); |
| |
| // Open another connection. |
| EXPECT_EQ(zx::socket::create(ZX_SOCKET_STREAM, &socket1, &socket2), ZX_OK); |
| ASSERT_EQ(vsock_endpoint->Connect(this->GetGuestCid(), 8002, std::move(socket2), &out_status), |
| ZX_OK); |
| ASSERT_EQ(out_status, ZX_OK); |
| // Read some data then close the connection |
| Read(socket1, 10); |
| socket1.reset(); |
| |
| // Wait for another connection to get accepted |
| EXPECT_EQ(zx::socket::create(ZX_SOCKET_STREAM, &socket1, &socket2), ZX_OK); |
| ASSERT_EQ(vsock_endpoint->Connect(this->GetGuestCid(), 8003, std::move(socket2), &out_status), |
| ZX_OK); |
| ASSERT_EQ(out_status, ZX_OK); |
| // Keep writing until we get peer closed |
| zx_status_t status; |
| do { |
| socket1.wait_one(ZX_SOCKET_PEER_CLOSED | ZX_SOCKET_WRITABLE, zx::time::infinite(), &pending); |
| if ((pending & ZX_SOCKET_WRITABLE) != 0) { |
| uint8_t buf[1000] = {}; |
| size_t actual = 0; |
| status = socket1.write(0, buf, sizeof(buf), &actual); |
| } |
| } while (status != ZX_ERR_PEER_CLOSED && (pending & ZX_SOCKET_PEER_CLOSED) == 0); |
| } |
| }; |
| |
| using GuestTypes = ::testing::Types<ZirconEnclosedGuest, DebianEnclosedGuest>; |
| |
| TYPED_TEST_SUITE(VsockGuestTest, GuestTypes); |
| |
| TYPED_TEST(VsockGuestTest, DISABLED_ConnectDisconnect) { |
| auto handle = std::async(std::launch::async, [this] { this->TestThread(); }); |
| |
| std::string result; |
| EXPECT_EQ(this->RunUtil("virtio_vsock_test_util", {}, &result), ZX_OK); |
| |
| handle.wait(); |
| |
| EXPECT_THAT(result, ::testing::HasSubstr("PASS")); |
| } |