From 8af45d28be8c817a2733b71db38c28cd01b87749 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Tue, 15 Jun 2021 14:02:54 -0700 Subject: [PATCH] Additional unit tests for the Json Serialization To cover the recent changes to the return code from containers and the initialization fixes additional unit tests were added. Almost all new tests are part of the conformity test suite so that they test any custom json serializers outside of AzCore that might need to be updated due to the fixes. --- .../AzCore/Tests/AssetJsonSerializerTests.cpp | 4 + .../Json/ArraySerializerTests.cpp | 63 ++--- .../Json/BasicContainerSerializerTests.cpp | 19 ++ .../Json/JsonSerializerConformityTests.h | 217 +++++++++++++++++- .../Serialization/Json/MapSerializerTests.cpp | 126 +++++++++- .../Json/TupleSerializerTests.cpp | 13 +- .../Json/UnorderedSetSerializerTests.cpp | 10 + 7 files changed, 384 insertions(+), 68 deletions(-) diff --git a/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp b/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp index 3e4ddac3af..ceff314c81 100644 --- a/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp @@ -168,6 +168,10 @@ namespace JsonSerializationTests { features.EnableJsonType(rapidjson::kObjectType); features.m_typeToInject = rapidjson::kNullType; + // The type information in the Serialize Context is incomplete so this test will fail. + // This is because assets have traditionally been treated as a special case, so there's + // information missing in the Json Serialization to deal with these. + features.m_enableNewInstanceTests = false; } bool AreEqual(const Asset& lhs, const Asset& rhs) override diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/ArraySerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/ArraySerializerTests.cpp index da66bdd0b5..1f39feccbd 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/ArraySerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/ArraySerializerTests.cpp @@ -126,9 +126,9 @@ namespace JsonSerializationTests { auto array = AZStd::shared_ptr(new Array(), Deleter); (*array)[0] = nullptr; - (*array)[1] = aznew MultipleInheritence(); + (*array)[1] = nullptr; (*array)[2] = nullptr; - (*array)[3] = aznew MultipleInheritence(); + (*array)[3] = nullptr; return array; } @@ -154,6 +154,7 @@ namespace JsonSerializationTests null, null, { + "$type": "MultipleInheritence", "base_var": 242.0, "var1" : 142 } @@ -246,56 +247,21 @@ namespace JsonSerializationTests ])"; } - AZStd::string_view GetJsonFor_Store_SerializeFullySetInstance() override - { - // This is a unique situation because the $type is determined separate from other values, so all - // member values can be changed, but since the default type matches the stored type the $type - // will only be written if default values are explicitly kept. - return R"( - [ - { - "$type": "MultipleInheritence", - "base_var": 1142.0, - "base2_var1": 1242.0, - "base2_var2": 1342.0, - "base2_var3": 1442.0, - "var1" : 1542, - "var2" : 1642.0 - }, - { - "base_var": 2142.0, - "base2_var1": 2242.0, - "base2_var2": 2342.0, - "base2_var3": 2442.0, - "var1" : 2542, - "var2" : 2642.0 - }, - { - "$type": "MultipleInheritence", - "base_var": 3142.0, - "base2_var1": 3242.0, - "base2_var2": 3342.0, - "base2_var3": 3442.0, - "var1" : 3542, - "var2" : 3642.0 - }, - { - "base_var": 4142.0, - "base2_var1": 4242.0, - "base2_var2": 4342.0, - "base2_var3": 4442.0, - "var1" : 4542, - "var2" : 4642.0 - } - ])"; - } - void Reflect(AZStd::unique_ptr& context) override { Base::Reflect(context); MultipleInheritence::Reflect(context, true); } + void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override + { + Base::ConfigureFeatures(features); + // These tests don't work with pointers because there'll be a random value in the pointer + // which the Json Serialization try to delete. The POD version of these tests already cover + // these cases. + features.m_enableNewInstanceTests = false; + } + bool AreEqual(const Array& lhs, const Array& rhs) override { size_t size = lhs.size(); @@ -311,6 +277,11 @@ namespace JsonSerializationTests return rhs[i] == nullptr; } + if (rhs[i] == nullptr) + { + return false; + } + if (!static_cast(lhs[i])->Equals(*static_cast(rhs[i]), true)) { return false; diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/BasicContainerSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/BasicContainerSerializerTests.cpp index b3c900c710..508658e2a5 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/BasicContainerSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/BasicContainerSerializerTests.cpp @@ -54,6 +54,11 @@ namespace JsonSerializationTests return AZStd::make_shared(Container{ 188, 288, 388 }); } + AZStd::shared_ptr CreateSingleArrayDefaultInstance() override + { + return AZStd::make_shared(Container{ 0 }); + } + AZStd::string_view GetJsonForFullySetInstance() override { return "[188, 288, 388]"; @@ -120,6 +125,13 @@ namespace JsonSerializationTests &SimplePointerTestDescription::Delete); } + AZStd::shared_ptr CreateSingleArrayDefaultInstance() override + { + int* value = reinterpret_cast(azmalloc(sizeof(int), alignof(int))); + *value = 0; + return AZStd::shared_ptr(new Container{ value }, &SimplePointerTestDescription::Delete); + } + AZStd::string_view GetJsonForFullySetInstance() override { return "[188, 288, 388]"; @@ -180,6 +192,13 @@ namespace JsonSerializationTests return instance; } + AZStd::shared_ptr CreateSingleArrayDefaultInstance() override + { + auto instance = AZStd::make_shared(); + *instance = { SimpleClass{} }; + return instance; + } + AZStd::string_view GetJsonForFullySetInstance() override { return R"([ diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/JsonSerializerConformityTests.h b/Code/Framework/AzCore/Tests/Serialization/Json/JsonSerializerConformityTests.h index c0f470378c..f45e0a3658 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/JsonSerializerConformityTests.h +++ b/Code/Framework/AzCore/Tests/Serialization/Json/JsonSerializerConformityTests.h @@ -62,6 +62,14 @@ namespace JsonSerializationTests //! can be used to manually create these documents. If that is also not an option the tests can be //! disabled by setting this flag to false. bool m_supportsInjection{ true }; + //! Enables the check that tries to determine if variables are initialized and if not whether they have the + //! OperationFlags::ManualDefault set. This applies for instance to integers, which won't be initialized if + //! constructed a new instance is created for pointers. + bool m_enableInitializationTest{ true }; + //! Enable the test that creates a new instance of the provided test type through the factory that's found in + //! the Serialize Context. This test is automatically disabled for classes that don't have a factory or + //! have a null factory. + bool m_enableNewInstanceTests{ true }; private: // There's no way to retrieve the number of types from RapidJSON so they're hard-coded here. @@ -87,6 +95,7 @@ namespace JsonSerializationTests { public: using Type = T; + virtual ~JsonSerializerConformityTestDescriptor() = default; virtual AZStd::shared_ptr CreateSerializer() = 0; @@ -104,13 +113,21 @@ namespace JsonSerializationTests virtual AZStd::shared_ptr CreatePartialDefaultInstance() { return nullptr; } //! Create an instance where all values are set to non-default values. virtual AZStd::shared_ptr CreateFullySetInstance() = 0; + //! Create an instance of the target array type with a single value that has all defaults. + //! If the target type doesn't support arrays or requires more than one entry this can be ignored and + //! tests using this value will be skipped. + virtual AZStd::shared_ptr CreateSingleArrayDefaultInstance() { return nullptr; } //! Get the json that represents the default instance. //! If the target type doesn't support partial specialization this can be ignored and //! tests for partial support will be skipped. - virtual AZStd::string_view GetJsonForPartialDefaultInstance() { return ""; } + virtual AZStd::string_view GetJsonForPartialDefaultInstance() { return ""; } //! Get the json that represents the instance with all values set. virtual AZStd::string_view GetJsonForFullySetInstance() = 0; + //! Get the json that represents an array with a single value that has only defaults. + //! If the target type doesn't support arrays or requires more than one entry this can be ignored and + //! tests using this value will be skipped. + virtual AZStd::string_view GetJsonForSingleArrayDefaultInstance() { return "[{}]"; } //! Get the json where additional values are added to the json file. //! If this function is not overloaded, but features.m_supportsInjection is enabled then //! the Json Serializer Conformity Tests will inject extra values in the json for a fully. @@ -138,12 +155,15 @@ namespace JsonSerializationTests virtual AZStd::string_view GetJsonFor_Load_DeserializeUnreflectedType() { return this->GetJsonForFullySetInstance(); } virtual AZStd::string_view GetJsonFor_Load_DeserializeFullySetInstance() { return this->GetJsonForFullySetInstance(); } virtual AZStd::string_view GetJsonFor_Load_DeserializePartialInstance() { return this->GetJsonForPartialDefaultInstance(); } + virtual AZStd::string_view GetJsonFor_Load_DeserializeArrayWithDefaultValue() { return this->GetJsonForSingleArrayDefaultInstance(); } + virtual AZStd::string_view GetJsonFor_Load_DeserializeFullInstanceOnTopOfPartialDefaulted() { return this->GetJsonForFullySetInstance(); } virtual AZStd::string_view GetJsonFor_Load_HaltedThroughCallback() { return this->GetJsonForFullySetInstance(); } virtual AZStd::string_view GetJsonFor_Store_SerializeWithDefaultsKept() { return this->GetJsonForFullySetInstance(); } virtual AZStd::string_view GetJsonFor_Store_SerializeFullySetInstance() { return this->GetJsonForFullySetInstance(); } virtual AZStd::string_view GetJsonFor_Store_SerializeWithoutDefault() { return this->GetJsonForFullySetInstance(); } virtual AZStd::string_view GetJsonFor_Store_SerializeWithoutDefaultAndDefaultsKept() { return this->GetJsonForFullySetInstance(); } virtual AZStd::string_view GetJsonFor_Store_SerializePartialInstance() { return this->GetJsonForPartialDefaultInstance(); } + virtual AZStd::string_view GetJsonFor_Store_SerializeArrayWithSingleDefaultValue() { return this->GetJsonForSingleArrayDefaultInstance(); } }; template @@ -154,6 +174,21 @@ namespace JsonSerializationTests using Description = T; using Type = typename T::Type; + struct PointerWrapper + { + AZ_TYPE_INFO(PointerWrapper, "{32FA6645-074A-458A-B79C-B173D0BD4B42}"); + AZ_CLASS_ALLOCATOR(PointerWrapper, AZ::SystemAllocator, 0); + + Type* m_value{ nullptr }; + + ~PointerWrapper() + { + // Using free because not all types can safely use delete. Since this just to clear the memory to satisfy the memory + // leak test, this is fine. + azfree(m_value); + } + }; + void SetUp() override { using namespace AZ::JsonSerializationResult; @@ -165,6 +200,7 @@ namespace JsonSerializationTests descriptor->ConfigureFeatures(this->m_features); descriptor->Reflect(this->m_serializeContext); descriptor->Reflect(this->m_jsonRegistrationContext); + this->m_serializeContext->Class()->Field("Value", &PointerWrapper::m_value); this->m_deserializationSettings->m_reporting = &Internal::VerifyCallback; this->m_serializationSettings->m_reporting = &Internal::VerifyCallback; @@ -185,6 +221,7 @@ namespace JsonSerializationTests this->m_jsonRegistrationContext->DisableRemoveReflection(); this->m_serializeContext->EnableRemoveReflection(); + this->m_serializeContext->Class()->Field("Value", &PointerWrapper::m_value); descriptor->Reflect(this->m_serializeContext); this->m_serializeContext->DisableRemoveReflection(); @@ -487,6 +524,41 @@ namespace JsonSerializationTests } } + TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeArrayWithDefaultValue_SucceedsAndReportPartialDefaults) + { + using namespace AZ::JsonSerializationResult; + + if (this->m_features.SupportsJsonType(rapidjson::kArrayType)) + { + this->m_jsonDocument->Parse(this->m_description.GetJsonFor_Load_DeserializeArrayWithDefaultValue().data()); + ASSERT_FALSE(this->m_jsonDocument->HasParseError()); + + auto serializer = this->m_description.CreateSerializer(); + auto instance = this->m_description.CreateDefaultInstance(); + + this->m_jsonDeserializationContext->PushPath(DefaultPath); + + ResultCode result = + serializer->Load(instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext); + + if (this->m_features.m_fixedSizeArray) + { + EXPECT_EQ(Outcomes::Unsupported, result.GetOutcome()); + EXPECT_EQ(Processing::Altered, result.GetProcessing()); + } + else + { + EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome()); + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + + auto compare = this->m_description.CreateSingleArrayDefaultInstance(); + ASSERT_NE(nullptr, compare) + << "Conformity tests for variably sized arrays require an implementation of CreateSingleArrayDefaultInstance"; + EXPECT_TRUE(this->m_description.AreEqual(*compare, *instance)); + } + } + } + TYPED_TEST_P(JsonSerializerConformityTests, Load_InterruptClearingTarget_ContainerIsNotCleared) { using namespace AZ::JsonSerializationResult; @@ -548,7 +620,6 @@ namespace JsonSerializationTests this->m_jsonDocument->Parse(json.data()); ASSERT_FALSE(this->m_jsonDocument->HasParseError()); - auto serializer = this->m_description.CreateSerializer(); auto instance = this->m_description.CreateDefaultConstructedInstance(); auto compare = this->m_description.CreateFullySetInstance(); @@ -622,6 +693,94 @@ namespace JsonSerializationTests } } + TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeFullInstanceOnTopOfPartialDefaulted_SucceedsAndObjectMatchesParialInstance) + { + using namespace AZ::JsonSerializationResult; + + if (this->m_features.m_supportsPartialInitialization) + { + AZStd::string_view json = this->m_description.GetJsonFor_Load_DeserializeFullInstanceOnTopOfPartialDefaulted(); + // If tests for partial initialization are enabled than json for the partial initialization is needed. + ASSERT_FALSE(json.empty()); + this->m_jsonDocument->Parse(json.data()); + ASSERT_FALSE(this->m_jsonDocument->HasParseError()); + + auto serializer = this->m_description.CreateSerializer(); + auto instance = this->m_description.CreatePartialDefaultInstance(); + auto compare = this->m_description.CreateFullySetInstance(); + ASSERT_NE(nullptr, compare); + + // Clear containers which should effectively turn them into default containers. + this->m_deserializationSettings->m_clearContainers = true; + this->ResetJsonContexts(); + this->m_jsonDeserializationContext->PushPath(DefaultPath); + + ResultCode result = + serializer->Load(instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext); + + EXPECT_EQ(Outcomes::Success, result.GetOutcome()); + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + EXPECT_TRUE(this->m_description.AreEqual(*instance, *compare)); + } + } + + TYPED_TEST_P(JsonSerializerConformityTests, Load_DefaultToPointer_SucceedsAndValueIsInitialized) + { + using namespace AZ::JsonSerializationResult; + + if (this->m_features.m_enableNewInstanceTests) + { + AZ::SerializeContext* serializeContext = this->m_jsonDeserializationContext->GetSerializeContext(); + const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(azrtti_typeid()); + ASSERT_NE(nullptr, classData); + // Skip this test if the target type doesn't have a factor to create a new instance with or if the factor explicit + // prohibits construction. + if (classData->m_factory && classData->m_factory != AZ::Internal::NullFactory::GetInstance()) + { + PointerWrapper instance; + auto compare = this->m_description.CreateDefaultInstance(); + + this->m_jsonDocument->Parse(R"({ "Value": {}})"); + ASSERT_FALSE(this->m_jsonDocument->HasParseError()); + + AZ::JsonDeserializerSettings settings; + settings.m_serializeContext = serializeContext; + settings.m_registrationContext = this->m_jsonDeserializationContext->GetRegistrationContext(); + ResultCode result = AZ::JsonSerialization::Load(instance, *this->m_jsonDocument, settings); + + EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome()); + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + ASSERT_NE(nullptr, instance.m_value); + EXPECT_TRUE(this->m_description.AreEqual(*instance.m_value, *compare)); + } + } + } + + TYPED_TEST_P(JsonSerializerConformityTests, Load_InitializeNewInstance_SucceedsAndValueIsInitialized) + { + using namespace AZ; + using namespace AZ::JsonSerializationResult; + + if (this->m_features.m_enableNewInstanceTests) + { + auto serializer = this->m_description.CreateSerializer(); + if ((serializer->GetOperationsFlags() & BaseJsonSerializer::OperationFlags::InitializeNewInstance) == + BaseJsonSerializer::OperationFlags::InitializeNewInstance) + { + Type instance; + auto compare = this->m_description.CreateDefaultInstance(); + this->m_jsonDocument->SetObject(); + + ResultCode result = + serializer->Load(&instance, azrtti_typeid(instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext); + + EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome()); + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + EXPECT_TRUE(this->m_description.AreEqual(instance, *compare)); + } + } + } + TYPED_TEST_P(JsonSerializerConformityTests, Load_HaltedThroughCallback_LoadFailsAndHaltReported) { using namespace AZ::JsonSerializationResult; @@ -909,6 +1068,28 @@ namespace JsonSerializationTests } } + TYPED_TEST_P(JsonSerializerConformityTests, Store_SerializeArrayWithSingleDefaultValue_StoredSuccessfullyAndJsonMatches) + { + using namespace AZ::JsonSerializationResult; + + if (this->m_features.SupportsJsonType(rapidjson::kArrayType) && !this->m_features.m_fixedSizeArray) + { + this->m_jsonSerializationContext->PushPath(DefaultPath); + + auto serializer = this->m_description.CreateSerializer(); + auto instance = this->m_description.CreateSingleArrayDefaultInstance(); + ASSERT_NE(nullptr, instance) + << "Conformity tests for variably sized arrays require an implementation of CreateSingleArrayDefaultInstance"; + + ResultCode result = serializer->Store( + *this->m_jsonDocument, instance.get(), instance.get(), azrtti_typeid(*instance), *this->m_jsonSerializationContext); + + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome()); + this->Expect_DocStrEq(this->m_description.GetJsonFor_Store_SerializeArrayWithSingleDefaultValue()); + } + } + TYPED_TEST_P(JsonSerializerConformityTests, Store_HaltedThroughCallback_StoreFailsAndHaltReported) { using namespace AZ::JsonSerializationResult; @@ -1017,7 +1198,29 @@ namespace JsonSerializationTests } } - TYPED_TEST_P(JsonSerializerConformityTests, GetOperationsFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared) + TYPED_TEST_P(JsonSerializerConformityTests, GetOperationFlags_RequiresExplicitInit_ObjectsThatDoNotConstructHaveExplicitInitOption) + { + using namespace AZ; + using namespace AZ::JsonSerializationResult; + + if (this->m_features.m_enableInitializationTest) + { + auto instance = this->m_description.CreateDefaultInstance(); + Type compare; + if (!this->m_description.AreEqual(*instance, compare)) + { + auto serializer = this->m_description.CreateSerializer(); + BaseJsonSerializer::OperationFlags flags = serializer->GetOperationsFlags(); + bool hasManualDefaultSet = + (flags & BaseJsonSerializer::OperationFlags::ManualDefault) == BaseJsonSerializer::OperationFlags::ManualDefault || + (flags & BaseJsonSerializer::OperationFlags::InitializeNewInstance) == + BaseJsonSerializer::OperationFlags::InitializeNewInstance; + EXPECT_TRUE(hasManualDefaultSet); + } + } + } + + TYPED_TEST_P(JsonSerializerConformityTests, GetOperationFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared) { if (this->m_features.SupportsJsonType(rapidjson::kObjectType)) { @@ -1048,10 +1251,14 @@ namespace JsonSerializationTests Load_DeserializeEmptyArray_SucceedsAndObjectMatchesDefaults, Load_DeserializeEmptyArrayWithClearEnabled_SucceedsAndObjectMatchesDefaults, Load_DeserializeEmptyArrayWithClearedTarget_SucceedsAndObjectMatchesDefaults, + Load_DeserializeArrayWithDefaultValue_SucceedsAndReportPartialDefaults, Load_InterruptClearingTarget_ContainerIsNotCleared, Load_DeserializeFullySetInstance_SucceedsAndObjectMatchesFullySetInstance, Load_DeserializeFullySetInstanceThroughMainLoad_SucceedsAndObjectMatchesFullySetInstance, Load_DeserializePartialInstance_SucceedsAndObjectMatchesParialInstance, + Load_DeserializeFullInstanceOnTopOfPartialDefaulted_SucceedsAndObjectMatchesParialInstance, + Load_DefaultToPointer_SucceedsAndValueIsInitialized, + Load_InitializeNewInstance_SucceedsAndValueIsInitialized, Load_DeserializeWithMissingMandatoryField_LoadFailedAndUnsupportedReported, Load_InsertAdditionalData_SucceedsAndObjectMatchesFullySetInstance, Load_HaltedThroughCallback_LoadFailsAndHaltReported, @@ -1066,13 +1273,15 @@ namespace JsonSerializationTests Store_SerializeWithoutDefaultAndDefaultsKept_StoredSuccessfullyAndJsonMatches, Store_SerializePartialInstance_StoredSuccessfullyAndJsonMatches, Store_SerializeEmptyArray_StoredSuccessfullyAndJsonMatches, + Store_SerializeArrayWithSingleDefaultValue_StoredSuccessfullyAndJsonMatches, Store_HaltedThroughCallback_StoreFailsAndHaltReported, StoreLoad_RoundTripWithPartialDefault_IdenticalInstances, StoreLoad_RoundTripWithFullSet_IdenticalInstances, StoreLoad_RoundTripWithDefaultsKept_IdenticalInstances, - GetOperationsFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared); + GetOperationFlags_RequiresExplicitInit_ObjectsThatDoNotConstructHaveExplicitInitOption, + GetOperationFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared); } // namespace JsonSerializationTests namespace AZ diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/MapSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/MapSerializerTests.cpp index 363cd7a7b7..7ec5ca3b9a 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/MapSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/MapSerializerTests.cpp @@ -35,6 +35,11 @@ namespace JsonSerializationTests return AZStd::make_shared(); } + AZStd::string_view GetJsonForSingleArrayDefaultInstance() override + { + return R"({ "{}": {} })"; + } + void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override { features.EnableJsonType(rapidjson::kArrayType); @@ -61,6 +66,13 @@ namespace JsonSerializationTests public: using Map = T; + AZStd::shared_ptr CreateSingleArrayDefaultInstance() override + { + auto instance = AZStd::make_shared(); + instance->emplace(AZStd::make_pair(0, 0.0)); + return instance; + } + AZStd::shared_ptr CreateFullySetInstance() override { auto instance = AZStd::make_shared(); @@ -100,6 +112,13 @@ namespace JsonSerializationTests public: using Map = T; + AZStd::shared_ptr CreateSingleArrayDefaultInstance() override + { + auto instance = AZStd::make_shared(); + instance->emplace(AZStd::make_pair(AZStd::string(), 0.0)); + return instance; + } + AZStd::shared_ptr CreateFullySetInstance() override { auto instance = AZStd::make_shared(); @@ -163,6 +182,14 @@ namespace JsonSerializationTests return instance; } + AZStd::shared_ptr CreateSingleArrayDefaultInstance() override + { + auto instance = AZStd::shared_ptr(new Map{}, &Delete); + instance->emplace(AZStd::make_pair(aznew SimpleClass(), aznew SimpleClass())); + return instance; + } + + AZStd::string_view GetJsonForPartialDefaultInstance() override { if constexpr (IsMultiMap) @@ -237,13 +264,23 @@ namespace JsonSerializationTests return false; } - auto compare = [](typename Map::const_reference lhs, typename Map::const_reference rhs) -> bool + // Naive compare to avoid having to split up the test because comparing for ordered and unordered maps would need to be + // different. + for (auto&& [key, value] : lhs) { - return - lhs.first->Equals(*rhs.first, true) && - lhs.second->Equals(*rhs.second, true); - }; - return AZStd::equal(lhs.begin(), lhs.end(), rhs.begin(), compare); + for (auto&& [keyCompare, valueCompare] : rhs) + { + if (key->Equals(*keyCompare, true)) + { + if (!value->Equals(*valueCompare, true)) + { + return false; + } + break; + } + } + } + return true; } }; @@ -393,6 +430,29 @@ namespace JsonSerializationTests { using namespace AZ::JsonSerializationResult; + m_jsonDocument->Parse(R"( + { + "{}": {} + })"); + ASSERT_FALSE(m_jsonDocument->HasParseError()); + + TestStringMap values; + ResultCode result = m_unorderedMapSerializer.Load(&values, azrtti_typeid(&values), *m_jsonDocument, *m_jsonDeserializationContext); + + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome()); + + EXPECT_EQ(1, values.size()); + + auto defaultKey = values.find(TestString()); + EXPECT_NE(values.end(), defaultKey); + EXPECT_STRCASEEQ(TestString().m_value.c_str(), defaultKey->second.m_value.c_str()); + } + + TEST_F(JsonMapSerializerTests, Load_DefaultForStringKeyAndAdditionalValue_LoadedBackWithDefaults) + { + using namespace AZ::JsonSerializationResult; + m_jsonDocument->Parse(R"( { "{}": {}, @@ -563,13 +623,13 @@ namespace JsonSerializationTests EXPECT_EQ(Outcomes::Catastrophic, result.GetOutcome()); } - TEST_F(JsonMapSerializerTests, Load_DefaultValueInMultiMap_DefaultUsed) + TEST_F(JsonMapSerializerTests, Load_DefaultObjectInMultiMap_DefaultUsed) { using namespace AZ::JsonSerializationResult; m_jsonDocument->Parse(R"( { - "Hello": {} + "World": {} })"); ASSERT_FALSE(m_jsonDocument->HasParseError()); @@ -581,17 +641,40 @@ namespace JsonSerializationTests EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome()); ASSERT_FALSE(values.empty()); - EXPECT_STREQ("Hello", values.begin()->first.m_value.c_str()); + EXPECT_STREQ("World", values.begin()->first.m_value.c_str()); EXPECT_STREQ(TestString().m_value.c_str(), values.begin()->second.m_value.c_str()); } - TEST_F(JsonMapSerializerTests, Load_DefaultArrayValueInMultiMap_DefaultUsed) + TEST_F(JsonMapSerializerTests, Load_FullDefaultObjectInMultiMap_DefaultUsed) { using namespace AZ::JsonSerializationResult; m_jsonDocument->Parse(R"( { - "Hello": [{}] + "{}": {} + })"); + ASSERT_FALSE(m_jsonDocument->HasParseError()); + + TestStringMultiMap values; + ResultCode result = + m_unorderedMultiMapSerializer.Load(&values, azrtti_typeid(&values), *m_jsonDocument, *m_jsonDeserializationContext); + + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome()); + + ASSERT_FALSE(values.empty()); + EXPECT_STREQ("Hello", values.begin()->first.m_value.c_str()); + EXPECT_STREQ("Hello", values.begin()->second.m_value.c_str()); + EXPECT_STREQ(TestString().m_value.c_str(), values.begin()->second.m_value.c_str()); + } + + TEST_F(JsonMapSerializerTests, Load_DefaultObjectValueInMultiMap_DefaultUsed) + { + using namespace AZ::JsonSerializationResult; + + m_jsonDocument->Parse(R"( + { + "World": [{}] })"); ASSERT_FALSE(m_jsonDocument->HasParseError()); @@ -603,7 +686,8 @@ namespace JsonSerializationTests EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome()); ASSERT_FALSE(values.empty()); - EXPECT_STREQ("Hello", values.begin()->first.m_value.c_str()); + EXPECT_STREQ("World", values.begin()->first.m_value.c_str()); + EXPECT_STREQ("Hello", values.begin()->second.m_value.c_str()); EXPECT_STREQ(TestString().m_value.c_str(), values.begin()->second.m_value.c_str()); } @@ -661,6 +745,24 @@ namespace JsonSerializationTests })"); } + TEST_F(JsonMapSerializerTests, Store_SingleAllDefaulValue_InitializedWithDefaults) + { + using namespace AZ::JsonSerializationResult; + + SimpleClassMap values; + values.emplace(SimpleClass(), SimpleClass()); + + ResultCode result = + m_unorderedMapSerializer.Store(*m_jsonDocument, &values, nullptr, azrtti_typeid(&values), *m_jsonSerializationContext); + + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome()); + Expect_DocStrEq(R"( + { + "{}": {} + })"); + } + TEST_F(JsonMapSerializerTests, Store_DefaultsWithObjectKey_InitializedWithDefaults) { using namespace AZ::JsonSerializationResult; diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/TupleSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/TupleSerializerTests.cpp index eceaaae5f1..77a88fda6c 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/TupleSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/TupleSerializerTests.cpp @@ -48,12 +48,12 @@ namespace JsonSerializationTests AZStd::shared_ptr CreateDefaultInstance() override { - return AZStd::make_shared(142, 242.0); + return AZStd::make_shared(0, 0.0); } AZStd::shared_ptr CreatePartialDefaultInstance() override { - return AZStd::make_shared(142, 288.0); + return AZStd::make_shared(0, 288.0); } AZStd::shared_ptr CreateFullySetInstance() override @@ -102,12 +102,12 @@ namespace JsonSerializationTests AZStd::shared_ptr CreateDefaultInstance() override { - return AZStd::make_shared(142, 242.0, 342.0f); + return AZStd::make_shared(0, 0.0, 0.0f); } AZStd::shared_ptr CreatePartialDefaultInstance() override { - return AZStd::make_shared(142, 288.0, 342.0f); + return AZStd::make_shared(0, 288.0, 0.0f); } AZStd::shared_ptr CreateFullySetInstance() override @@ -345,6 +345,7 @@ namespace JsonSerializationTests { TupleSerializerTestsInternal::ConfigureFeatures(features); features.m_supportsPartialInitialization = true; + features.m_enableNewInstanceTests = false; } void Reflect(AZStd::unique_ptr& context) override @@ -447,14 +448,14 @@ namespace JsonSerializationTests { return AZStd::make_shared( AZStd::vector(), - AZStd::make_pair(442, "")); + AZStd::make_pair(0, "")); } AZStd::shared_ptr CreatePartialDefaultInstance() override { return AZStd::make_shared( AZStd::vector(), - AZStd::make_pair(442, "hello")); + AZStd::make_pair(0, "hello")); } AZStd::shared_ptr CreateFullySetInstance() override diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/UnorderedSetSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/UnorderedSetSerializerTests.cpp index 6e9cd0f3d6..fb7b683d2e 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/UnorderedSetSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/UnorderedSetSerializerTests.cpp @@ -36,6 +36,11 @@ namespace JsonSerializationTests return AZStd::make_shared(); } + AZStd::shared_ptr CreateSingleArrayDefaultInstance() override + { + return AZStd::make_shared(Set{ 0 }); + } + AZStd::shared_ptr CreateFullySetInstance() override { return AZStd::make_shared(Set{42, -88, 342}); @@ -80,6 +85,11 @@ namespace JsonSerializationTests return AZStd::make_shared(); } + AZStd::shared_ptr CreateSingleArrayDefaultInstance() override + { + return AZStd::make_shared(MultiSet{ 0 }); + } + AZStd::shared_ptr CreateFullySetInstance() override { return AZStd::make_shared(MultiSet{ 42, -88, 42, 342 });