/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include #include #include namespace UnitTest { class VariantSerializationTest : public AllocatorsFixture { public: // We must expose the class for serialization first. void SetUp() override { AllocatorsFixture::SetUp(); AZ::AllocatorInstance::Create(); AZ::AllocatorInstance::Create(); m_serializeContext = AZStd::make_unique(); AZ::Entity::Reflect(m_serializeContext.get()); } void TearDown() override { m_serializeContext->EnableRemoveReflection(); AZ::Entity::Reflect(m_serializeContext.get()); m_serializeContext->DisableRemoveReflection(); m_serializeContext.reset(); AZ::AllocatorInstance::Destroy(); AZ::AllocatorInstance::Destroy(); AllocatorsFixture::TearDown(); } protected: AZStd::unique_ptr m_serializeContext; }; struct VariantWrapper { AZ_TYPE_INFO(VariantWrapper, "{B086FD5B-1E6F-4CB1-9379-80C35DA3B430}"); AZ_CLASS_ALLOCATOR(VariantWrapper, AZ::SystemAllocator, 0); static void Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context)) { serializeContext->Class() ->Field("WrappedField", &VariantWrapper::m_wrappedVariant) ; } } AZStd::variant* m_wrappedVariant{}; }; TEST_F(VariantSerializationTest, VariantWithMonostateAlternativeSerializesCorrectly) { using TestVariant1 = AZStd::variant; ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } }); TestVariant1 testVariant; AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&testVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); TestVariant1 loadVariant; EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get())); EXPECT_EQ(testVariant, loadVariant); } TEST_F(VariantSerializationTest, VariantWithOneAlternativeSerializesCorrectly) { using TestVariant1 = AZStd::variant; ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); }}); TestVariant1 testVariant{ AZ::Entity("Variant") }; AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&testVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); TestVariant1 loadVariant; EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get())); EXPECT_EQ(testVariant.index(), loadVariant.index()); ASSERT_EQ(0U, testVariant.index()); EXPECT_EQ(AZStd::get<0>(testVariant).GetName(), AZStd::get<0>(loadVariant).GetName()); } TEST_F(VariantSerializationTest, VariantWithOneAlternativeWhichIsPointerTypeSerializesCorrectly) { using TestVariant1 = AZStd::variant; ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } }); TestVariant1 testVariant{ aznew AZ::Entity(AZ::EntityId(42), "Variant Pointer") }; AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&testVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); TestVariant1 loadVariant; EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get())); EXPECT_EQ(testVariant.index(), loadVariant.index()); ASSERT_EQ(0U, testVariant.index()); AZ::Entity* testEntity = AZStd::get<0>(testVariant); AZ::Entity* loadEntity = AZStd::get<0>(loadVariant); EXPECT_NE(testEntity, loadEntity); ASSERT_NE(loadEntity, nullptr); EXPECT_EQ(testEntity->GetId(), loadEntity->GetId()); EXPECT_EQ(testEntity->GetName(), loadEntity->GetName()); delete testEntity; delete loadEntity; AZStd::get<0>(testVariant) = nullptr; AZStd::get<0>(loadVariant) = nullptr; } TEST_F(VariantSerializationTest, MultipleAlternativeSerializesCorrectly) { using TestVariant1 = AZStd::variant; ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } }); // Store integer in variant and attempt to serialize it out and back in constexpr int32_t expectedIntValue = 0x85; TestVariant1 sourceVariant{ expectedIntValue }; TestVariant1 loadIntVariant; { AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&sourceVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadIntVariant, m_serializeContext.get())); } EXPECT_EQ(sourceVariant.index(), loadIntVariant.index()); ASSERT_EQ(0U, loadIntVariant.index()); int32_t loadIntValue = AZStd::get<0>(loadIntVariant); EXPECT_EQ(expectedIntValue, loadIntValue); // Update source variant with string value and attempt to serialize it out and back in const AZStd::string expectedStringValue = "Our Dog Food Eats the Dog"; sourceVariant = expectedStringValue; TestVariant1 loadStringVariant; { AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_BINARY); objStream->WriteClass(&sourceVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadStringVariant, m_serializeContext.get())); } EXPECT_EQ(sourceVariant.index(), loadStringVariant.index()); ASSERT_EQ(1U, loadStringVariant.index()); const AZStd::string& loadStringValue = AZStd::get<1>(loadStringVariant); EXPECT_EQ(expectedStringValue, loadStringValue); } TEST_F(VariantSerializationTest, VariantStoringAnyAlternativeSerializesCorrectly) { using TestVariant1 = AZStd::variant; ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } }); // Store integer in variant and attempt to serialize it out and back in constexpr int32_t expectedIntValue = 0x2A; TestVariant1 sourceVariant{ AZStd::make_any(expectedIntValue) }; AZStd::any& sourceAnyValue = AZStd::get<0>(sourceVariant); EXPECT_TRUE(sourceAnyValue.is()); int32_t* sourceIntValue = AZStd::any_cast(&sourceAnyValue); ASSERT_NE(nullptr, sourceIntValue); EXPECT_EQ(expectedIntValue, *sourceIntValue); TestVariant1 loadAnyVariant1; { AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&sourceVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadAnyVariant1, m_serializeContext.get())); } EXPECT_EQ(sourceVariant.index(), loadAnyVariant1.index()); ASSERT_EQ(0U, loadAnyVariant1.index()); AZStd::any& loadAnyValue = AZStd::get<0>(loadAnyVariant1); EXPECT_TRUE(loadAnyValue.is()); int32_t* loadIntValue = AZStd::any_cast(&loadAnyValue); ASSERT_NE(nullptr, loadIntValue); EXPECT_EQ(expectedIntValue, *loadIntValue); } TEST_F(VariantSerializationTest, AnyStoringVariantSavesAlternativeAndLoadsAlternativeCorrectly) { using TestVariant1 = AZStd::variant; ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } }); // Store integer in variant and attempt to serialize it out and back in constexpr int32_t expectedIntValue = 0170; AZStd::any sourceAny = AZStd::make_any(AZStd::in_place_type_t{}, expectedIntValue); EXPECT_TRUE(sourceAny.is()); TestVariant1* sourceVariantValue = AZStd::any_cast(&sourceAny); ASSERT_NE(nullptr, sourceVariantValue); EXPECT_EQ(expectedIntValue, AZStd::get<0>(*sourceVariantValue)); AZStd::any loadAny; { AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_JSON); objStream->WriteClass(&sourceAny); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadAny, m_serializeContext.get())); } // Due to the Variant Serialization only writing out the alternative to disk and the AZStd::any class // being only able to determine the type dynamically, the type that is stored in the any is the // alternative, not the variant EXPECT_TRUE(loadAny.is()); int* loadIntValue = AZStd::any_cast(&loadAny); ASSERT_NE(nullptr, loadIntValue); EXPECT_EQ(expectedIntValue, *loadIntValue); } TEST_F(VariantSerializationTest, TypeWhichWrapsVariantSavesAndLoadsCorrectly) { ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { VariantWrapper::Reflect(serializeContext); } }); // Store integer in variant and attempt to serialize it out and back in constexpr int32_t expectedIntValue = 7001; VariantWrapper saveWrapper; saveWrapper.m_wrappedVariant = new AZStd::variant(expectedIntValue); EXPECT_EQ(expectedIntValue, AZStd::get<0>(*saveWrapper.m_wrappedVariant)); VariantWrapper loadWrapper; { AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_JSON); objStream->WriteClass(&saveWrapper); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadWrapper, m_serializeContext.get())); } ASSERT_NE(nullptr, loadWrapper.m_wrappedVariant); EXPECT_NE(saveWrapper.m_wrappedVariant, loadWrapper.m_wrappedVariant); ASSERT_EQ(0U, loadWrapper.m_wrappedVariant->index()); EXPECT_EQ(expectedIntValue, AZStd::get<0>(*loadWrapper.m_wrappedVariant)); delete saveWrapper.m_wrappedVariant; // SerializeCotnext IObjectFactory allocates memory for types without AZClassAllocator using azmalloc // None of the AZStd::containers implement the AZ_CLASS_ALLOCATOR, so it uses the os allocator by default azdestroy(loadWrapper.m_wrappedVariant); } TEST_F(VariantSerializationTest, VariantStoringVariantSerializesCorrectly) { using InnerVariant = AZStd::variant; using VariantCeption = AZStd::variant; ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } }); // Store integer in variant and attempt to serialize it out and back in constexpr int32_t expectedIntValue = -43; // Sets the int32_t element of the inner variant // Therefore the outer variant index should be 1 and the inner variant index should be 1 VariantCeption sourceCeptionVariant{ InnerVariant{expectedIntValue} }; VariantCeption loadCeptionVariant; { AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&sourceCeptionVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadCeptionVariant, m_serializeContext.get())); } EXPECT_EQ(sourceCeptionVariant.index(), loadCeptionVariant.index()); ASSERT_EQ(1U, loadCeptionVariant.index()); InnerVariant& innerVariant = AZStd::get<1>(loadCeptionVariant); EXPECT_EQ(1U, innerVariant.index()); EXPECT_EQ(expectedIntValue, AZStd::get<1>(innerVariant)); } TEST_F(VariantSerializationTest, SavingVariantWithIntAlternativeCanBeLoadedByVariantWithIntAlternativeAtDifferentIndex) { using SaveVariant = AZStd::variant; using LoadVariant = AZStd::variant; // Store integer in variant and attempt to serialize it out and back in constexpr int32_t expectedIntValue = 72; // Sets the int32_t element of the source variant which is the zeroth index SaveVariant sourceVariant{ expectedIntValue }; AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); { ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); }} ); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&sourceVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); } ScopedSerializeContextReflector loadReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); LoadVariant loadVariant; EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get())); // Source variant should have different index than loaded variant EXPECT_EQ(0U, sourceVariant.index()); EXPECT_NE(sourceVariant.index(), loadVariant.index()); // The AZ Serialization system does not distinguish between const and non-const. // As the LoadVariant type has const int32_t as the 2nd index, it is the alternative that will be loaded constexpr size_t expectedLoadIndex = 2U; ASSERT_EQ(expectedLoadIndex, loadVariant.index()); EXPECT_TRUE(AZStd::holds_alternative(loadVariant)); EXPECT_EQ(AZStd::get<0>(sourceVariant), AZStd::get(loadVariant)); } TEST_F(VariantSerializationTest, SavingVariantWithIntAlternativeAndLoadingToVariantWithInnerVariantPointerSucceeds) { using SaveVariant = AZStd::variant; using LoadVariant = AZStd::variant*>; // Store integer in variant and attempt to serialize it out and back in constexpr int32_t expectedIntValue = 146; // Sets the int32_t element of the source variant which is the zeroth index SaveVariant sourceVariant{ expectedIntValue }; EXPECT_EQ(0U, sourceVariant.index()); AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); { ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&sourceVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); } ScopedSerializeContextReflector loadReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); LoadVariant loadVariant; EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get())); constexpr size_t expectedLoadIndex = 1U; ASSERT_EQ(expectedLoadIndex, loadVariant.index()); ASSERT_TRUE(AZStd::holds_alternative*>(loadVariant)); AZStd::variant*& innerVariant = AZStd::get(loadVariant); ASSERT_NE(nullptr, innerVariant); EXPECT_TRUE(AZStd::holds_alternative(*innerVariant)); int32_t* loadInt = AZStd::get_if(innerVariant); ASSERT_NE(nullptr, loadInt); EXPECT_EQ(expectedIntValue, *loadInt); azdestroy(innerVariant); } TEST_F(VariantSerializationTest, SavingVariantWithIntAlternativeAndLoadingToVariantWithInnerVariantPointerWhichHasAnIntPointerSucceeds) { using SaveVariant = AZStd::variant; using LoadVariant = AZStd::variant*, double>; // Store integer in variant and attempt to serialize it out and back in constexpr int32_t expectedIntValue = 146; // Sets the int32_t element of the source variant which is the zeroth index SaveVariant sourceVariant{ expectedIntValue }; EXPECT_EQ(0U, sourceVariant.index()); AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); { ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&sourceVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); } ScopedSerializeContextReflector loadReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); LoadVariant loadVariant; EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get())); constexpr size_t expectedLoadIndex = 0U; ASSERT_EQ(expectedLoadIndex, loadVariant.index()); ASSERT_TRUE(AZStd::holds_alternative*>(loadVariant)); AZStd::variant*& innerVariant = AZStd::get(loadVariant); ASSERT_NE(nullptr, innerVariant); EXPECT_TRUE(AZStd::holds_alternative(*innerVariant)); int32_t* loadInt = AZStd::get<0>(*innerVariant); ASSERT_NE(nullptr, loadInt); EXPECT_EQ(expectedIntValue, *loadInt); azdestroy(loadInt); azdestroy(innerVariant); } TEST_F(VariantSerializationTest, SavingVariantWithIntAlternativeAndLoadingToVariantWithInnerVariantIntTypeAndIntPointerTypeAndIntValueTypeChoosesIntValueType) { using SaveVariant = AZStd::variant; using LoadVariant = AZStd::variant*, int32_t*, int32_t>; // Store integer in variant and attempt to serialize it out and back in constexpr int32_t expectedIntValue = 146; // Sets the int32_t element of the source variant which is the zeroth index SaveVariant sourceVariant{ expectedIntValue }; EXPECT_EQ(0U, sourceVariant.index()); AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); { ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&sourceVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); } ScopedSerializeContextReflector loadReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); LoadVariant loadVariant; EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get())); constexpr size_t expectedLoadIndex = 2U; ASSERT_EQ(expectedLoadIndex, loadVariant.index()); int32_t loadInt = AZStd::get(loadVariant); EXPECT_EQ(expectedIntValue, loadInt); } TEST_F(VariantSerializationTest, SavingIntAlternativeAndLoadingToRootVariantSucceeds) { using LoadVariant = AZStd::variant; // Store integer in variant and attempt to serialize it out and back in constexpr int32_t expectedIntValue = 146; AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); { auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&expectedIntValue); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); } ScopedSerializeContextReflector loadReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); LoadVariant loadVariant; EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get())); constexpr size_t expectedLoadIndex = 0U; ASSERT_EQ(expectedLoadIndex, loadVariant.index()); int32_t* loadInt = AZStd::get(loadVariant); EXPECT_EQ(expectedIntValue, *loadInt); azdestroy(loadInt); } TEST_F(VariantSerializationTest, SavingAssetAlternativeAndLoadingToRootVariantSucceeds) { using SaveVariant = AZStd::variant>; using LoadVariant = AZStd::variant>; AZ::Data::AssetType sliceAssetTypeId("{C62C7A87-9C09-4148-A985-12F2C99C0A45}"); AZ::Data::Asset saveAsset(AZ::Data::AssetId{}, sliceAssetTypeId); SaveVariant saveVariant(saveAsset); AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); { ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&saveVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); } ScopedSerializeContextReflector loadReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); LoadVariant loadVariant; EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get())); constexpr size_t expectedLoadIndex = 0U; ASSERT_EQ(expectedLoadIndex, loadVariant.index()); AZ::Data::Asset& loadAsset= AZStd::get(loadVariant); EXPECT_FALSE(loadAsset.GetId().IsValid()); } TEST_F(VariantSerializationTest, SavingVariantWithIntAlternativeAndLoadingToVariantWithoutIntAlternativeFails) { using SaveVariant = AZStd::variant; using LoadVariant = AZStd::variant; // Store integer in variant and attempt to serialize it out and back in constexpr int32_t expectedIntValue = 72; // Sets the int32_t element of the source variant which is the zeroth index SaveVariant sourceVariant{ expectedIntValue }; EXPECT_EQ(0U, sourceVariant.index()); AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); { ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML); objStream->WriteClass(&sourceVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); } ScopedSerializeContextReflector loadReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); LoadVariant loadVariant; AZ_TEST_START_TRACE_SUPPRESSION; EXPECT_FALSE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get())); AZ_TEST_STOP_TRACE_SUPPRESSION(1); } TEST_F(VariantSerializationTest, SavingVariantWithVectorOfStringAlternativeAndIsAbleToLoadCorrectly) { using SaveVariant = AZStd::variant, float>; using LoadVariant = AZStd::variant>; // Store a vector of strings and attempt serialized to a stream and back const AZStd::string expectedStringValue1{ "ChimeYard" }; const AZStd::string expectedStringValue2{ "BirdCakeFactory" }; // Sets the vector of string element which corresponds to index 1 of the source variant SaveVariant sourceVariant{ AZStd::vector{expectedStringValue1, expectedStringValue2} }; EXPECT_EQ(0U, sourceVariant.index()); AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); { ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_BINARY); objStream->WriteClass(&sourceVariant); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); } ScopedSerializeContextReflector loadReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); LoadVariant loadVariant; EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get())); ASSERT_EQ(1, loadVariant.index()); EXPECT_EQ(AZStd::get<0>(sourceVariant), AZStd::get<1>(loadVariant)); } TEST_F(VariantSerializationTest, SavingVectorOfVectorTypeIsAbleToLoadIntoVariantCorrectly) { using SaveType = AZStd::vector>; using LoadVariant = AZStd::variant; // Store a vector of vector of strings and attempt serialized to a stream and back const AZStd::string expectedStringValue1{ "Zubat Key" }; const AZStd::string expectedStringValue2{ "Yubioh Key" }; const AZStd::string expectedStringValue3{ "Gelato Token" }; SaveType twoStepsVectorAndTwoStepsBack; // Set the inner vector first element to have an expected string value of "Yubioh Key" and "Gelato Token" twoStepsVectorAndTwoStepsBack.emplace_back(); twoStepsVectorAndTwoStepsBack.back().push_back(expectedStringValue2); twoStepsVectorAndTwoStepsBack.back().push_back(expectedStringValue3); // Set the inner vector second element to have an expected string value of "Gelato Token" and "Zubat Key" twoStepsVectorAndTwoStepsBack.emplace_back(); twoStepsVectorAndTwoStepsBack.back().push_back(expectedStringValue3); twoStepsVectorAndTwoStepsBack.back().push_back(expectedStringValue1); AZStd::vector byteBuffer; AZ::IO::ByteContainerStream byteStream(&byteBuffer); { ScopedSerializeContextReflector scopedReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_BINARY); objStream->WriteClass(&twoStepsVectorAndTwoStepsBack); objStream->Finalize(); byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); } ScopedSerializeContextReflector loadReflector(*m_serializeContext, { [](AZ::SerializeContext* serializeContext) { serializeContext->RegisterGenericType(); } } ); LoadVariant loadVariant; EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get())); ASSERT_EQ(0, loadVariant.index()); EXPECT_EQ(twoStepsVectorAndTwoStepsBack, AZStd::get<0>(loadVariant)); } }