blob: 2ff8b187091d446fdb992472c9a2aad10d8cc0c7 [file] [log] [blame]
// Copyright 2022 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 <lib/elfldltl/fd.h>
#include <lib/elfldltl/file.h>
#include <lib/elfldltl/memory.h>
#include <lib/elfldltl/testing/diagnostics.h>
#include <stdio.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#ifdef __Fuchsia__
#include <lib/elfldltl/vmo.h>
#include <lib/zx/vmo.h>
#endif
namespace {
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::Optional;
class TestFdFile : public ::testing::Test {
protected:
static constexpr bool kDestroysHandle = false;
static constexpr elfldltl::PosixError kInvalidFdError{EBADF};
template <typename Diagnostics>
using FileT = elfldltl::FdFile<Diagnostics>;
TestFdFile() {
f_ = tmpfile();
EXPECT_NE(f_, nullptr);
}
~TestFdFile() {
if (f_) {
fclose(f_);
}
}
void Write(const void* p, size_t size) {
EXPECT_EQ(fwrite(p, 1, size, f_), size);
EXPECT_EQ(fflush(f_), 0);
}
int GetHandle() const { return fileno(f_); }
private:
FILE* f_;
};
class TestUniqueFdFile : public TestFdFile {
protected:
static constexpr bool kDestroysHandle = true;
template <typename Diagnostics>
using FileT = elfldltl::UniqueFdFile<Diagnostics>;
fbl::unique_fd GetHandle() { return fbl::unique_fd{TestFdFile::GetHandle()}; }
};
#ifdef __Fuchsia__
class TestVmoFile : public ::testing::Test {
protected:
static constexpr bool kDestroysHandle = true;
static constexpr elfldltl::ZirconError kInvalidFdError{ZX_ERR_BAD_HANDLE};
template <typename Diagnostics>
using FileT = elfldltl::VmoFile<Diagnostics>;
// The fixture starts with an empty VMO so that reads on GetHandle() will
// fail with ZX_ERR_OUT_OF_RANGE rather than ZX_ERR_BAD_HANDLE.
TestVmoFile() { EXPECT_EQ(zx::vmo::create(0, 0, &vmo_), ZX_OK); }
void Write(const void* p, size_t size) {
// This replaces the empty VMO.
ASSERT_EQ(zx::vmo::create(size, 0, &vmo_), ZX_OK);
ASSERT_EQ(vmo_.write(p, 0, size), ZX_OK);
}
zx::vmo GetHandle() { return std::move(vmo_); }
zx::unowned_vmo Borrow() { return vmo_.borrow(); }
private:
zx::vmo vmo_;
};
class TestUnownedVmoFile : public TestVmoFile {
protected:
static constexpr bool kDestroysHandle = false;
template <typename Diagnostics>
using FileT = elfldltl::UnownedVmoFile<Diagnostics>;
zx::unowned_vmo GetHandle() { return Borrow(); }
};
#endif // __Fuchsia__
using FileTypes = ::testing::Types<
#ifdef __Fuchsia__
TestVmoFile, TestUnownedVmoFile,
#endif
TestFdFile, TestUniqueFdFile>;
using ExpectOkDiagnosticsType = decltype(elfldltl::testing::ExpectOkDiagnostics());
template <class TestFile>
class ElfldltlFileTests : public TestFile {
public:
using OkFileT = typename TestFile::template FileT<ExpectOkDiagnosticsType>;
using offset_type = typename OkFileT::offset_type;
using unsigned_offset_type = std::make_unsigned_t<offset_type>;
static constexpr unsigned_offset_type kZeroOffset = 0;
static constexpr elfldltl::FileOffset kZeroFileOffset{kZeroOffset};
static constexpr auto MakeExpectedInvalidFd() {
return elfldltl::testing::ExpectedSingleError{
"cannot read ", sizeof(int), " bytes", kZeroFileOffset, ": ", TestFile::kInvalidFdError,
};
}
static constexpr auto MakeExpectedEof() {
return elfldltl::testing::ExpectedSingleError{
"cannot read ", sizeof(int), " bytes", kZeroFileOffset, ": ", "reached end of file",
};
}
using InvalidFdDiagnostics = decltype(MakeExpectedInvalidFd());
using InvalidFdFileT = typename TestFile::template FileT<InvalidFdDiagnostics>;
using EofDiagnostics = decltype(MakeExpectedEof());
using EofFileT = typename TestFile::template FileT<EofDiagnostics>;
};
TYPED_TEST_SUITE(ElfldltlFileTests, FileTypes);
TYPED_TEST(ElfldltlFileTests, InvalidFd) {
auto expected = TestFixture::MakeExpectedInvalidFd();
typename TestFixture::InvalidFdFileT file{expected};
std::optional<int> got = file.template ReadFromFile<int>(0);
EXPECT_FALSE(got);
}
TYPED_TEST(ElfldltlFileTests, Eof) {
auto expected = TestFixture::MakeExpectedEof();
typename TestFixture::EofFileT file{this->GetHandle(), expected};
std::optional<int> got = file.template ReadFromFile<int>(0);
EXPECT_EQ(got, std::nullopt);
}
TYPED_TEST(ElfldltlFileTests, ReadFromFile) {
int i = 123;
this->Write(&i, sizeof(i));
auto diag = elfldltl::testing::ExpectOkDiagnostics();
typename TestFixture::OkFileT file{this->GetHandle(), diag};
std::optional<int> got = file.template ReadFromFile<int>(0);
EXPECT_THAT(got, Optional(Eq(i)));
}
TYPED_TEST(ElfldltlFileTests, ReadArrayFromFile) {
constexpr int kData[] = {123, 456, 789};
this->Write(kData, sizeof(kData));
auto diag = elfldltl::testing::ExpectOkDiagnostics();
typename TestFixture::OkFileT file{this->GetHandle(), diag};
elfldltl::FixedArrayFromFile<int, 3> allocator;
auto got = file.template ReadArrayFromFile<int>(0, allocator, 3);
ASSERT_TRUE(got);
cpp20::span<const int> data = *got;
EXPECT_THAT(data, ElementsAreArray(kData));
}
TYPED_TEST(ElfldltlFileTests, Assignment) {
auto test_assignment = [this](auto&& diag) {
using Diagnostics = std::decay_t<decltype(diag)>;
using FileT = typename TestFixture::template FileT<Diagnostics>;
int i = 123;
this->Write(&i, sizeof(i));
FileT file{this->GetHandle(), diag};
EXPECT_THAT(file.template ReadFromFile<int>(0), Optional(Eq(i)));
if constexpr (std::is_copy_assignable_v<decltype(file)>) {
auto other = file;
EXPECT_THAT(other.template ReadFromFile<int>(0), Optional(Eq(i)));
}
EXPECT_THAT(file.template ReadFromFile<int>(0), Optional(Eq(i)));
{
auto other = std::move(file);
EXPECT_THAT(other.template ReadFromFile<int>(0), Optional(Eq(i)));
}
if constexpr (TestFixture::kDestroysHandle) {
EXPECT_FALSE(file.template ReadFromFile<int>(0).has_value());
}
};
if constexpr (TestFixture::kDestroysHandle) {
test_assignment(TestFixture::MakeExpectedInvalidFd());
} else {
test_assignment(elfldltl::testing::ExpectOkDiagnostics());
}
}
} // anonymous namespace