| // 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 <fuchsia/sysinfo/llcpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/fidl-async/cpp/bind.h> |
| |
| #include <memory> |
| #include <string_view> |
| |
| #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(std::string_view 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 fidl::WireInterface<fuchsia_sysinfo::SysInfo> { |
| public: |
| FakeSysinfo(async_dispatcher_t* dispatcher) { |
| zx::channel remote; |
| ASSERT_OK(zx::channel::create(0, &remote, &svc_chan_)); |
| fidl::BindSingleInFlightOnly(dispatcher, std::move(remote), this); |
| } |
| |
| void GetBoardName(GetBoardNameCompleter::Sync& completer) { |
| completer.Reply(ZX_OK, fidl::StringView{board_, sizeof(board_)}); |
| } |
| |
| void GetBoardRevision(GetBoardRevisionCompleter::Sync& completer) { completer.Reply(ZX_OK, 0); } |
| |
| void GetBootloaderVendor(GetBootloaderVendorCompleter::Sync& completer) { |
| completer.Reply(ZX_OK, fidl::StringView{vendor_, sizeof(vendor_)}); |
| } |
| |
| void GetInterruptControllerInfo(GetInterruptControllerInfoCompleter::Sync& completer) { |
| completer.Reply(ZX_ERR_NOT_SUPPORTED, nullptr); |
| } |
| |
| zx::channel& 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: |
| zx::channel svc_chan_; |
| |
| char board_[32] = {}; |
| char vendor_[32] = {}; |
| }; |
| |
| } // namespace |
| |
| 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"), 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); |
| #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(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, ReadBoardInfo) { |
| fake_sysinfo_.set_board_name(kFakeData); |
| board_info_t board_info = {}; |
| size_t len = sizeof(board_info); |
| ASSERT_EQ(file_api_.OpenRead(NB_BOARD_INFO_FILENAME), 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(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(); |
| } |