blob: fd1ce02f7dda96b2eebc996f72577fac8f7cbc20 [file] [log] [blame] [edit]
// Copyright 2021 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.
#ifndef SRC_LIB_ZBITL_TESTS_BOOTFS_TESTS_H_
#define SRC_LIB_ZBITL_TESTS_BOOTFS_TESTS_H_
#include <lib/zbitl/items/bootfs.h>
#include <lib/zbitl/view.h>
#include <iterator>
#include <fbl/unique_fd.h>
#include <gtest/gtest.h>
#include "array-tests.h"
#include "fd-tests.h"
#include "src/lib/files/scoped_temp_dir.h"
#include "tests.h"
template <typename TestTraits>
void CreateBootfs(typename TestTraits::Context& context,
zbitl::Bootfs<typename TestTraits::storage_type>& bootfs) {
using Storage = typename TestTraits::storage_type;
files::ScopedTempDir dir;
fbl::unique_fd fd;
size_t size = 0;
ASSERT_NO_FATAL_FAILURE(OpenTestDataZbi(TestDataZbiType::kBootfs, dir.path(), &fd, &size));
// Read the ZBI containing the BOOTFS into memory.
typename FblByteArrayTestTraits::Context zbi_context;
ASSERT_NO_FATAL_FAILURE(FblByteArrayTestTraits::Create(std::move(fd), size, &zbi_context));
zbitl::View view(zbi_context.TakeStorage());
auto it = view.begin();
ASSERT_EQ(std::next(it), view.end()) << "expected a single BOOTFS item";
ASSERT_EQ(uint32_t{ZBI_TYPE_STORAGE_BOOTFS}, it->header->type);
// Ultimately we want to create an object of storage_type containing the
// BOOTFS - and the preferred choice of test traits for creating storage
// objects with prescribed contents is to use a fbl::unique_fd. Accordingly,
// we decompress the BOOTFS into this form.
const uint32_t bootfs_size = zbitl::UncompressedLength(*it->header);
typename FdTestTraits::Context decompressed_context;
ASSERT_NO_FATAL_FAILURE(FdTestTraits::Create(bootfs_size, &decompressed_context));
fbl::unique_fd bootfs_fd = decompressed_context.TakeStorage();
{
auto result = view.CopyStorageItem(bootfs_fd, it);
ASSERT_FALSE(result.is_error()) << ViewCopyErrorString(result.error_value());
}
ASSERT_NO_FATAL_FAILURE(TestTraits::Create(std::move(bootfs_fd), bootfs_size, &context));
{
auto result = zbitl::Bootfs<Storage>::Create(context.TakeStorage());
ASSERT_FALSE(result.is_error()) << BootfsErrorString(result.error_value());
bootfs = std::move(result.value());
}
{
auto result = view.take_error();
EXPECT_FALSE(result.is_error()) << ViewErrorString(result.error_value());
}
}
template <typename BootfsView>
void TestFind(BootfsView& bootfs, std::initializer_list<std::string_view> path_parts,
typename BootfsView::iterator expected_it) {
auto match = bootfs.find(path_parts);
auto result = bootfs.take_error();
ASSERT_FALSE(result.is_error()) << BootfsErrorString(result.error_value());
EXPECT_EQ(expected_it, match);
}
template <typename TestTraits>
void TestBootfsIteration() {
using namespace std::string_view_literals;
using Storage = typename TestTraits::storage_type;
typename TestTraits::Context context;
zbitl::Bootfs<Storage> reader;
ASSERT_NO_FATAL_FAILURE(CreateBootfs<TestTraits>(context, reader));
auto bootfs = reader.root();
uint32_t idx = 0;
for (auto it = bootfs.begin(); it != bootfs.end(); ++it) {
Bytes contents;
ASSERT_NO_FATAL_FAILURE(TestTraits::Read(reader.storage(), it->data, it->size, &contents));
ASSERT_NO_FATAL_FAILURE(TestFind(bootfs, {it->name}, it));
ASSERT_NO_FATAL_FAILURE(TestFind(bootfs, {it->name}, it));
switch (idx) {
case 0:
EXPECT_EQ(it->name, "A.txt"sv);
EXPECT_EQ(contents,
"Four score and seven years ago our fathers brought forth on this "
"continent, a new nation, conceived in Liberty, and dedicated to the "
"proposition that all men are created equal.");
break;
case 1:
EXPECT_EQ(it->name, "nested/B.txt"sv);
ASSERT_NO_FATAL_FAILURE(TestFind(bootfs, {"nested"sv, "B.txt"sv}, it));
EXPECT_EQ(contents,
"Now we are engaged in a great civil war, testing whether that nation, "
"or any nation so conceived and so dedicated, can long endure.");
break;
case 2:
EXPECT_EQ(it->name, "nested/again/C.txt"sv);
ASSERT_NO_FATAL_FAILURE(TestFind(bootfs, {"nested/again"sv, "C.txt"sv}, it));
ASSERT_NO_FATAL_FAILURE(TestFind(bootfs, {"nested"sv, "again/C.txt"sv}, it));
ASSERT_NO_FATAL_FAILURE(TestFind(bootfs, {"nested"sv, "again"sv, "C.txt"sv}, it));
EXPECT_EQ(contents, "We are met on a great battle-field of that war.");
break;
default:
__UNREACHABLE;
}
++idx;
}
EXPECT_EQ(3u, idx) << "we expect three files in the BOOTFS";
{
auto result = bootfs.take_error();
EXPECT_FALSE(result.is_error()) << BootfsErrorString(result.error_value());
}
}
template <typename TestTraits>
void TestBootfsSubdirectory() {
using namespace std::string_view_literals;
using Storage = typename TestTraits::storage_type;
typename TestTraits::Context context;
zbitl::Bootfs<Storage> reader;
ASSERT_NO_FATAL_FAILURE(CreateBootfs<TestTraits>(context, reader));
zbitl::BootfsView<Storage> root = reader.root();
{
auto result = root.subdir("nonexistent/directory"sv);
ASSERT_TRUE(result.is_error());
EXPECT_EQ("unknown directory"sv, result.error_value().reason);
}
constexpr std::string_view kErrFileNotDir = "provided name is for a file, not a directory";
{
auto result = root.subdir("A.txt"sv);
ASSERT_TRUE(result.is_error());
EXPECT_EQ(kErrFileNotDir, result.error_value().reason);
}
{
auto result = root.subdir("nested/B.txt"sv);
ASSERT_TRUE(result.is_error());
EXPECT_EQ(kErrFileNotDir, result.error_value().reason);
}
{
auto result = root.subdir("nested/again/C.txt"sv);
ASSERT_TRUE(result.is_error());
EXPECT_EQ(kErrFileNotDir, result.error_value().reason);
}
{
auto result = root.subdir("nested"sv);
ASSERT_FALSE(result.is_error()) << BootfsErrorString(result.error_value());
auto subdir = std::move(result).value();
EXPECT_EQ("nested"sv, subdir.directory());
uint32_t idx = 0u;
for (auto it = subdir.begin(); it != subdir.end(); ++it) {
ASSERT_NO_FATAL_FAILURE(TestFind(subdir, {it->name}, it));
switch (idx++) {
case 0u:
EXPECT_EQ(it->name, "B.txt"sv);
break;
case 1u:
EXPECT_EQ(it->name, "again/C.txt"sv);
ASSERT_NO_FATAL_FAILURE(TestFind(subdir, {"again"sv, "C.txt"sv}, it));
break;
}
}
EXPECT_EQ(2u, idx) << "expected two files in the BOOTFS subdirectory";
auto take_result = subdir.take_error();
EXPECT_FALSE(take_result.is_error()) << BootfsErrorString(take_result.error_value());
}
{
auto result = root.subdir("nested/again"sv);
ASSERT_FALSE(result.is_error()) << BootfsErrorString(result.error_value());
auto subdir = std::move(result).value();
EXPECT_EQ("nested/again"sv, subdir.directory());
uint32_t idx = 0u;
for (auto it = subdir.begin(); it != subdir.end(); ++it) {
ASSERT_NO_FATAL_FAILURE(TestFind(subdir, {it->name}, it));
switch (idx++) {
case 0u:
EXPECT_EQ(it->name, "C.txt"sv);
break;
}
}
EXPECT_EQ(1u, idx) << "expected one files in the BOOTFS subdirectory";
auto take_result = subdir.take_error();
EXPECT_FALSE(take_result.is_error()) << BootfsErrorString(take_result.error_value());
}
}
#endif // SRC_LIB_ZBITL_TESTS_BOOTFS_TESTS_H_