blob: 46743449c8d54fb8f3b3774357dfe7e7fa7e607a [file] [log] [blame]
// 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"));
}