blob: c61283b37f0ebf03ebdf214d0abf964b54829176 [file] [log] [blame]
// Copyright 2019 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 "src/bringup/bin/netsvc/file-api.h"
#include <fcntl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <memory>
#include <string_view>
#include <zxtest/zxtest.h>
namespace {
class FakePaver : public netsvc::PaverInterface {
public:
FakePaver() { exit_code_.set_value(ZX_OK); }
std::shared_future<zx_status_t> exit_code() override {
if (exit_code_future_.has_value()) {
return exit_code_future_.value();
}
return exit_code_future_.emplace(exit_code_.get_future());
}
tftp_status OpenWrite(std::string_view filename, size_t size, zx::duration) override {
exit_code_future_.reset();
exit_code_ = {};
return TFTP_NO_ERROR;
}
tftp_status Write(const void* data, size_t* length, off_t offset) override {
if (exit_code().wait_for(std::chrono::nanoseconds::zero()) == std::future_status::ready) {
return TFTP_ERR_INTERNAL;
}
return TFTP_NO_ERROR;
}
void Close() override {}
void Abort() override { ADD_FATAL_FAILURE("unexpected call to abort"); }
void set_exit_code(zx_status_t exit_code) {
exit_code_future_.reset();
exit_code_ = {};
exit_code_.set_value(exit_code);
}
private:
std::promise<zx_status_t> exit_code_;
std::optional<std::shared_future<zx_status_t>> exit_code_future_;
};
constexpr char kReadData[] = "laksdfjsadfa";
constexpr char kFakeData[] = "lalala";
class FakeNetCopy : public netsvc::NetCopyInterface {
public:
int Open(const char* filename, uint32_t arg, size_t* file_size) override {
if (arg == O_RDONLY) {
*file_size = sizeof(kReadData);
}
return 0;
}
ssize_t Read(void* data_out, std::optional<off_t> offset, size_t max_len) override {
const size_t len = std::min(sizeof(kReadData), max_len);
memcpy(data_out, kReadData, len);
return len;
}
ssize_t Write(const char* data, std::optional<off_t> offset, size_t length) override {
return length;
}
int Close() override { return 0; }
void AbortWrite() override {}
};
class FakeSysinfo : public fidl::WireServer<fuchsia_sysinfo::SysInfo> {
public:
explicit FakeSysinfo(async_dispatcher_t* dispatcher) {
zx::result server_end = fidl::CreateEndpoints(&svc_chan_);
ASSERT_OK(server_end.status_value());
fidl::BindServer(dispatcher, std::move(server_end.value()), this);
}
void GetBoardName(GetBoardNameCompleter::Sync& completer) override {
completer.Reply(ZX_OK, fidl::StringView::FromExternal(board_, sizeof(board_)));
}
void GetBoardRevision(GetBoardRevisionCompleter::Sync& completer) override {
completer.Reply(ZX_OK, 0);
}
void GetBootloaderVendor(GetBootloaderVendorCompleter::Sync& completer) override {
completer.Reply(ZX_OK, fidl::StringView::FromExternal(vendor_, sizeof(vendor_)));
}
void GetInterruptControllerInfo(GetInterruptControllerInfoCompleter::Sync& completer) override {
completer.Reply(ZX_ERR_NOT_SUPPORTED, nullptr);
}
void GetSerialNumber(GetSerialNumberCompleter::Sync& completer) override {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
fidl::ClientEnd<fuchsia_sysinfo::SysInfo>& svc_chan() { return svc_chan_; }
void set_board_name(const char* board) { strlcpy(board_, board, sizeof(board_)); }
void set_bootloader_vendor(const char* vendor) { strlcpy(vendor_, vendor, sizeof(vendor_)); }
private:
fidl::ClientEnd<fuchsia_sysinfo::SysInfo> svc_chan_;
char board_[32] = {};
char vendor_[32] = {};
};
class FileApiTest : public zxtest::Test {
protected:
FileApiTest()
: loop_(&kAsyncLoopConfigNoAttachToCurrentThread),
fake_sysinfo_(loop_.dispatcher()),
file_api_(true, std::make_unique<FakeNetCopy>(), std::move(fake_sysinfo_.svc_chan()),
fake_paver_) {
loop_.StartThread("file-api-test-loop");
}
async::Loop loop_;
FakePaver fake_paver_;
FakeSysinfo fake_sysinfo_;
netsvc::FileApi file_api_;
};
TEST_F(FileApiTest, OpenReadNetCopy) {
ASSERT_EQ(file_api_.OpenRead("file", zx::duration::infinite()), sizeof(kReadData));
file_api_.Close();
}
TEST_F(FileApiTest, OpenReadFailedPave) {
fake_paver_.set_exit_code(ZX_ERR_INTERNAL);
ASSERT_NE(file_api_.OpenRead("file", zx::duration::infinite()), sizeof(kReadData));
}
TEST_F(FileApiTest, OpenWriteNetCopy) {
ASSERT_EQ(file_api_.OpenWrite("file", 10, zx::duration::infinite()), TFTP_NO_ERROR);
file_api_.Close();
}
TEST_F(FileApiTest, OpenWriteBoardName) {
ASSERT_EQ(file_api_.OpenWrite(NETBOOT_BOARD_NAME_FILENAME, 10, zx::duration::infinite()),
TFTP_NO_ERROR);
file_api_.Close();
}
TEST_F(FileApiTest, OpenWritePaver) {
ASSERT_EQ(file_api_.OpenWrite(NETBOOT_IMAGE_PREFIX, 10, zx::duration::infinite()), TFTP_NO_ERROR);
file_api_.Close();
}
TEST_F(FileApiTest, OpenWriteWhilePaving) {
ASSERT_EQ(file_api_.OpenWrite(NETBOOT_IMAGE_PREFIX, 10, zx::duration::infinite()), TFTP_NO_ERROR);
ASSERT_NE(file_api_.OpenWrite(NETBOOT_IMAGE_PREFIX, 10, zx::duration::infinite()), TFTP_NO_ERROR);
file_api_.Close();
}
TEST_F(FileApiTest, OpenReadWhilePaving) {
ASSERT_EQ(file_api_.OpenWrite(NETBOOT_IMAGE_PREFIX, 10, zx::duration::infinite()), TFTP_NO_ERROR);
ASSERT_LT(file_api_.OpenRead("file", zx::duration::infinite()), 0);
file_api_.Close();
}
TEST_F(FileApiTest, OpenWriteFailedPave) {
fake_paver_.set_exit_code(ZX_ERR_INTERNAL);
ASSERT_NE(file_api_.OpenWrite("file", 10, zx::duration::infinite()), TFTP_NO_ERROR);
}
TEST_F(FileApiTest, WriteNetCopy) {
ASSERT_EQ(file_api_.OpenWrite("file", 10, zx::duration::infinite()), TFTP_NO_ERROR);
size_t len = sizeof(kFakeData);
ASSERT_EQ(file_api_.Write(kFakeData, &len, 0), TFTP_NO_ERROR);
ASSERT_EQ(len, sizeof(kFakeData));
file_api_.Close();
}
TEST_F(FileApiTest, WriteBoardName) {
fake_sysinfo_.set_board_name(kFakeData);
ASSERT_EQ(file_api_.OpenWrite(NETBOOT_BOARD_NAME_FILENAME, 10, zx::duration::infinite()),
TFTP_NO_ERROR);
#if __x86_64__
// We hardcode x64 to return "x64" no matter what sysinfo returns.
constexpr char kBoardName[] = "x64";
size_t len = sizeof(kBoardName);
ASSERT_EQ(file_api_.Write(kBoardName, &len, 0), TFTP_NO_ERROR);
ASSERT_EQ(len, sizeof(kBoardName));
#else
size_t len = sizeof(kFakeData);
ASSERT_EQ(file_api_.Write(kFakeData, &len, 0), TFTP_NO_ERROR);
ASSERT_EQ(len, sizeof(kFakeData));
#endif
file_api_.Close();
}
TEST_F(FileApiTest, WriteWrongBoardName) {
fake_sysinfo_.set_board_name("other");
ASSERT_EQ(file_api_.OpenWrite(NETBOOT_BOARD_NAME_FILENAME, 10, zx::duration::infinite()),
TFTP_NO_ERROR);
size_t len = sizeof(kFakeData);
ASSERT_NE(file_api_.Write(kFakeData, &len, 0), TFTP_NO_ERROR);
file_api_.Close();
}
TEST_F(FileApiTest, ReadBoardInfo) {
fake_sysinfo_.set_board_name(kFakeData);
netboot_board_info_t board_info = {};
size_t len = sizeof(board_info);
ASSERT_EQ(file_api_.OpenRead(NETBOOT_BOARD_INFO_FILENAME, zx::duration::infinite()), len);
ASSERT_EQ(file_api_.Read(&board_info, &len, 0), TFTP_NO_ERROR);
ASSERT_EQ(len, sizeof(board_info));
#if __x86_64__
// We hardcode x64 to return "x64" no matter what sysinfo returns.
constexpr char kBoardName[] = "x64";
ASSERT_BYTES_EQ(board_info.board_name, kBoardName, sizeof(kBoardName));
#else
ASSERT_BYTES_EQ(board_info.board_name, kFakeData, sizeof(kFakeData));
#endif
file_api_.Close();
}
TEST_F(FileApiTest, WritePaver) {
ASSERT_EQ(file_api_.OpenWrite(NETBOOT_IMAGE_PREFIX, 10, zx::duration::infinite()), TFTP_NO_ERROR);
size_t len = sizeof(kFakeData);
ASSERT_EQ(file_api_.Write(kFakeData, &len, 0), TFTP_NO_ERROR);
ASSERT_EQ(len, sizeof(kFakeData));
file_api_.Close();
}
TEST_F(FileApiTest, WriteAfterClose) {
ASSERT_EQ(file_api_.OpenWrite("file", 10, zx::duration::infinite()), TFTP_NO_ERROR);
file_api_.Close();
size_t len = sizeof(kFakeData);
ASSERT_NE(file_api_.Write(kFakeData, &len, 0), TFTP_NO_ERROR);
}
TEST_F(FileApiTest, WriteNoLength) {
ASSERT_EQ(file_api_.OpenWrite(NETBOOT_IMAGE_PREFIX, 10, zx::duration::infinite()), TFTP_NO_ERROR);
ASSERT_NE(file_api_.Write(kFakeData, nullptr, 0), TFTP_NO_ERROR);
file_api_.Close();
}
TEST_F(FileApiTest, WriteWithoutOpen) {
size_t len = sizeof(kFakeData);
ASSERT_NE(file_api_.Write(kFakeData, &len, 0), TFTP_NO_ERROR);
}
TEST_F(FileApiTest, AbortNetCopyWrite) {
ASSERT_EQ(file_api_.OpenWrite("file", 10, zx::duration::infinite()), TFTP_NO_ERROR);
size_t len = sizeof(kFakeData);
ASSERT_EQ(file_api_.Write(kFakeData, &len, 0), TFTP_NO_ERROR);
ASSERT_EQ(len, sizeof(kFakeData));
file_api_.Abort();
}
} // namespace