blob: 9a5deb365ff5341afb367c5c4dba3f82926f1af5 [file] [log] [blame]
// Copyright 2019 The Chromium OS 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 "arc/vm/vsock_proxy/socket_stream.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/files/file_descriptor_watcher_posix.h>
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <base/macros.h>
#include <base/message_loop/message_loop.h>
#include <base/optional.h>
#include <base/posix/eintr_wrapper.h>
#include <base/posix/unix_domain_socket_linux.h>
#include <base/run_loop.h>
#include <base/strings/string_piece.h>
#include <gtest/gtest.h>
#include "arc/vm/vsock_proxy/file_descriptor_util.h"
namespace arc {
namespace {
class SocketStreamTest : public testing::Test {
public:
SocketStreamTest() = default;
~SocketStreamTest() override = default;
void SetUp() override {
auto sockets = CreateSocketPair();
ASSERT_TRUE(sockets.has_value());
stream_ = std::make_unique<SocketStream>(std::move(sockets.value().first));
socket_ = std::move(sockets.value().second);
}
protected:
std::unique_ptr<SocketStream> stream_; // Paired with socket_.
base::ScopedFD socket_; // Paired with stream_.
private:
base::MessageLoopForIO message_loop_;
base::FileDescriptorWatcher watcher_{&message_loop_};
DISALLOW_COPY_AND_ASSIGN(SocketStreamTest);
};
TEST_F(SocketStreamTest, Read) {
base::ScopedFD attached_fd(HANDLE_EINTR(open("/dev/null", O_RDONLY)));
ASSERT_TRUE(attached_fd.is_valid());
constexpr char kData[] = "abcdefghijklmnopqrstuvwxyz";
ASSERT_TRUE(base::UnixDomainSocket::SendMsg(
socket_.get(), kData, sizeof(kData), {attached_fd.get()}));
auto read_result = stream_->Read();
EXPECT_EQ(0, read_result.error_code);
EXPECT_EQ(base::StringPiece(kData, sizeof(kData)), read_result.blob);
EXPECT_EQ(1, read_result.fds.size());
}
TEST_F(SocketStreamTest, ReadEOF) {
// Close the other side immediately.
socket_.reset();
auto read_result = stream_->Read();
EXPECT_EQ(0, read_result.error_code);
EXPECT_TRUE(read_result.blob.empty());
EXPECT_TRUE(read_result.fds.empty());
}
TEST_F(SocketStreamTest, ReadError) {
// Pass invalid FD.
auto read_result = SocketStream(base::ScopedFD()).Read();
EXPECT_EQ(EBADF, read_result.error_code);
}
TEST_F(SocketStreamTest, Write) {
base::ScopedFD attached_fd(HANDLE_EINTR(open("/dev/null", O_RDONLY)));
ASSERT_TRUE(attached_fd.is_valid());
constexpr char kData[] = "abcdefghijklmnopqrstuvwxyz";
{
std::vector<base::ScopedFD> fds;
fds.emplace_back(std::move(attached_fd));
ASSERT_TRUE(
stream_->Write(std::string(kData, sizeof(kData)), std::move(fds)));
}
std::string read_data;
read_data.resize(sizeof(kData));
std::vector<base::ScopedFD> fds;
ASSERT_EQ(sizeof(kData),
base::UnixDomainSocket::RecvMsg(socket_.get(), &read_data[0],
sizeof(kData), &fds));
EXPECT_EQ(1, fds.size());
}
TEST_F(SocketStreamTest, PendingWrite) {
int sndbuf_value = 0;
socklen_t len = sizeof(sndbuf_value);
ASSERT_EQ(
0, getsockopt(socket_.get(), SOL_SOCKET, SO_SNDBUF, &sndbuf_value, &len));
const std::string data1(sndbuf_value, 'a');
const std::string data2(sndbuf_value, 'b');
const std::string data3(sndbuf_value, 'c');
// Write data1, data2, and data3 to the stream.
ASSERT_TRUE(stream_->Write(data1, {}));
ASSERT_TRUE(stream_->Write(data2, {}));
ASSERT_TRUE(stream_->Write(data3, {}));
// Read data1 from the other socket.
std::string read_data;
read_data.resize(sndbuf_value);
std::vector<base::ScopedFD> fds;
ASSERT_EQ(data1.size(),
base::UnixDomainSocket::RecvMsg(socket_.get(), &read_data[0],
read_data.size(), &fds));
read_data.resize(data1.size());
EXPECT_EQ(data1, read_data);
// data2 is still pending.
ASSERT_EQ(-1, base::UnixDomainSocket::RecvMsg(socket_.get(), &read_data[0],
read_data.size(), &fds));
ASSERT_EQ(EAGAIN, errno);
// Now the socket's buffer is empty. Let the stream write data2 to the socket.
base::RunLoop().RunUntilIdle();
// Read data2 from the other socket.
read_data.resize(sndbuf_value);
ASSERT_EQ(data2.size(),
base::UnixDomainSocket::RecvMsg(socket_.get(), &read_data[0],
read_data.size(), &fds));
read_data.resize(data2.size());
EXPECT_EQ(data2, read_data);
// data3 is still pending.
ASSERT_EQ(-1, base::UnixDomainSocket::RecvMsg(socket_.get(), &read_data[0],
read_data.size(), &fds));
ASSERT_EQ(EAGAIN, errno);
// Let the stream write data3 to the socket.
base::RunLoop().RunUntilIdle();
// Read data3 from the other socket.
read_data.resize(sndbuf_value);
ASSERT_EQ(data3.size(),
base::UnixDomainSocket::RecvMsg(socket_.get(), &read_data[0],
read_data.size(), &fds));
read_data.resize(data3.size());
EXPECT_EQ(data3, read_data);
}
} // namespace
} // namespace arc