| // 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. | 
 |  | 
 | #include "src/storage/volume_image/address_descriptor.h" | 
 |  | 
 | #include <string> | 
 | #include <string_view> | 
 | #include <vector> | 
 |  | 
 | #include <gmock/gmock.h> | 
 | #include <gtest/gtest.h> | 
 |  | 
 | #include "rapidjson/document.h" | 
 | #include "rapidjson/schema.h" | 
 | #include "rapidjson/writer.h" | 
 | #include "src/lib/json_parser/json_parser.h" | 
 | #include "src/lib/json_parser/rapidjson_validation.h" | 
 | #include "src/storage/volume_image/serialization/schema.h" | 
 |  | 
 | namespace storage::volume_image { | 
 | namespace { | 
 |  | 
 | std::string GetSerializedJson(fit::function<void(rapidjson::Document*)> mutator = nullptr) { | 
 |   constexpr std::string_view kSerializedAddressDescriptor = R"( | 
 |     { | 
 |         "magic": 12526821592682033285, | 
 |         "mappings": [ | 
 |           { | 
 |             "source": 20, | 
 |             "target": 120, | 
 |             "count": 10, | 
 |             "options": {} | 
 |           }, | 
 |           { | 
 |             "source": 250, | 
 |             "target": 160, | 
 |             "count": 10 | 
 |           }, | 
 |           { | 
 |             "source": 2900, | 
 |             "target": 170, | 
 |             "count": 10, | 
 |             "size": 20 | 
 |           } | 
 |         ] | 
 |     })"; | 
 |   json_parser::JSONParser parser; | 
 |   rapidjson::Document parsed_document = parser.ParseFromString( | 
 |       kSerializedAddressDescriptor.data(), "serialized_address_descriptor.json"); | 
 |   ZX_ASSERT_MSG(!parser.HasError(), "%s\n", parser.error_str().c_str()); | 
 |   if (mutator != nullptr) { | 
 |     mutator(&parsed_document); | 
 |   } | 
 |   rapidjson::StringBuffer buffer; | 
 |   rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); | 
 |   parsed_document.Accept(writer); | 
 |   return buffer.GetString(); | 
 | } | 
 |  | 
 | TEST(AddressDescriptorTest, SerializeReturnsSchemaValidData) { | 
 |   AddressDescriptor descriptor = {}; | 
 |   descriptor.mappings = std::vector<AddressMap>({ | 
 |       {.source = 10, | 
 |        .target = 20, | 
 |        .count = 10, | 
 |        .options = {{"random_option_1", 32}, {"random_option_2", 33}}}, | 
 |       {.source = 20, .target = 30, .count = 10}, | 
 |   }); | 
 |   auto schema_json = GetSchema(Schema::kAddressDescriptor); | 
 |   ASSERT_FALSE(schema_json.empty()); | 
 |  | 
 |   json_parser::JSONParser parser; | 
 |   auto result = descriptor.Serialize(); | 
 |   ASSERT_TRUE(result.is_ok()) << result.error(); | 
 |   auto value = result.take_value(); | 
 |   auto document = | 
 |       parser.ParseFromString(std::string(value.begin(), value.end()), "serialized.json"); | 
 |   ASSERT_FALSE(parser.HasError()) << parser.error_str(); | 
 |   auto schema_result = json_parser::InitSchema(schema_json); | 
 |   ASSERT_TRUE(schema_result.is_ok()) << schema_result.error_value().ToString(); | 
 |   auto validation_result = json_parser::ValidateSchema(document, schema_result.value()); | 
 |   EXPECT_TRUE(validation_result.is_ok()) << validation_result.error_value(); | 
 | } | 
 |  | 
 | MATCHER(AddressMapEq, "") { | 
 |   auto [a, b] = arg; | 
 |   return a.source == b.source && a.target == b.target && a.count == b.count && a.size == b.size; | 
 | } | 
 |  | 
 | TEST(AddressDescriptorTest, DeserializeSerializedDataIsOk) { | 
 |   const auto deserialized_result = AddressDescriptor::Deserialize(GetSerializedJson()); | 
 |   ASSERT_TRUE(deserialized_result.is_ok()); | 
 |  | 
 |   const auto serialized_result = deserialized_result.value().Serialize(); | 
 |   ASSERT_TRUE(serialized_result.is_ok()); | 
 |  | 
 |   const auto redeserialized_result = AddressDescriptor::Deserialize(serialized_result.value()); | 
 |   ASSERT_TRUE(redeserialized_result.is_ok()); | 
 |  | 
 |   const auto& deserialized_1 = deserialized_result.value(); | 
 |   const auto& deserialized_2 = deserialized_result.value(); | 
 |  | 
 |   ASSERT_THAT(deserialized_1.mappings, | 
 |               testing::UnorderedPointwise(AddressMapEq(), deserialized_2.mappings)); | 
 | } | 
 |  | 
 | TEST(AddressDescriptorTest, DeserializeFromValidDataReturnsAddressDescritptor) { | 
 |   const auto deserialized_result = AddressDescriptor::Deserialize(GetSerializedJson()); | 
 |   ASSERT_TRUE(deserialized_result.is_ok()) << deserialized_result.error(); | 
 |  | 
 |   ASSERT_THAT( | 
 |       deserialized_result.value().mappings, | 
 |       testing::UnorderedPointwise( | 
 |           AddressMapEq(), std::vector<AddressMap>({ | 
 |                               {.source = 250, .target = 160, .count = 10, .size = std::nullopt}, | 
 |                               {.source = 20, .target = 120, .count = 10, .size = std::nullopt}, | 
 |                               {.source = 2900, .target = 170, .count = 10, .size = 20}, | 
 |                           }))); | 
 | } | 
 |  | 
 | TEST(AddressMapTest, DebugStringIsOk) { | 
 |   AddressMap map = { | 
 |       .source = 100, | 
 |       .target = 200, | 
 |       .count = 50, | 
 |   }; | 
 |  | 
 |   EXPECT_THAT(map.DebugString(), | 
 |               testing::AllOf(testing::ContainsRegex("source:[ ]+100"), | 
 |                              testing::ContainsRegex("target:[ ]+200"), | 
 |                              testing::ContainsRegex("count:[ ]+50"), | 
 |                              testing::ContainsRegex("size:[ ]+std::nullopt"), | 
 |                              testing::ContainsRegex("options:[ ]+\\{[ \n]*\\}"))); | 
 |  | 
 |   map.size = 150; | 
 |   EXPECT_THAT( | 
 |       map.DebugString(), | 
 |       testing::AllOf(testing::ContainsRegex("source:[ ]+100"), | 
 |                      testing::ContainsRegex("target:[ ]+200"), | 
 |                      testing::ContainsRegex("count:[ ]+50"), testing::ContainsRegex("size:[ ]+150"), | 
 |                      testing::ContainsRegex("options:[ ]+\\{[ \n]*\\}"))); | 
 |  | 
 |   map.options["option_name"] = 1234; | 
 |   map.options["option_name_2"] = 12345; | 
 |   EXPECT_THAT( | 
 |       map.DebugString(), | 
 |       testing::AllOf( | 
 |           testing::ContainsRegex("source:[ ]+100"), testing::ContainsRegex("target:[ ]+200"), | 
 |           testing::ContainsRegex("count:[ ]+50"), testing::ContainsRegex("size:[ ]+150"), | 
 |           testing::ContainsRegex( | 
 |               "options:[ ]+\\{\n[ ]+option_name:[ ]+1234[ \n]+option_name_2:[ ]+12345[ \n]+\\}"))); | 
 | } | 
 |  | 
 | TEST(AddressDescriptorTest, DeserializeWithBadMagicIsError) { | 
 |   ASSERT_TRUE(AddressDescriptor::Deserialize(GetSerializedJson([](auto* document) { | 
 |                 (*document)["magic"] = AddressDescriptor::kMagic - 1; | 
 |               })).is_error()); | 
 | } | 
 |  | 
 | TEST(AddressDescriptorTest, DeserializeWithEmptyMappingsIsError) { | 
 |   ASSERT_TRUE(AddressDescriptor::Deserialize(GetSerializedJson([](auto* document) { | 
 |                 rapidjson::Value value; | 
 |                 value.SetArray(); | 
 |                 (*document)["mappings"] = value; | 
 |               })).is_error()); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | }  // namespace storage::volume_image |