Merge pull request #7561 from aws-lumberyard-dev/ConfigurableStack

Improved the way the Settings Registry can handle stacks/arrays.
monroegm-disable-blank-issue-2
Ronald Koppers 4 years ago committed by GitHub
commit f2378fc8d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -22,6 +22,7 @@
#include <AzCore/Serialization/Json/UnorderedSetSerializer.h>
#include <AzCore/Serialization/Json/UnsupportedTypesSerializer.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Settings/ConfigurableStack.h>
#include <AzCore/std/any.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/tuple.h>
@ -50,6 +51,8 @@ namespace AZ
void JsonSystemComponent::Reflect(ReflectContext* reflectContext)
{
JsonConfigurableStackSerializer::Reflect(reflectContext);
if (JsonRegistrationContext* jsonContext = azrtti_cast<JsonRegistrationContext*>(reflectContext))
{
jsonContext->Serializer<JsonBoolSerializer>()->HandlesType<bool>();

@ -8,11 +8,12 @@
#pragma once
#include <AzCore/Math/Uuid.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AZ
@ -22,6 +23,7 @@ namespace AZ
{
public:
AZ_RTTI(JsonRegistrationContext, "{5A763774-CA8B-4245-A897-A03C503DCD60}", ReflectContext);
AZ_CLASS_ALLOCATOR(JsonRegistrationContext, SystemAllocator, 0);
class SerializerBuilder;
using SerializerMap = AZStd::unordered_map<Uuid, AZStd::unique_ptr<BaseJsonSerializer>, AZStd::hash<Uuid>>;

@ -0,0 +1,172 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Settings/ConfigurableStack.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/std/containers/queue.h>
#include <AzCore/std/tuple.h>
namespace AZ
{
AZ_CLASS_ALLOCATOR_IMPL(JsonConfigurableStackSerializer, AZ::SystemAllocator, 0);
JsonSerializationResult::Result JsonConfigurableStackSerializer::Load(
void* outputValue,
[[maybe_unused]] const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
return LoadFromArray(outputValue, inputValue, context);
case rapidjson::kObjectType:
return LoadFromObject(outputValue, inputValue, context);
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kNumberType:
return context.Report(
JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Configurable stack values can only be read from arrays or objects.");
default:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for string value.");
}
}
JsonSerializationResult::Result JsonConfigurableStackSerializer::Store(
[[maybe_unused]] rapidjson::Value& outputValue,
[[maybe_unused]] const void* inputValue,
[[maybe_unused]] const void* defaultValue,
[[maybe_unused]] const Uuid& valueTypeId,
JsonSerializerContext& context)
{
return context.Report(
JsonSerializationResult::Tasks::WriteValue, JsonSerializationResult::Outcomes::Unsupported,
"Configuration stacks can not be written out.");
}
void JsonConfigurableStackSerializer::Reflect(ReflectContext* context)
{
if (JsonRegistrationContext* jsonContext = azrtti_cast<JsonRegistrationContext*>(context))
{
jsonContext->Serializer<JsonConfigurableStackSerializer>()->HandlesType<ConfigurableStack>();
}
}
JsonSerializationResult::Result JsonConfigurableStackSerializer::LoadFromArray(
void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
auto stack = reinterpret_cast<ConfigurableStackInterface*>(outputValue);
const Uuid& nodeValueType = stack->GetNodeType();
JSR::ResultCode result(JSR::Tasks::ReadField);
uint32_t counter = 0;
for (auto& it : inputValue.GetArray())
{
ScopedContextPath subPath(context, counter);
void* value = stack->AddNode(AZStd::to_string(counter));
result.Combine(ContinueLoading(value, nodeValueType, it, context));
counter++;
}
return context.Report(
result,
result.GetProcessing() != JSR::Processing::Halted ? "Loaded configurable stack from array."
: "Failed to load configurable stack from array.");
}
JsonSerializationResult::Result JsonConfigurableStackSerializer::LoadFromObject(
void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
auto stack = reinterpret_cast<ConfigurableStackInterface*>(outputValue);
const Uuid& nodeValueType = stack->GetNodeType();
AZStd::queue<AZStd::tuple<ConfigurableStackInterface::InsertPosition, AZStd::string_view, rapidjson::Value::ConstMemberIterator>>
delayedEntries;
JSR::ResultCode result(JSR::Tasks::ReadField);
auto add = [&](ConfigurableStackInterface::InsertPosition position, rapidjson::Value::ConstMemberIterator target,
rapidjson::Value::ConstMemberIterator it)
{
if (target->value.IsString())
{
delayedEntries.emplace(position, AZStd::string_view(target->value.GetString(), target->value.GetStringLength()), it);
}
else
{
result.Combine(context.Report(
JSR::Tasks::ReadField, JSR::Outcomes::Skipped,
"Skipped value for the Configurable Stack because the target wasn't a string."));
}
};
// Load all the regular entries into the stack and store any with a before or after binding for
// later inserting.
for (auto it = inputValue.MemberBegin(); it != inputValue.MemberEnd(); ++it)
{
AZStd::string_view name(it->name.GetString(), it->name.GetStringLength());
ScopedContextPath subPath(context, name);
if (it->value.IsObject())
{
if (auto target = it->value.FindMember(StackBefore); target != it->value.MemberEnd())
{
add(ConfigurableStackInterface::InsertPosition::Before, target, it);
continue;
}
if (auto target = it->value.FindMember(StackAfter); target != it->value.MemberEnd())
{
add(ConfigurableStackInterface::InsertPosition::After, target, it);
continue;
}
}
void* value = stack->AddNode(name);
result.Combine(ContinueLoading(value, nodeValueType, it->value, context));
}
// Insert the entries that have been delayed.
while (!delayedEntries.empty())
{
auto&& [insertPosition, target, valueStore] = delayedEntries.front();
AZStd::string_view name(valueStore->name.GetString(), valueStore->name.GetStringLength());
ScopedContextPath subPath(context, name);
void* value = stack->AddNode(name, target, insertPosition);
if (value != nullptr)
{
result.Combine(ContinueLoading(value, nodeValueType, valueStore->value, context));
}
else
{
result.Combine(context.Report(
JSR::Tasks::ReadField, JSR::Outcomes::Skipped,
AZStd::string::format(
"Skipped value for the Configurable Stack because the target '%.*s' couldn't be found.", AZ_STRING_ARG(name))));
}
delayedEntries.pop();
}
return context.Report(
result,
result.GetProcessing() != JSR::Processing::Halted ? "Loaded configurable stack from array."
: "Failed to load configurable stack from array.");
}
} // namespace AZ

@ -0,0 +1,187 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/base.h>
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/utils.h>
namespace AZ
{
//! The ConfigurableStack makes configuring stacks and arrays through the Settings Registry easier than using direct serialization
//! to a container like AZStd::vector. It does this by using JSON Objects rather than JSON arrays, although arrays are supported
//! for backwards compatibility. Two key words were added:
//! - $stack_before : Insert the new entry before the referenced entry. Referencing is done by name.
//! - $stack_after : Insert the new entry after the referenced entry. Referencing is done by name.
//! to allow inserting new entries at specific locations. An example of a .setreg file at updates existing settings would be:
//! // Original settings
//! {
//! "Settings in a stack":
//! {
//! "AnOriginalEntry":
//! {
//! "MyValue": "hello",
//! "ExampleValue": 84
//! },
//! "TheSecondEntry":
//! {
//! "MyValue": "world"
//! }
//! }
//! }
//!
//! // Customized settings.
//! {
//! "Settings in a stack":
//! {
//! // Add a new entry before "AnOriginalEntry" in the original document.
//! "NewEntry":
//! {
//! "$stack_before": "AnOriginalEntry",
//! "MyValue": 42
//! },
//! // Add a second entry after "AnOriginalEntry" in the original document.
//! "SecondNewEntry":
//! {
//! "$stack_after": "AnOriginalEntry",
//! "MyValue": "FortyTwo".
//! },
//! // Update a value in "AnOriginalEntry".
//! "AnOriginalEntry":
//! {
//! "ExampleValue": 42
//! },
//! // Delete the "TheSecondEntry" from the settings.
//! "TheSecondEntry" : null,
//! }
//! }
//!
//! The ConfigurableStack uses an AZStd::shared_ptr to store the values. This supports settings up a base class and specifying
//! derived classes in the settings, but requires that the base and derived classes all have a memory allocator associated with
//! them (i.e. by using the "AZ_CLASS_ALLOCATOR" macro) and that the relation of the classes is reflected. Loading a
//! ConfigurableStack can be done using the GetObject call on the SettingsRegistryInterface.
class ReflectContext;
class ConfigurableStackInterface
{
public:
friend class JsonConfigurableStackSerializer;
virtual ~ConfigurableStackInterface() = default;
virtual const TypeId& GetNodeType() const = 0;
protected:
enum class InsertPosition
{
Before,
After
};
virtual void* AddNode(AZStd::string name) = 0;
virtual void* AddNode(AZStd::string name, AZStd::string_view target, InsertPosition position) = 0;
};
template<typename StackBaseType>
class ConfigurableStack final : public ConfigurableStackInterface
{
public:
using NodeValue = AZStd::shared_ptr<StackBaseType>;
using Node = AZStd::pair<AZStd::string, NodeValue>;
using NodeContainer = AZStd::vector<Node>;
using Iterator = typename NodeContainer::iterator;
using ConstIterator = typename NodeContainer::const_iterator;
~ConfigurableStack() override = default;
const TypeId& GetNodeType() const override;
Iterator begin();
Iterator end();
ConstIterator begin() const;
ConstIterator end() const;
size_t size() const;
protected:
void* AddNode(AZStd::string name) override;
void* AddNode(AZStd::string name, AZStd::string_view target, InsertPosition position) override;
private:
NodeContainer m_nodes;
};
AZ_TYPE_INFO_TEMPLATE(ConfigurableStack, "{0A3D2038-6E6A-4EFD-A1B4-F30D947E21B1}", AZ_TYPE_INFO_TYPENAME);
template<typename StackBaseType>
struct SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>
{
using ConfigurableStackType = ConfigurableStack<StackBaseType>;
class GenericConfigurableStackInfo : public GenericClassInfo
{
public:
AZ_TYPE_INFO(GenericConfigurableStackInfo, "{FC5A9353-D0DE-48F6-81B5-1CB2985C5F65}");
GenericConfigurableStackInfo();
SerializeContext::ClassData* GetClassData() override;
size_t GetNumTemplatedArguments() override;
const Uuid& GetTemplatedTypeId([[maybe_unused]] size_t element) override;
const Uuid& GetSpecializedTypeId() const override;
const Uuid& GetGenericTypeId() const override;
void Reflect(SerializeContext* serializeContext) override;
SerializeContext::ClassData m_classData;
};
using ClassInfoType = GenericConfigurableStackInfo;
static ClassInfoType* GetGenericInfo();
static const Uuid& GetClassTypeId();
};
class JsonConfigurableStackSerializer : public BaseJsonSerializer
{
public:
AZ_RTTI(JsonConfigurableStackSerializer, "{45A31805-9058-41A9-B1A3-71E2CB4D9237}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(
rapidjson::Value& outputValue,
const void* inputValue,
const void* defaultValue,
const Uuid& valueTypeId,
JsonSerializerContext& context) override;
static void Reflect(ReflectContext* context);
private:
JsonSerializationResult::Result LoadFromArray(
void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context);
JsonSerializationResult::Result LoadFromObject(
void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context);
static constexpr const char* StackBefore = "$stack_before";
static constexpr const char* StackAfter = "$stack_after";
};
} // namespace AZ
#include <AzCore/Settings/ConfigurableStack.inl>

@ -0,0 +1,149 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
namespace AZ
{
//
// ConfigurableStack
//
template<typename StackBaseType>
const TypeId& ConfigurableStack<StackBaseType>::GetNodeType() const
{
return azrtti_typeid<NodeValue>();
}
template<typename StackBaseType>
auto ConfigurableStack<StackBaseType>::begin() -> Iterator
{
return m_nodes.begin();
}
template<typename StackBaseType>
auto ConfigurableStack<StackBaseType>::end() -> Iterator
{
return m_nodes.end();
}
template<typename StackBaseType>
auto ConfigurableStack<StackBaseType>::begin() const -> ConstIterator
{
return m_nodes.begin();
}
template<typename StackBaseType>
auto ConfigurableStack<StackBaseType>::end() const -> ConstIterator
{
return m_nodes.end();
}
template<typename StackBaseType>
size_t ConfigurableStack<StackBaseType>::size() const
{
return m_nodes.size();
}
template<typename StackBaseType>
void* ConfigurableStack<StackBaseType>::AddNode(AZStd::string name)
{
Node& result = m_nodes.emplace_back(AZStd::move(name));
return &result.second;
}
template<typename StackBaseType>
void* ConfigurableStack<StackBaseType>::AddNode(AZStd::string name, AZStd::string_view target, InsertPosition position)
{
auto end = m_nodes.end();
for (auto it = m_nodes.begin(); it != end; ++it)
{
if (it->first == target)
{
if (position == InsertPosition::After)
{
++it;
}
auto result = m_nodes.insert(it, Node(AZStd::move(name), {}));
return &result->second;
}
}
return nullptr;
}
//
// SerializeGenericTypeInfo
//
template<typename StackBaseType>
SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::GenericConfigurableStackInfo()
: m_classData{ SerializeContext::ClassData::Create<ConfigurableStackType>(
"AZ::ConfigurableStack", GetSpecializedTypeId(), Internal::NullFactory::GetInstance(), nullptr, nullptr) }
{
}
template<typename StackBaseType>
SerializeContext::ClassData* SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::GetClassData()
{
return &m_classData;
}
template<typename StackBaseType>
size_t SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::GetNumTemplatedArguments()
{
return 1;
}
template<typename StackBaseType>
const Uuid& SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::GetTemplatedTypeId(
[[maybe_unused]] size_t element)
{
return SerializeGenericTypeInfo<StackBaseType>::GetClassTypeId();
}
template<typename StackBaseType>
const Uuid& SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::GetSpecializedTypeId() const
{
return azrtti_typeid<ConfigurableStackType>();
}
template<typename StackBaseType>
const Uuid& SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::GetGenericTypeId() const
{
return TYPEINFO_Uuid();
}
template<typename StackBaseType>
void SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::Reflect(SerializeContext* serializeContext)
{
if (serializeContext)
{
serializeContext->RegisterGenericClassInfo(GetSpecializedTypeId(), this, &AnyTypeInfoConcept<ConfigurableStackType>::CreateAny);
if (serializeContext->FindClassData(azrtti_typeid<AZStd::shared_ptr<StackBaseType>>()) == nullptr)
{
serializeContext->RegisterGenericType<AZStd::shared_ptr<StackBaseType>>();
}
}
}
template<typename StackBaseType>
auto SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GetGenericInfo() -> ClassInfoType*
{
return GetCurrentSerializeContextModule().CreateGenericClassInfo<ConfigurableStackType>();
}
template<typename StackBaseType>
const Uuid& SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GetClassTypeId()
{
return GetGenericInfo()->GetClassData()->m_typeId;
}
} // namespace AZ

@ -560,6 +560,9 @@ set(FILES
Serialization/std/VariantReflection.inl
Settings/CommandLine.cpp
Settings/CommandLine.h
Settings/ConfigurableStack.cpp
Settings/ConfigurableStack.inl
Settings/ConfigurableStack.h
Settings/SettingsRegistry.cpp
Settings/SettingsRegistry.h
Settings/SettingsRegistryConsoleUtils.cpp

@ -0,0 +1,281 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/JSON/document.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Settings/ConfigurableStack.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
struct ConfigInt
{
AZ_TYPE_INFO(UnitTest::ConfigInt, "{1FAF6E55-7FA4-4FFA-8C41-34F422B8E8AB}");
AZ_CLASS_ALLOCATOR(ConfigInt, AZ::SystemAllocator, 0);
int m_value;
static void Reflect(AZ::ReflectContext* context)
{
if (auto sc = azrtti_cast<AZ::SerializeContext*>(context))
{
sc->Class<ConfigInt>()->Field("Value", &ConfigInt::m_value);
}
}
};
struct ConfigurableStackTests : public ScopedAllocatorSetupFixture
{
void Reflect(AZ::ReflectContext* context)
{
if (auto sc = azrtti_cast<AZ::SerializeContext*>(context))
{
AZ::JsonSystemComponent::Reflect(sc);
ConfigInt::Reflect(sc);
sc->RegisterGenericType<AZ::ConfigurableStack<ConfigInt>>();
}
else if (auto jrc = azrtti_cast<AZ::JsonRegistrationContext*>(context))
{
AZ::JsonSystemComponent::Reflect(jrc);
}
}
void SetUp() override
{
ScopedAllocatorSetupFixture::SetUp();
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
m_jsonRegistrationContext = AZStd::make_unique<AZ::JsonRegistrationContext>();
Reflect(m_serializeContext.get());
Reflect(m_jsonRegistrationContext.get());
m_deserializationSettings.m_registrationContext = m_jsonRegistrationContext.get();
m_deserializationSettings.m_serializeContext = m_serializeContext.get();
}
void TearDown()
{
m_jsonRegistrationContext->EnableRemoveReflection();
Reflect(m_jsonRegistrationContext.get());
m_jsonRegistrationContext->DisableRemoveReflection();
m_serializeContext->EnableRemoveReflection();
Reflect(m_serializeContext.get());
m_serializeContext->DisableRemoveReflection();
ScopedAllocatorSetupFixture::TearDown();
}
void ObjectTest(AZStd::string_view jsonText)
{
AZ::ConfigurableStack<ConfigInt> stack;
rapidjson::Document document;
document.Parse(jsonText.data(), jsonText.length());
ASSERT_FALSE(document.HasParseError());
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(stack, document, m_deserializationSettings);
ASSERT_EQ(AZ::JsonSerializationResult::Processing::Completed, result.GetProcessing());
ASSERT_EQ(4, stack.size());
int numberCounter = 0;
int valueCounter = 42;
for (auto& [name, value] : stack)
{
EXPECT_STREQ(AZStd::string::format("Value%i", numberCounter).c_str(), name.c_str());
EXPECT_EQ(valueCounter, value->m_value);
numberCounter++;
valueCounter++;
}
}
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
AZStd::unique_ptr<AZ::JsonRegistrationContext> m_jsonRegistrationContext;
AZ::JsonDeserializerSettings m_deserializationSettings;
};
TEST_F(ConfigurableStackTests, DeserializeArray)
{
AZ::ConfigurableStack<ConfigInt> stack;
rapidjson::Document document;
document.Parse(
R"([
{ "Value": 42 },
{ "Value": 43 },
{ "Value": 44 },
{ "Value": 45 }
])");
ASSERT_FALSE(document.HasParseError());
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(stack, document, m_deserializationSettings);
ASSERT_EQ(AZ::JsonSerializationResult::Processing::Completed, result.GetProcessing());
ASSERT_EQ(4, stack.size());
int numberCounter = 0;
int valueCounter = 42;
for (auto& [name, value] : stack)
{
EXPECT_STREQ(AZStd::to_string(numberCounter).c_str(), name.c_str());
EXPECT_EQ(valueCounter, value->m_value);
numberCounter++;
valueCounter++;
}
}
TEST_F(ConfigurableStackTests, DeserializeObject)
{
ObjectTest(
R"({
"Value0": { "Value": 42 },
"Value1": { "Value": 43 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 }
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithLateBefore)
{
ObjectTest(
R"({
"Value0": { "Value": 42 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 },
"Value1":
{
"$stack_before": "Value2",
"Value": 43
}
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithEarlyBefore)
{
ObjectTest(
R"({
"Value1":
{
"$stack_before": "Value2",
"Value": 43
},
"Value0": { "Value": 42 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 }
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithLateAfter)
{
ObjectTest(
R"({
"Value0": { "Value": 42 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 },
"Value1":
{
"$stack_after": "Value0",
"Value": 43
}
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithEarlyAfter)
{
ObjectTest(
R"({
"Value1":
{
"$stack_after": "Value0",
"Value": 43
},
"Value0": { "Value": 42 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 }
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithBeforeFirst)
{
ObjectTest(
R"({
"Value1": { "Value": 43 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 },
"Value0":
{
"$stack_before": "Value1",
"Value": 42
}
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithInsertAfterLast)
{
ObjectTest(
R"({
"Value3":
{
"$stack_after": "Value2",
"Value": 45
},
"Value0": { "Value": 42 },
"Value1": { "Value": 43 },
"Value2": { "Value": 44 }
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithInvalidTarget)
{
AZ::ConfigurableStack<ConfigInt> stack;
rapidjson::Document document;
document.Parse(
R"({
"Value1":
{
"$stack_after": "airplane",
"Value": 43
},
"Value0": { "Value": 42 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 }
})");
ASSERT_FALSE(document.HasParseError());
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(stack, document, m_deserializationSettings);
EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, result.GetProcessing());
EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialSkip, result.GetOutcome());
EXPECT_EQ(3, stack.size());
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithInvalidTargetType)
{
AZ::ConfigurableStack<ConfigInt> stack;
rapidjson::Document document;
document.Parse(
R"({
"Value1":
{
"$stack_after": 42,
"Value": 43
},
"Value0": { "Value": 42 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 }
})");
ASSERT_FALSE(document.HasParseError());
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(stack, document, m_deserializationSettings);
EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, result.GetProcessing());
EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialSkip, result.GetOutcome());
EXPECT_EQ(3, stack.size());
}
} // namespace UnitTest

@ -76,6 +76,7 @@ set(FILES
Name/NameTests.cpp
RTTI/TypeSafeIntegralTests.cpp
Settings/CommandLineTests.cpp
Settings/ConfigurableStackTests.cpp
Settings/SettingsRegistryTests.cpp
Settings/SettingsRegistryConsoleUtilsTests.cpp
Settings/SettingsRegistryMergeUtilsTests.cpp

Loading…
Cancel
Save