blob: 3392655a981f3dd33583fdc04bc349cc45411504 [file] [log] [blame] [edit]
// 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 <stdio.h>
#include <gtest/gtest.h>
#include "tests.h"
#ifdef __Fuchsia__
#include <lib/elfldltl/vmo.h>
#include <lib/zx/vmo.h>
#endif
namespace {
struct TestFdFile {
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_;
};
struct TestUniqueFdFile : public TestFdFile {
template <typename Diagnostics>
using FileT = elfldltl::UniqueFdFile<Diagnostics>;
fbl::unique_fd GetHandle() { return fbl::unique_fd{TestFdFile::GetHandle()}; }
};
#ifdef __Fuchsia__
struct TestVmoFile {
template <typename Diagnostics>
using FileT = elfldltl::VmoFile<Diagnostics>;
void Write(const void* p, size_t size) {
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_;
};
struct TestUnownedVmoFile : public TestVmoFile {
template <typename Diagnostics>
using FileT = elfldltl::UnownedVmoFile<Diagnostics>;
zx::unowned_vmo GetHandle() { return Borrow(); }
};
#else // __Fuchsia__
using TestUnownedVmoFile = struct {};
#endif // __Fuchsia__
template <typename Test>
void TestPlatforms(Test&& test) {
test(TestFdFile{});
test(TestUniqueFdFile{});
#ifdef __Fuchsia__
test(TestVmoFile{});
test(TestUnownedVmoFile{});
#endif
}
auto InvalidFd = [](auto&& test_file) {
ExpectedSingleError expected("couldn't read file at offset: ", 0);
using FileT =
typename std::decay_t<decltype(test_file)>::template FileT<decltype(expected.diag())>;
{
FileT file{expected.diag()};
std::optional<int> got = file.template ReadFromFile<int>(0);
EXPECT_FALSE(got);
}
{
FileT file(test_file.GetHandle(), expected.diag());
std::optional<int> got = file.template ReadFromFile<int>(0);
EXPECT_FALSE(got);
}
};
TEST(ElfldltlFileTests, InvalidFd) { TestPlatforms(InvalidFd); }
auto ReadFromFile = [](auto&& test_file) {
auto diag = ExpectOkDiagnostics();
using FileT = typename std::decay_t<decltype(test_file)>::template FileT<decltype(diag)>;
int i = 123;
test_file.Write(&i, sizeof(i));
FileT file{test_file.GetHandle(), diag};
std::optional<int> got = file.template ReadFromFile<int>(0);
EXPECT_EQ(*got, i);
};
TEST(ElfldltlFileTests, ReadFromFile) { TestPlatforms(ReadFromFile); }
auto ReadArrayFromFile = [](auto&& test_file) {
auto diag = ExpectOkDiagnostics();
using FileT = typename std::decay_t<decltype(test_file)>::template FileT<decltype(diag)>;
const std::array<int, 3> data{123, 456, 789};
test_file.Write(data.data(), sizeof(data));
elfldltl::FixedArrayFromFile<int, 3> allocator;
FileT file{test_file.GetHandle(), diag};
auto got = file.template ReadArrayFromFile<int>(0, allocator, 3);
ASSERT_TRUE(got);
cpp20::span<int> span = *got;
ASSERT_EQ(span.size(), data.size());
for (size_t i = 0; i < data.size(); i++) {
EXPECT_EQ(span[i], data[i]);
}
};
TEST(ElfldltlFileTests, ReadArrayFromFile) { TestPlatforms(ReadArrayFromFile); }
auto Assignment = [](auto&& test_file) {
using TestFileT = std::decay_t<std::decay_t<decltype(test_file)>>;
constexpr bool DestroysHandle =
std::is_same_v<TestFileT, TestUniqueFdFile> || std::is_same_v<TestFileT, TestUnownedVmoFile>;
ExpectedSingleError expected("couldn't read file at offset: ", 0);
auto diag = [&] {
if constexpr (DestroysHandle) {
return expected.diag();
} else {
return ExpectOkDiagnostics();
}
}();
using FileT = typename TestFileT::template FileT<decltype(diag)>;
int i = 123;
test_file.Write(&i, sizeof(i));
// If we ever switch to gtest, use EXPECT_THAT(..., Optional(...));
auto expect_optional = [](const auto& optional, const auto& expected) {
ASSERT_TRUE(optional);
EXPECT_EQ(*optional, expected);
};
FileT file{test_file.GetHandle(), diag};
expect_optional(file.template ReadFromFile<int>(0), i);
if constexpr (std::is_copy_assignable_v<FileT>) {
FileT other = file;
expect_optional(other.template ReadFromFile<int>(0), i);
}
expect_optional(file.template ReadFromFile<int>(0), i);
{
FileT other = std::move(file);
expect_optional(other.template ReadFromFile<int>(0), i);
}
if (DestroysHandle) {
EXPECT_FALSE(file.template ReadFromFile<int>(0).has_value());
}
};
TEST(ElfldltlFileTests, Assignment) { TestPlatforms(Assignment); }
} // anonymous namespace