blob: c4ffb745941aa796b8fc978d1781426ab548f2b3 [file] [log] [blame]
// Copyright 2020 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 ZIRCON_SYSTEM_ULIB_ZBITL_TEST_TESTS_H_
#define ZIRCON_SYSTEM_ULIB_ZBITL_TEST_TESTS_H_
#include <lib/zbitl/json.h>
#include <lib/zbitl/view.h>
#include <string>
#include <type_traits>
#include <zxtest/zxtest.h>
#include "corpus.h"
#define ASSERT_IS_OK(result) \
do { \
const auto& result_ = result; \
ASSERT_TRUE(result_.is_ok(), "unexpected error: %.*s", \
static_cast<int>(result_.error_value().zbi_error.size()), \
result_.error_value().zbi_error.data()); \
} while (0)
// Usage of StorageIo below is a default-constructible class that should look
// like
// * a namespace member of `storage_type`, giving the underlying storage type.
// * a means of creating a 'ZBI' of that type from a string representation:
// ```
// void Create(std::string_view, storage_type* zbi)
// ```
// * a means of reading the payload of an item:
// ```
// void ReadPayload(const storage_type& zbi, const zbi_header_t& header, payload_type payload)
// ```
// (where payload_type is that of the official storage traits associated with
// storage_type.)
template <typename StorageIo>
inline void TestDefaultConstructedView(bool expect_storage_error) {
static_assert(std::is_default_constructible_v<typename StorageIo::storage_type>,
"this test case only applies to default-constructible storage types");
zbitl::View<typename StorageIo::storage_type> view;
// This ensures that everything statically compiles when instantiating the
// templates, even though the header/payloads are never used.
for (auto [header, payload] : view) {
EXPECT_EQ(header->flags, header->flags);
EXPECT_TRUE(false, "should not be reached");
}
auto error = view.take_error();
ASSERT_TRUE(error.is_error(), "no error when header cannot be read??");
EXPECT_FALSE(error.error_value().zbi_error.empty(), "empty zbi_error string!!");
if (expect_storage_error) {
EXPECT_TRUE(error.error_value().storage_error.has_value());
} else {
EXPECT_FALSE(error.error_value().storage_error.has_value());
}
}
template <typename StorageIo>
inline void TestEmptyZbi() {
StorageIo io;
typename StorageIo::storage_type zbi;
ASSERT_NO_FATAL_FAILURES(
io.Create({zbitl::test::kEmptyZbi, sizeof(zbitl::test::kEmptyZbi)}, &zbi));
zbitl::View view(std::move(zbi)); // Yay deduction guides!
ASSERT_IS_OK(view.container_header());
EXPECT_EQ(view.end(), view.begin());
for (auto [header, payload] : view) {
EXPECT_EQ(header->type, header->type);
EXPECT_TRUE(false, "should not be reached");
}
auto error = view.take_error();
EXPECT_FALSE(error.is_error(), "%s at offset %#x",
std::string(error.error_value().zbi_error).c_str(), // No '\0'.
error.error_value().item_offset);
}
template <typename StorageIo>
inline void TestSimpleZbi() {
StorageIo io;
typename StorageIo::storage_type zbi;
ASSERT_NO_FATAL_FAILURES(
io.Create({zbitl::test::kSimpleZbi, sizeof(zbitl::test::kSimpleZbi)}, &zbi));
zbitl::View view(std::move(zbi));
ASSERT_IS_OK(view.container_header());
size_t num_items = 0;
for (auto [header, payload] : view) {
EXPECT_EQ(ZBI_TYPE_CMDLINE, header->type);
std::string contents;
ASSERT_NO_FATAL_FAILURES(io.ReadPayload(view.storage(), *header, payload, &contents));
switch (num_items++) {
case 0:
EXPECT_STR_EQ("hello world", contents.c_str(), "payload: %s", contents.c_str());
break;
}
const uint32_t flags = header->flags;
EXPECT_TRUE(flags & ZBI_FLAG_VERSION, "flags: %#x", flags);
}
EXPECT_EQ(1, num_items);
auto error = view.take_error();
EXPECT_FALSE(error.is_error(), "%s at offset %#x",
std::string(error.error_value().zbi_error).c_str(), // No '\0'.
error.error_value().item_offset);
}
template <typename StorageIo>
void TestBadCrcZbi() {
StorageIo io;
typename StorageIo::storage_type zbi;
ASSERT_NO_FATAL_FAILURES(
io.Create({zbitl::test::kBadCrcZbi, sizeof(zbitl::test::kBadCrcZbi)}, &zbi));
zbitl::View<typename StorageIo::storage_type, zbitl::Checking::kCrc> view(std::move(zbi));
ASSERT_IS_OK(view.container_header());
for (auto [header, payload] : view) {
EXPECT_EQ(header->type, header->type);
EXPECT_TRUE(false, "should not be reached");
}
auto error = view.take_error();
ASSERT_TRUE(error.is_error());
// The error shouldn't be one of storage.
EXPECT_FALSE(error.error_value().storage_error);
}
template <typename StorageIo>
void TestMutation() {
StorageIo io;
typename StorageIo::storage_type zbi;
ASSERT_NO_FATAL_FAILURES(
io.Create({zbitl::test::kSimpleZbi, sizeof(zbitl::test::kSimpleZbi)}, &zbi));
zbitl::View view(std::move(zbi));
ASSERT_IS_OK(view.container_header());
size_t num_items = 0;
for (auto it = view.begin(); it != view.end(); ++it) {
auto [header, payload] = *it;
EXPECT_EQ(ZBI_TYPE_CMDLINE, header->type);
std::string contents;
ASSERT_NO_FATAL_FAILURES(io.ReadPayload(view.storage(), *header, payload, &contents));
switch (num_items++) {
case 0:
EXPECT_STR_EQ("hello world", contents.c_str());
ASSERT_TRUE(it.Replace({.type = ZBI_TYPE_DISCARD}).is_ok());
break;
}
}
EXPECT_EQ(1, num_items);
{
auto error = view.take_error();
EXPECT_FALSE(error.is_error(), "%s at offset %#x",
std::string(error.error_value().zbi_error).c_str(), // No '\0'.
error.error_value().item_offset);
}
num_items = 0;
for (auto [header, payload] : view) {
EXPECT_EQ(ZBI_TYPE_DISCARD, header->type);
std::string contents;
ASSERT_NO_FATAL_FAILURES(io.ReadPayload(view.storage(), *header, payload, &contents));
switch (num_items++) {
case 0:
EXPECT_STR_EQ("hello world", contents.c_str());
break;
}
}
EXPECT_EQ(1, num_items);
{
auto error = view.take_error();
EXPECT_FALSE(error.is_error(), "%s at offset %#x",
std::string(error.error_value().zbi_error).c_str(), // No '\0'.
error.error_value().item_offset);
}
}
#endif // ZIRCON_SYSTEM_ULIB_ZBITL_TEST_TESTS_H_