blob: 224fce043d34a961e337675e66296f90110ffc5c [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.
#include "src/storage/volume_image/volume_descriptor.h"
#include <lib/fit/function.h>
#include <limits>
#include <string_view>
#include <type_traits>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "rapidjson/document.h"
#include "rapidjson/schema.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "src/lib/fxl/strings/string_view.h"
#include "src/lib/json_parser/json_parser.h"
#include "src/lib/json_parser/rapidjson_validation.h"
#include "src/storage/volume_image/options.h"
#include "src/storage/volume_image/serialization/schema.h"
namespace storage::volume_image {
namespace {
TEST(VolumeDescriptorTest, SerializeReturnsSchemaValidData) {
VolumeDescriptor descriptor = {};
descriptor.compression.schema = CompressionSchema::kLz4;
descriptor.compression.options = {{"option_1", 1}};
descriptor.options = {Option::kNone};
descriptor.encryption = EncryptionType::kZxcrypt;
auto schema_json = GetSchema(Schema::kVolumeDescriptor);
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();
std::unique_ptr<rapidjson::SchemaDocument> schema = json_parser::InitSchema(schema_json);
EXPECT_TRUE(json_parser::ValidateSchema(document, *schema, "VolumeDescriptor::Serialize output"));
}
std::string GetSerializedJson(fit::function<void(rapidjson::Document*)> mutator = nullptr) {
// A Valid JSON being serialized.
static constexpr std::string_view kSerializedVolumeDescriptor = R"(
{
"magic": 11602964,
"instance_guid": "04030201-0605-0807-1009-111213141516",
"type_guid": "A4A3A2A1-B6B5-C8C7-D0D1-E0E1E2E3E4E5",
"name": "i-have-a-name",
"block_size": 512,
"encryption_type": "ENCRYPTION_TYPE_ZXCRYPT",
"compression_schema": "COMPRESSION_SCHEMA_LZ4",
"compression_options": {
"random_option": 24,
"random_option_2": 25
},
"options" : [
"OPTION_NONE",
"OPTION_EMPTY"
]
}
)";
json_parser::JSONParser parser;
rapidjson::Document parsed_document = parser.ParseFromString(kSerializedVolumeDescriptor.data(),
"serialized_volume_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(VolumeDescriptorTest, DeserializeSerializedDataIsOk) {
const auto deserialized_result = VolumeDescriptor::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 = VolumeDescriptor::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_EQ(deserialized_1.type, deserialized_2.type);
ASSERT_EQ(deserialized_1.block_size, deserialized_2.block_size);
ASSERT_EQ(deserialized_1.instance, deserialized_2.instance);
ASSERT_EQ(deserialized_1.name, deserialized_2.name);
ASSERT_EQ(deserialized_1.compression.schema, deserialized_2.compression.schema);
ASSERT_EQ(deserialized_1.compression.options, deserialized_2.compression.options);
ASSERT_EQ(deserialized_1.encryption, deserialized_2.encryption);
ASSERT_EQ(deserialized_1.options, deserialized_2.options);
}
TEST(VolumeDescriptorTest, DeserializeFromValidDataReturnsVolumeDescriptor) {
constexpr std::string_view kTypeGuid = "A4A3A2A1-B6B5-C8C7-D0D1-E0E1E2E3E4E5";
constexpr std::string_view kInstanceGuid = "04030201-0605-0807-1009-111213141516";
constexpr std::string_view kName = "i-have-a-name";
const std::string kSerializedVolumeDescriptor = GetSerializedJson();
auto descriptor_result = VolumeDescriptor::Deserialize(kSerializedVolumeDescriptor);
ASSERT_TRUE(descriptor_result.is_ok()) << descriptor_result.take_error();
auto descriptor = descriptor_result.take_value();
auto expected_type_guid = Guid::FromString(kTypeGuid);
auto expected_instance_guid = Guid::FromString(kInstanceGuid);
ASSERT_TRUE(expected_type_guid.is_ok());
ASSERT_TRUE(expected_instance_guid.is_ok());
EXPECT_EQ(expected_type_guid.value(), descriptor.type);
EXPECT_EQ(expected_instance_guid.value(), descriptor.instance);
EXPECT_EQ(kName, std::string(reinterpret_cast<const char*>(descriptor.name.data())));
EXPECT_EQ(512u, descriptor.block_size);
EXPECT_EQ(EncryptionType::kZxcrypt, descriptor.encryption);
EXPECT_EQ(CompressionSchema::kLz4, descriptor.compression.schema);
std::map<std::string, uint64_t> kExpectedCompressionOptions = {{"random_option", 24},
{"random_option_2", 25}};
EXPECT_THAT(descriptor.compression.options,
::testing::UnorderedElementsAreArray(kExpectedCompressionOptions));
EXPECT_THAT(descriptor.options, ::testing::UnorderedElementsAre(Option::kNone, Option::kEmpty));
}
TEST(VolumeDescriptorTest, DeserializeWithBadTypeGuidIsError) {
ASSERT_TRUE(VolumeDescriptor::Deserialize(GetSerializedJson([](auto* document) {
(*document)["type_guid"] = "012345678";
})).is_error());
}
TEST(VolumeDescriptorTest, DeserializeWithBadInstanceGuidIsError) {
ASSERT_TRUE(VolumeDescriptor::Deserialize(GetSerializedJson([](auto* document) {
(*document)["instance_guid"] = "012345678";
})).is_error());
}
TEST(VolumeDescriptorTest, DeserializeWithLongNameIsTruncated) {
constexpr std::string_view kName = "01234567890123456789012345678901234567891";
auto descriptor_result = VolumeDescriptor::Deserialize(GetSerializedJson(
[kName](auto* document) { (*document)["name"] = rapidjson::StringRef(kName.data()); }));
ASSERT_TRUE(descriptor_result.is_ok());
ASSERT_EQ(kName.substr(0, kNameLength),
std::string(reinterpret_cast<const char*>(descriptor_result.value().name.data())));
}
TEST(VolumeDescriptorTest, DeserializeWithBadMagicIsError) {
ASSERT_TRUE(VolumeDescriptor::Deserialize(GetSerializedJson([](auto* document) {
(*document)["magic"] = 0xB201C4;
})).is_error());
}
TEST(VolumeDescriptorTest, DeserializeWithBadCompressionSchemaIsError) {
ASSERT_TRUE(VolumeDescriptor::Deserialize(GetSerializedJson([](auto* document) {
(*document)["compression_schema"] = "BAD_OR_UNKNOWN_SCHEMA";
})).is_error());
}
TEST(VolumeDescriptorTest, DeserializeWithBadEncryptionTypeIsError) {
ASSERT_TRUE(VolumeDescriptor::Deserialize(GetSerializedJson([](auto* document) {
(*document)["encryption_type"] = "BAD_OR_UNKNOWN_ENCRYPTION";
})).is_error());
}
TEST(VolumeDescriptorTest, DeserializeWithBadOptionIsError) {
ASSERT_TRUE(VolumeDescriptor::Deserialize(GetSerializedJson([](auto* document) {
(*document)["options"].GetArray().PushBack("BAD_OR_UNKNOWN_OPTION",
document->GetAllocator());
})).is_error());
}
} // namespace
} // namespace storage::volume_image