blob: 0fa654135439b9761bda0a064abd79f8986581bb [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 "file-api.h"
#include <memory>
#include <fcntl.h>
#include <fuchsia/sysinfo/c/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/fidl-utils/bind.h>
#include <zxtest/zxtest.h>
namespace {
class FakePaver : public netsvc::PaverInterface {
public:
bool InProgress() override { return in_progress_; }
zx_status_t exit_code() override { return exit_code_; }
void reset_exit_code() override { exit_code_ = ZX_OK; }
tftp_status OpenWrite(const char* filename, size_t size) override {
in_progress_ = true;
return TFTP_NO_ERROR;
}
tftp_status Write(const void* data, size_t* length, off_t offset) override {
if (!in_progress_) {
return TFTP_ERR_INTERNAL;
}
exit_code_ = ZX_OK;
return TFTP_NO_ERROR;
}
void Close() override {
in_progress_ = false;
}
void set_exit_code(zx_status_t exit_code) {
exit_code_ = exit_code;
}
private:
bool in_progress_ = false;
zx_status_t exit_code_ = ZX_OK;
};
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:
FakeSysinfo(async_dispatcher_t* dispatcher) {
zx::channel remote;
ASSERT_OK(zx::channel::create(0, &remote, &svc_chan_));
fidl_bind(dispatcher, remote.release(),
reinterpret_cast<fidl_dispatch_t*>(fuchsia_sysinfo_Device_dispatch),
this, &ops_);
}
zx_status_t GetRootJob(fidl_txn_t* txn) {
return fuchsia_sysinfo_DeviceGetRootJob_reply(txn, ZX_ERR_NOT_SUPPORTED, ZX_HANDLE_INVALID);
}
zx_status_t GetRootResource(fidl_txn_t* txn) {
return fuchsia_sysinfo_DeviceGetRootResource_reply(txn, ZX_ERR_NOT_SUPPORTED,
ZX_HANDLE_INVALID);
}
zx_status_t GetHypervisorResource(fidl_txn_t* txn) {
return fuchsia_sysinfo_DeviceGetHypervisorResource_reply(txn, ZX_ERR_NOT_SUPPORTED,
ZX_HANDLE_INVALID);
}
zx_status_t GetBoardName(fidl_txn_t* txn) {
return fuchsia_sysinfo_DeviceGetBoardName_reply(txn, ZX_OK, board_, 32);
}
zx_status_t GetInterruptControllerInfo(fidl_txn_t* txn) {
return fuchsia_sysinfo_DeviceGetInterruptControllerInfo_reply(txn, ZX_ERR_NOT_SUPPORTED,
nullptr);
}
zx::channel& svc_chan() { return svc_chan_; }
void set_board_name(const char* board) {
strncpy(board_, board, 32);
}
private:
using Binder = fidl::Binder<FakeSysinfo>;
zx::channel svc_chan_;
char board_[32] = {};
static constexpr fuchsia_sysinfo_Device_ops_t ops_ = {
.GetRootJob = Binder::BindMember<&FakeSysinfo::GetRootJob>,
.GetRootResource = Binder::BindMember<&FakeSysinfo::GetRootResource>,
.GetHypervisorResource = Binder::BindMember<&FakeSysinfo::GetHypervisorResource>,
.GetBoardName = Binder::BindMember<&FakeSysinfo::GetBoardName>,
.GetInterruptControllerInfo = Binder::BindMember<&FakeSysinfo::GetInterruptControllerInfo>,
};
};
} // namespace
class FileApiTest : public zxtest::Test {
protected:
FileApiTest()
: loop_(&kAsyncLoopConfigNoAttachToThread),
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"), sizeof(kReadData));
file_api_.Close();
}
TEST_F(FileApiTest, OpenReadFailedPave) {
fake_paver_.set_exit_code(ZX_ERR_INTERNAL);
ASSERT_NE(file_api_.OpenRead("file"), sizeof(kReadData));
}
TEST_F(FileApiTest, OpenWriteNetCopy) {
ASSERT_EQ(file_api_.OpenWrite("file", 10), TFTP_NO_ERROR);
file_api_.Close();
}
TEST_F(FileApiTest, OpenWriteBoardName) {
ASSERT_EQ(file_api_.OpenWrite(NB_BOARD_NAME_FILENAME, 10), TFTP_NO_ERROR);
file_api_.Close();
}
TEST_F(FileApiTest, OpenWritePaver) {
ASSERT_EQ(file_api_.OpenWrite(NB_IMAGE_PREFIX, 10), TFTP_NO_ERROR);
file_api_.Close();
}
TEST_F(FileApiTest, OpenWriteWhilePaving) {
ASSERT_EQ(file_api_.OpenWrite(NB_IMAGE_PREFIX, 10), TFTP_NO_ERROR);
ASSERT_NE(file_api_.OpenWrite(NB_IMAGE_PREFIX, 10), TFTP_NO_ERROR);
file_api_.Close();
}
TEST_F(FileApiTest, OpenReadWhilePaving) {
ASSERT_EQ(file_api_.OpenWrite(NB_IMAGE_PREFIX, 10), TFTP_NO_ERROR);
ASSERT_LT(file_api_.OpenRead("file"), 0);
file_api_.Close();
}
TEST_F(FileApiTest, OpenWriteFailedPave) {
fake_paver_.set_exit_code(ZX_ERR_INTERNAL);
ASSERT_NE(file_api_.OpenWrite("file", 10), TFTP_NO_ERROR);
}
TEST_F(FileApiTest, WriteNetCopy) {
ASSERT_EQ(file_api_.OpenWrite("file", 10), 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(NB_BOARD_NAME_FILENAME, 10), 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, WriteWrongBoardName) {
fake_sysinfo_.set_board_name("other");
ASSERT_EQ(file_api_.OpenWrite(NB_BOARD_NAME_FILENAME, 10), 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, WritePaver) {
ASSERT_EQ(file_api_.OpenWrite(NB_IMAGE_PREFIX, 10), 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), 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(NB_IMAGE_PREFIX, 10), 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), 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();
}