blob: 043046eabe551c8f061723c27ab9fe349183de7c [file] [log] [blame]
/*
* Copyright 2020 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/adbd/arcvm_usb_to_sock.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <memory>
#include <utility>
#include <vector>
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <gtest/gtest.h>
namespace adbd {
namespace {
std::unique_ptr<ArcVmUsbToSock> SetupChannel(const int sock_fd,
const int output_fd) {
std::unique_ptr<ArcVmUsbToSock> channel =
std::make_unique<ArcVmUsbToSock>(sock_fd, output_fd);
if (!channel->Start()) {
LOG(ERROR) << "Failed to start channel for test";
return nullptr;
}
return channel;
}
base::Optional<std::pair<base::ScopedFD, base::ScopedFD>> SetupSocketPair() {
int fds[2];
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds) == -1) {
PLOG(ERROR) << "Failed to create socket pair for test";
return base::nullopt;
}
return base::make_optional(
std::make_pair(base::ScopedFD(fds[0]), base::ScopedFD(fds[1])));
}
// Writes the data in a vector to a fd and read the data back to check.
// return true if the content is identical between the write and read.
bool LoopCheck(int fd, const std::vector<char>& src) {
auto sz = src.size();
if (fd < 0 || !sz)
return false;
if (!base::WriteFileDescriptor(fd, src.data(), sz))
return false;
std::vector<char> output(sz);
if (!base::ReadFromFD(fd, output.data(), sz))
return false;
return src == output;
}
// Tests transfer.
void TestTransfer() {
auto sock_pair = SetupSocketPair();
ASSERT_TRUE(sock_pair.has_value());
auto channel_fd = sock_pair->second.get();
// A socket in the pair is bidirectional so we use the same socket
// for both of input and output of the channel.
auto channel = SetupChannel(channel_fd, channel_fd);
ASSERT_NE(channel, nullptr);
const size_t data_len = 100;
std::vector<char> message(data_len, 0x5);
EXPECT_TRUE(LoopCheck(sock_pair->first.get(), message));
exit(EXIT_SUCCESS);
}
// TODO(crbug.com/1087440): Replace deathtest with conventional unit test
// APIs once we refactor the main code to test transfer only, without
// creating threads.
// We use death test in gtest to run test cases in another process.
// By doing so exiting channel won't terminate the gtest process
// prematurely when we still have other cases to run with it.
TEST(ArcVmUsbToSockDeathTest, TestTransfer) {
EXPECT_EXIT(TestTransfer(), ::testing::ExitedWithCode(EXIT_SUCCESS), "");
}
} // namespace
} // namespace adbd