| // 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_ |