You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Framework/AzCore/Tests/SettingsRegistryTests.cpp

1764 lines
78 KiB
C++

/*
* 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 <AzCore/Casting/numeric_cast.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
#include <AzCore/Settings/SettingsRegistryImpl.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AZTestShared/Utils/Utils.h>
namespace SettingsRegistryTests
{
class TestClass
{
public:
AZ_TYPE_INFO(TestClass, "{CDD9648A-27CA-4625-9FD9-DD3BB9CB093D}");
int m_var1 = { 42 };
double m_var2 = { 42.0 };
static TestClass Initialize()
{
TestClass result;
result.m_var1 = 88;
result.m_var2 = 88.0;
return result;
}
static void Reflect(AZ::SerializeContext& context)
{
context.Class<TestClass>()
->Field("Var1", &TestClass::m_var1)
->Field("Var2", &TestClass::m_var2);
}
};
struct RegistryEntry
{
AZStd::string_view m_path;
AZStd::string_view m_valueName;
AZ::SettingsRegistryInterface::Type m_type;
AZ::SettingsRegistryInterface::VisitAction m_action;
AZ::SettingsRegistryInterface::VisitResponse m_response;
RegistryEntry() = default;
RegistryEntry(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type type,
AZ::SettingsRegistryInterface::VisitAction action,
AZ::SettingsRegistryInterface::VisitResponse response = AZ::SettingsRegistryInterface::VisitResponse::Continue)
: m_path(path)
, m_valueName(valueName)
, m_type(type)
, m_action(action)
, m_response(response)
{}
};
class SettingsRegistryTest
: public UnitTest::AllocatorsFixture
{
public:
~SettingsRegistryTest() override = default;
static void DeleteFolderRecursive(const AZStd::string& path)
{
auto callback = [&path](const char* filename, bool isFile) -> bool
{
if (isFile)
{
AZStd::string filePath = path;
filePath += '/';
filePath += filename;
AZ::IO::SystemFile::Delete(filePath.c_str());
}
else
{
if (strcmp(filename, ".") != 0 && strcmp(filename, "..") != 0)
{
AZStd::string folderPath = path;
folderPath += '/';
folderPath += filename;
DeleteFolderRecursive(folderPath);
}
}
return true;
};
AZStd::string searchPath = path;
searchPath += "/*";
AZ::IO::SystemFile::FindFiles(searchPath.c_str(), callback);
AZ::IO::SystemFile::DeleteDir(path.c_str());
}
void SetUp() override
{
SetupAllocator();
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
m_registrationContext = AZStd::make_unique<AZ::JsonRegistrationContext>();
m_registry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
m_registry->SetContext(m_serializeContext.get());
m_registry->SetContext(m_registrationContext.get());
AZ::JsonSystemComponent::Reflect(m_registrationContext.get());
AZStd::string rootFolder = UnitTest::GetTestFolderPath();
m_testFolder = AZStd::make_unique<AZStd::string>(AZStd::string::format("%sSettingsRegistryTest_", rootFolder.c_str()));
m_testFolder->append(AZ::Uuid::CreateRandom().ToString<AZStd::string>(false, false));
}
void TearDown() override
{
if (m_hasTestFiles)
{
DeleteFolderRecursive(*m_testFolder);
}
m_registrationContext->EnableRemoveReflection();
AZ::JsonSystemComponent::Reflect(m_registrationContext.get());
m_registrationContext->DisableRemoveReflection();
m_testFolder.reset();
m_registry.reset();
m_registrationContext.reset();
m_serializeContext.reset();
TeardownAllocator();
}
AZStd::string CreateTestFile(AZStd::string_view name, AZStd::string_view content)
{
using namespace AZ::IO;
AZStd::string path = AZStd::string::format("%s/%s/%.*s", m_testFolder->c_str(),
AZ::SettingsRegistryInterface::RegistryFolder, static_cast<int>(name.length()), name.data());
SystemFile file;
if (!file.Open(path.c_str(), SystemFile::OpenMode::SF_OPEN_CREATE | SystemFile::SF_OPEN_CREATE_PATH | SystemFile::SF_OPEN_WRITE_ONLY))
{
AZ_Assert(false, "Unable to open test file for writing: %s", path.c_str());
return path;
}
if (file.Write(content.data(), content.size()) != content.size())
{
AZ_Assert(false, "Unable to write content to test file: %s", path.c_str());
}
m_hasTestFiles = true;
return path;
}
void Visit(const AZStd::vector<RegistryEntry>& expected, AZStd::string_view path = "")
{
size_t counter = 0;
auto callback = [&expected, &counter](AZStd::string_view path, AZStd::string_view valueName,
AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type type)
{
EXPECT_LT(counter, expected.size());
if (counter < expected.size())
{
const RegistryEntry& entry = expected[counter];
EXPECT_STREQ(entry.m_path.data(), path.data());
EXPECT_STREQ(entry.m_valueName.data(), valueName.data());
EXPECT_EQ(entry.m_action, action);
EXPECT_EQ(entry.m_type, type);
counter++;
return entry.m_response;
}
return AZ::SettingsRegistryInterface::VisitResponse::Done;
};
EXPECT_TRUE(m_registry->Visit(callback, path));
EXPECT_EQ(counter, expected.size());
}
void MergeNotify(AZStd::string_view path, size_t index, size_t fileIdLength, const char* counterId, const char** fileIds)
{
EXPECT_TRUE(path.empty());
AZ::s64 value = -1;
bool result = m_registry->Get(value, counterId);
EXPECT_TRUE(result);
EXPECT_EQ(index, value);
size_t i = 0;
for (; i <= index; ++i)
{
bool configValue = false;
result = m_registry->Get(configValue, fileIds[i]);
EXPECT_TRUE(result);
EXPECT_TRUE(configValue);
}
for (; i < fileIdLength; ++i)
{
AZ::SettingsRegistryInterface::Type type = m_registry->GetType(fileIds[i]);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, type);
}
}
AZStd::unique_ptr<AZ::SettingsRegistryImpl> m_registry;
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
AZStd::unique_ptr<AZ::JsonRegistrationContext> m_registrationContext;
AZStd::unique_ptr<AZStd::string> m_testFolder;
bool m_hasTestFiles{ false };
};
template<typename> struct SettingsType {};
// Json makes a distinction between true and false as different types, so test for both
// versions.
struct BoolTrue {};
struct BoolFalse {};
template<> struct SettingsType<BoolTrue>
{
using DataType = bool;
using ValueType = bool;
constexpr static AZ::SettingsRegistryInterface::Type s_type = AZ::SettingsRegistryInterface::Type::Boolean;
static AZStd::string_view GetStoredJson() { return "true"; }
static DataType GetStoredValue() { return true; }
static DataType GetDefaultValue() { return false; }
static void ExpectEq(ValueType lhs, ValueType rhs) { EXPECT_EQ(lhs, rhs); }
};
template<> struct SettingsType<BoolFalse>
{
using DataType = bool;
using ValueType = bool;
constexpr static AZ::SettingsRegistryInterface::Type s_type = AZ::SettingsRegistryInterface::Type::Boolean;
static AZStd::string_view GetStoredJson() { return "false"; }
static DataType GetStoredValue() { return false; }
static DataType GetDefaultValue() { return true; }
static void ExpectEq(ValueType lhs, ValueType rhs) { EXPECT_EQ(lhs, rhs); }
};
template<> struct SettingsType<AZ::s64>
{
using DataType = AZ::s64;
using ValueType = AZ::s64;
constexpr static AZ::SettingsRegistryInterface::Type s_type = AZ::SettingsRegistryInterface::Type::Integer;
static AZStd::string_view GetStoredJson() { return "-88"; }
static DataType GetStoredValue() { return -88; }
static DataType GetDefaultValue() { return 42; }
static void ExpectEq(ValueType lhs, ValueType rhs) { EXPECT_EQ(lhs, rhs); }
};
template<> struct SettingsType<double>
{
using DataType = double;
using ValueType = double;
constexpr static AZ::SettingsRegistryInterface::Type s_type = AZ::SettingsRegistryInterface::Type::FloatingPoint;
static AZStd::string_view GetStoredJson() { return "88.0"; }
static DataType GetStoredValue() { return 88.0; }
static DataType GetDefaultValue() { return 42.0; }
static void ExpectEq(DataType lhs, DataType rhs) { EXPECT_DOUBLE_EQ(lhs, rhs); }
};
template<> struct SettingsType<AZStd::string>
{
using DataType = AZStd::string;
using ValueType = AZStd::string_view;
constexpr static AZ::SettingsRegistryInterface::Type s_type = AZ::SettingsRegistryInterface::Type::String;
static AZStd::string_view GetStoredJson() { return R"("World")"; }
static DataType GetStoredValue() { return "World"; }
static DataType GetDefaultValue() { return AZStd::string{}; }
static void ExpectEq(ValueType lhs, ValueType rhs) { EXPECT_STREQ(lhs.data(), rhs.data()); }
};
template<> struct SettingsType<AZ::SettingsRegistryInterface::FixedValueString>
{
using DataType = AZ::SettingsRegistryInterface::FixedValueString;
using ValueType = AZStd::string_view;
constexpr static AZ::SettingsRegistryInterface::Type s_type = AZ::SettingsRegistryInterface::Type::String;
static AZStd::string_view GetStoredJson() { return R"("World")"; }
static DataType GetStoredValue() { return "World"; }
static DataType GetDefaultValue() { return DataType{}; }
static void ExpectEq(ValueType lhs, ValueType rhs) { EXPECT_STREQ(lhs.data(), rhs.data()); }
};
template<typename SettingsType>
class TypedSettingsRegistryTest
: public SettingsRegistryTest
{
public:
~TypedSettingsRegistryTest() override = default;
};
using SettingsTypes = ::testing::Types<
BoolTrue, BoolFalse, AZ::s64, double, AZStd::string, AZ::SettingsRegistryInterface::FixedValueString>;
TYPED_TEST_CASE(TypedSettingsRegistryTest, SettingsTypes);
TYPED_TEST(TypedSettingsRegistryTest, GetSet_SetAndGetValue_Success)
{
typename SettingsType<TypeParam>::DataType value = SettingsType<TypeParam>::GetStoredValue();
typename SettingsType<TypeParam>::DataType readValue = SettingsType<TypeParam>::GetDefaultValue();
AZStd::string_view testPath = "/Test/Path/Value";
ASSERT_TRUE(this->m_registry->Set(testPath, value));
ASSERT_TRUE(this->m_registry->Get(readValue, testPath));
SettingsType<TypeParam>::ExpectEq(value, readValue);
}
TYPED_TEST(TypedSettingsRegistryTest, Get_InvalidPath_ReturnsFalse)
{
typename SettingsType<TypeParam>::DataType readValue = SettingsType<TypeParam>::GetDefaultValue();
AZStd::string_view testPath = "#$%^";
EXPECT_FALSE(this->m_registry->Get(readValue, testPath));
}
TYPED_TEST(TypedSettingsRegistryTest, Get_UnknownPath_ReturnsFalse)
{
typename SettingsType<TypeParam>::DataType readValue = SettingsType<TypeParam>::GetDefaultValue();
AZStd::string_view testPath = "/Unknown/Path";
EXPECT_FALSE(this->m_registry->Get(readValue, testPath));
}
TYPED_TEST(TypedSettingsRegistryTest, Get_InvalidType_ReturnsFalse)
{
ASSERT_TRUE(this->m_registry->MergeSettings(R"({ "Object": { "Value": 42 } })", AZ::SettingsRegistryInterface::Format::JsonMergePatch));
typename SettingsType<TypeParam>::DataType readValue = SettingsType<TypeParam>::GetDefaultValue();
AZStd::string_view testPath = "/Object";
EXPECT_FALSE(this->m_registry->Get(readValue, testPath));
}
TYPED_TEST(TypedSettingsRegistryTest, Set_InvalidPath_ReturnsFalse)
{
typename SettingsType<TypeParam>::DataType value = SettingsType<TypeParam>::GetStoredValue();
AZStd::string_view testPath = "#$%^";
EXPECT_FALSE(this->m_registry->Set(testPath, value));
}
TYPED_TEST(TypedSettingsRegistryTest, Set_NotifiersCalled_ReturnsFalse)
{
size_t counter = 0;
auto callback0 = [&counter](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
EXPECT_STREQ("/Object/Value", path.data());
counter++;
};
auto callback1 = [&counter](AZStd::string_view, AZ::SettingsRegistryInterface::Type callbackType)
{
AZ::SettingsRegistryInterface::Type type = SettingsType<TypeParam>::s_type;
EXPECT_EQ(callbackType, type);
counter++;
};
auto testNotifier1 = this->m_registry->RegisterNotifier(callback0);
auto testNotifier2 = this->m_registry->RegisterNotifier(AZStd::move(callback1));
typename SettingsType<TypeParam>::DataType value = SettingsType<TypeParam>::GetStoredValue();
AZStd::string_view testPath = "/Object/Value";
EXPECT_TRUE(this->m_registry->Set(testPath, value));
EXPECT_EQ(2, counter);
}
TYPED_TEST(TypedSettingsRegistryTest, Set_ConnectedNotifierCalledAndDisconnectedNotifierNotCalled_Success)
{
size_t counter1{};
auto callback0 = [&counter1](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
EXPECT_STREQ("/Object/Value", path.data());
counter1++;
};
size_t counter2{};
auto callback1 = [&counter2](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
EXPECT_STREQ("/Object/Value", path.data());
counter2++;
};;
auto testNotifier1 = this->m_registry->RegisterNotifier(callback0);
auto testNotifier2 = this->m_registry->RegisterNotifier(AZStd::move(callback1));
typename SettingsType<TypeParam>::DataType value = SettingsType<TypeParam>::GetStoredValue();
AZStd::string_view testPath = "/Object/Value";
EXPECT_TRUE(this->m_registry->Set(testPath, value));
EXPECT_EQ(1, counter1);
EXPECT_EQ(1, counter2);
// Disconnect the second Notifier
testNotifier2.Disconnect();
EXPECT_TRUE(this->m_registry->Set(testPath, SettingsType<TypeParam>::GetDefaultValue()));
EXPECT_EQ(2, counter1);
EXPECT_EQ(1, counter2);
}
TYPED_TEST(TypedSettingsRegistryTest, GetType_TypeForTheStoredValue_TypeMatchesProvidedType)
{
typename SettingsType<TypeParam>::DataType value = SettingsType<TypeParam>::GetStoredValue();
AZ::SettingsRegistryInterface::Type type = SettingsType<TypeParam>::s_type;
AZStd::string_view testPath = "/Test/Path/Value";
ASSERT_TRUE(this->m_registry->Set(testPath, value));
EXPECT_EQ(type, this->m_registry->GetType(testPath));
}
TYPED_TEST(TypedSettingsRegistryTest, VisitWithVisitor_VisitingObject_ValuesInJsonAreVisited)
{
AZStd::string_view storedJson = SettingsType<TypeParam>::GetStoredJson();
AZStd::string json = AZStd::string::format(
R"({
"Test":
{
"Object":{ "Type": %.*s }
}
})", aznumeric_cast<int>(storedJson.length()), storedJson.data());
ASSERT_TRUE(this->m_registry->MergeSettings(json.c_str(), AZ::SettingsRegistryInterface::Format::JsonMergePatch));
struct : public AZ::SettingsRegistryInterface::Visitor
{
using ValueType [[maybe_unused]] = typename SettingsType<TypeParam>::ValueType;
void Visit([[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type type, ValueType value) override
{
AZ::SettingsRegistryInterface::Type expectedType = SettingsType<TypeParam>::s_type;
EXPECT_EQ(expectedType, type);
SettingsType<TypeParam>::ExpectEq(SettingsType<TypeParam>::GetStoredValue(), value);
m_counter++;
}
size_t m_counter{ 0 };
} visitor;
EXPECT_TRUE(this->m_registry->Visit(visitor, "/Test"));
EXPECT_EQ(1, visitor.m_counter);
}
TYPED_TEST(TypedSettingsRegistryTest, VisitWithVisitor_VisitingArray_ValuesInJsonAreVisited)
{
AZStd::string_view storedJson = SettingsType<TypeParam>::GetStoredJson();
AZStd::string json = AZStd::string::format(
R"({
"Test":
{
"Array":[ %.*s ]
}
})", aznumeric_cast<int>(storedJson.length()), storedJson.data());
ASSERT_TRUE(this->m_registry->MergeSettings(json.c_str(), AZ::SettingsRegistryInterface::Format::JsonMergePatch));
struct : public AZ::SettingsRegistryInterface::Visitor
{
using ValueType [[maybe_unused]] = typename SettingsType<TypeParam>::ValueType;
void Visit([[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type type, ValueType value) override
{
AZ::SettingsRegistryInterface::Type expectedType = SettingsType<TypeParam>::s_type;
EXPECT_EQ(expectedType, type);
SettingsType<TypeParam>::ExpectEq(SettingsType<TypeParam>::GetStoredValue(), value);
m_counter++;
}
size_t m_counter{ 0 };
} visitor;
EXPECT_TRUE(this->m_registry->Visit(visitor, "/Test"));
EXPECT_EQ(1, visitor.m_counter);
}
TEST_F(SettingsRegistryTest, VisitWithVisitor_VisitingIntegerGreaterThanNumericLimitsOfSigned64Bit_SuppliesCorrectBitPattern)
{
// Create a 64-bit value that is greater than what can be represented in a signed int64_t
constexpr AZ::u64 unsigned64BitValue = aznumeric_cast<AZ::u64>((std::numeric_limits<AZ::s64>::max)()) + 1;
AZStd::string json = AZStd::string::format(
R"({
"Test":
{
"Object":{ "Type": %llu }
}
})", unsigned64BitValue);
ASSERT_TRUE(this->m_registry->MergeSettings(json.c_str(), AZ::SettingsRegistryInterface::Format::JsonMergePatch));
struct : public AZ::SettingsRegistryInterface::Visitor
{
void Visit([[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type type, AZ::s64 value) override
{
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Integer, type);
AZ::u64 testValue = reinterpret_cast<AZ::u64&>(value);
EXPECT_EQ(expectedValue, testValue);
}
const AZ::u64 expectedValue{ unsigned64BitValue };
} visitor;
EXPECT_TRUE(this->m_registry->Visit(visitor, "/Test/Object/Type"));
}
TEST_F(SettingsRegistryTest, VisitWithVisitor_EndOfPathArguments_MatchesValueNameArgument_ForAllIterations)
{
// Validate that every invocation of the Traverse and Visit command supplies a 'path' parameter
// whose end matches that of the 'valueName' parameter
AZStd::string json{
R"({
"Test":
{
"Object":{ "Type": "TestString" }
}
})"
};
ASSERT_TRUE(this->m_registry->MergeSettings(json.c_str(), AZ::SettingsRegistryInterface::Format::JsonMergePatch));
struct : public AZ::SettingsRegistryInterface::Visitor
{
AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName,
AZ::SettingsRegistryInterface::VisitAction, AZ::SettingsRegistryInterface::Type) override
{
EXPECT_TRUE(path.ends_with(valueName));
return AZ::SettingsRegistryInterface::VisitResponse::Continue;
}
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type , AZStd::string_view)override
{
EXPECT_TRUE(path.ends_with(valueName));
}
} visitor;
EXPECT_TRUE(this->m_registry->Visit(visitor, ""));
EXPECT_TRUE(this->m_registry->Visit(visitor, "/Test"));
}
//
// Object
//
TEST_F(SettingsRegistryTest, GetSetObject_SetAndGetValue_Success)
{
TestClass::Reflect(*m_serializeContext);
AZStd::string_view testPath = "/Test/Path/Value";
TestClass value;
value.m_var1 = 88;
value.m_var2 = 88.0;
TestClass readValue;
ASSERT_TRUE(m_registry->SetObject(testPath, &value, azrtti_typeid(value)));
ASSERT_TRUE(m_registry->GetObject(&readValue, azrtti_typeid(readValue), testPath));
EXPECT_EQ(value.m_var1, readValue.m_var1);
EXPECT_DOUBLE_EQ(value.m_var2, readValue.m_var2);
m_serializeContext->EnableRemoveReflection();
TestClass::Reflect(*m_serializeContext);
m_serializeContext->DisableRemoveReflection();
}
TEST_F(SettingsRegistryTest, GetObject_InvalidPath_ReturnsFalse)
{
TestClass::Reflect(*m_serializeContext);
AZStd::string_view testPath = "$%^&";
TestClass readValue;
EXPECT_FALSE(m_registry->GetObject(&readValue, azrtti_typeid(readValue), testPath));
m_serializeContext->EnableRemoveReflection();
TestClass::Reflect(*m_serializeContext);
m_serializeContext->DisableRemoveReflection();
}
TEST_F(SettingsRegistryTest, GetObject_UnknownPath_ReturnsFalse)
{
TestClass::Reflect(*m_serializeContext);
AZStd::string_view testPath = "/Unknown/Path";
TestClass readValue;
EXPECT_FALSE(m_registry->GetObject(&readValue, azrtti_typeid(readValue), testPath));
m_serializeContext->EnableRemoveReflection();
TestClass::Reflect(*m_serializeContext);
m_serializeContext->DisableRemoveReflection();
}
TEST_F(SettingsRegistryTest, SetObject_InvalidPath_ReturnFalse)
{
TestClass::Reflect(*m_serializeContext);
AZStd::string_view testPath = "$%^&";
TestClass value;
EXPECT_FALSE(m_registry->SetObject(testPath, &value, azrtti_typeid(value)));
m_serializeContext->EnableRemoveReflection();
TestClass::Reflect(*m_serializeContext);
m_serializeContext->DisableRemoveReflection();
}
TEST_F(SettingsRegistryTest, SetObject_NotifiersCalled_ReturnsFalse)
{
size_t counter = 0;
auto callback0 = [&counter](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
EXPECT_STREQ("/Object/Value", path.data());
counter++;
};
auto callback1 = [&counter](AZStd::string_view, AZ::SettingsRegistryInterface::Type callbackType)
{
EXPECT_EQ(callbackType, AZ::SettingsRegistryInterface::Type::Object);
counter++;
};
auto testNotifier1 = m_registry->RegisterNotifier(callback0);
auto testNotifier2 = m_registry->RegisterNotifier(AZStd::move(callback1));
TestClass::Reflect(*m_serializeContext);
AZStd::string_view testPath = "/Object/Value";
TestClass value;
EXPECT_TRUE(m_registry->SetObject(testPath, &value, azrtti_typeid(value)));
EXPECT_EQ(2, counter);
m_serializeContext->EnableRemoveReflection();
TestClass::Reflect(*m_serializeContext);
m_serializeContext->DisableRemoveReflection();
}
//
// Remove
//
TYPED_TEST(TypedSettingsRegistryTest, Remove_SetAndRemoveValue_Success)
{
typename SettingsType<TypeParam>::DataType value = SettingsType<TypeParam>::GetStoredValue();
typename SettingsType<TypeParam>::DataType newValue = SettingsType<TypeParam>::GetDefaultValue();
AZStd::string_view testPath = "/Test/Path/Value";
EXPECT_TRUE(this->m_registry->Set(testPath, value));
EXPECT_TRUE(this->m_registry->Get(newValue, testPath));
SettingsType<TypeParam>::ExpectEq(value, newValue);
EXPECT_TRUE(this->m_registry->Remove(testPath));
typename SettingsType<TypeParam>::DataType notFoundValue{};
EXPECT_FALSE(this->m_registry->Get(notFoundValue, testPath));
}
TYPED_TEST(TypedSettingsRegistryTest, Remove_InvalidPath_ReturnsFalse)
{
AZStd::string_view testPath = "#$%^";
EXPECT_FALSE(this->m_registry->Remove(testPath));
typename SettingsType<TypeParam>::DataType notFoundValue{};
EXPECT_FALSE(this->m_registry->Get(notFoundValue, testPath));
}
TYPED_TEST(TypedSettingsRegistryTest, Remove_UnknownPath_ReturnsFalse)
{
AZStd::string_view testPath = "/Unknown/Path";
EXPECT_FALSE(this->m_registry->Remove(testPath));
typename SettingsType<TypeParam>::DataType notFoundValue{};
EXPECT_FALSE(this->m_registry->Get(notFoundValue, testPath));
}
//
// Specializations::Append
//
TEST_F(SettingsRegistryTest, SpecializationsAppend_TooManySpecializations_ReportsErrorAndReturnsFalse)
{
AZ::SettingsRegistryInterface::Specializations specializations;
for (size_t i = 0; i < AZ::SettingsRegistryInterface::Specializations::MaxCount; ++i)
{
specializations.Append("test");
}
AZ_TEST_START_TRACE_SUPPRESSION;
bool result = specializations.Append("TooFar");
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_FALSE(result);
}
//
// GetType
// Note: The typed tests already test several GetType versions.
//
TEST_F(SettingsRegistryTest, GetType_TypeForObject_TypeMatchesProvidedType)
{
ASSERT_TRUE(m_registry->MergeSettings(R"({ "Object": { "Value": 42 } })", AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::string_view testPath = "/Object";
EXPECT_EQ(AZ::SettingsRegistryImpl::Type::Object, m_registry->GetType(testPath));
}
TEST_F(SettingsRegistryTest, GetType_TypeForArray_TypeMatchesProvidedType)
{
ASSERT_TRUE(m_registry->MergeSettings(R"({ "Array": [ 42 ] })", AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::string_view testPath = "/Array";
EXPECT_EQ(AZ::SettingsRegistryImpl::Type::Array, m_registry->GetType(testPath));
}
TEST_F(SettingsRegistryTest, GetType_TypeForNull_TypeMatchesProvidedType)
{
ASSERT_TRUE(m_registry->MergeSettings(
R"( [
{ "op": "add", "path": "/Object", "value": {"Value": null} }
])",
AZ::SettingsRegistryInterface::Format::JsonPatch));
AZStd::string_view testPath = "/Object/Value";
EXPECT_EQ(AZ::SettingsRegistryImpl::Type::Null, m_registry->GetType(testPath));
}
TEST_F(SettingsRegistryTest, GetType_InvalidPath_ReturnNone)
{
AZ::SettingsRegistryInterface::Type type = m_registry->GetType("#$%");
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, type);
}
TEST_F(SettingsRegistryTest, GetType_UnknownPath_ReturnNone)
{
AZ::SettingsRegistryInterface::Type type = m_registry->GetType("/Unknown/Path");
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, type);
}
//
// Visit
//
TEST_F(SettingsRegistryTest, VisitWithVisitor_InvalidPath_ReturnsFalse)
{
struct : public AZ::SettingsRegistryInterface::Visitor
{
} visitor;
EXPECT_FALSE(m_registry->Visit(visitor, "#$%"));
}
TEST_F(SettingsRegistryTest, VisitWithVisitor_UnknownPath_ReturnsFalse)
{
struct : public AZ::SettingsRegistryInterface::Visitor
{
} visitor;
EXPECT_FALSE(m_registry->Visit(visitor, "/Unknown/Path"));
}
TEST_F(SettingsRegistryTest, VisitWithCallback_FullIteration_AllFieldsAreReported)
{
using SRI = AZ::SettingsRegistryInterface;
ASSERT_TRUE(m_registry->MergeSettings(
R"( [
{ "op": "add", "path": "/Test", "value": { "Object": {} } },
{ "op": "add", "path": "/Test/Object/NullType", "value": null },
{ "op": "add", "path": "/Test/Object/TrueType", "value": true },
{ "op": "add", "path": "/Test/Object/FalseType", "value": false },
{ "op": "add", "path": "/Test/Object/IntType", "value": -42 },
{ "op": "add", "path": "/Test/Object/UIntType", "value": 42 },
{ "op": "add", "path": "/Test/Object/DoubleType", "value": 42.0 },
{ "op": "add", "path": "/Test/Object/StringType", "value": "Hello world" },
{ "op": "add", "path": "/Test/Array", "value": [ null, true, false, -42, 42, 42.0, "Hello world" ] }
])", SRI::Format::JsonPatch));
AZStd::vector<RegistryEntry> expected =
{
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Test/Object", "Object", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Test/Object/NullType", "NullType", SRI::Type::Null, SRI::VisitAction::Value),
RegistryEntry("/Test/Object/TrueType", "TrueType", SRI::Type::Boolean, SRI::VisitAction::Value),
RegistryEntry("/Test/Object/FalseType", "FalseType", SRI::Type::Boolean, SRI::VisitAction::Value),
RegistryEntry("/Test/Object/IntType", "IntType", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Object/UIntType", "UIntType", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Object/DoubleType", "DoubleType", SRI::Type::FloatingPoint, SRI::VisitAction::Value),
RegistryEntry("/Test/Object/StringType", "StringType", SRI::Type::String, SRI::VisitAction::Value),
RegistryEntry("/Test/Object", "Object", SRI::Type::Object, SRI::VisitAction::End),
RegistryEntry("/Test/Array", "Array", SRI::Type::Array, SRI::VisitAction::Begin),
RegistryEntry("/Test/Array/0", "0", SRI::Type::Null, SRI::VisitAction::Value),
RegistryEntry("/Test/Array/1", "1", SRI::Type::Boolean, SRI::VisitAction::Value),
RegistryEntry("/Test/Array/2", "2", SRI::Type::Boolean, SRI::VisitAction::Value),
RegistryEntry("/Test/Array/3", "3", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Array/4", "4", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Array/5", "5", SRI::Type::FloatingPoint, SRI::VisitAction::Value),
RegistryEntry("/Test/Array/6", "6", SRI::Type::String, SRI::VisitAction::Value),
RegistryEntry("/Test/Array", "Array", SRI::Type::Array, SRI::VisitAction::End),
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::End)
};
Visit(expected, "/Test");
}
TEST_F(SettingsRegistryTest, VisitWithCallback_IterateWithOffsetInObject_AllFieldsInSubSectionAreListed)
{
using SRI = AZ::SettingsRegistryInterface;
ASSERT_TRUE(m_registry->MergeSettings(
R"({
"Layer1":
{
"Layer2":
{
"Layer3":
{
"StringType": "Hello World"
}
}
}
})", SRI::Format::JsonMergePatch));
AZStd::vector<RegistryEntry> expected =
{
RegistryEntry("/Layer1/Layer2", "Layer2", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Layer1/Layer2/Layer3", "Layer3", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Layer1/Layer2/Layer3/StringType", "StringType", SRI::Type::String, SRI::VisitAction::Value),
RegistryEntry("/Layer1/Layer2/Layer3", "Layer3", SRI::Type::Object, SRI::VisitAction::End),
RegistryEntry("/Layer1/Layer2", "Layer2", SRI::Type::Object, SRI::VisitAction::End)
};
Visit(expected, "/Layer1/Layer2");
}
TEST_F(SettingsRegistryTest, VisitWithCallback_IterateWithOffsetInArray_AllFieldsInSubSectionAreListed)
{
using SRI = AZ::SettingsRegistryInterface;
ASSERT_TRUE(m_registry->MergeSettings(
R"({
"Object":
[
[
[
"Hello World"
]
]
]
})", SRI::Format::JsonMergePatch));
AZStd::vector<RegistryEntry> expected =
{
RegistryEntry("/Object/0/0", "0", SRI::Type::Array, SRI::VisitAction::Begin),
RegistryEntry("/Object/0/0/0", "0", SRI::Type::String, SRI::VisitAction::Value),
RegistryEntry("/Object/0/0", "0", SRI::Type::Array, SRI::VisitAction::End)
};
Visit(expected, "/Object/0/0");
}
TEST_F(SettingsRegistryTest, VisitWithCallback_SkipObject_FirstAndThirdObjectOnly)
{
using SRI = AZ::SettingsRegistryInterface;
ASSERT_TRUE(m_registry->MergeSettings(
R"({
"Test":
{
"Object0": { "Field" : 142 },
"Object1": { "Field" : 242 },
"Object2": { "Field" : 342 }
}
})", SRI::Format::JsonMergePatch));
AZStd::vector<RegistryEntry> expected =
{
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Test/Object0/Field", "Field", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::End),
RegistryEntry("/Test/Object1", "Object1", SRI::Type::Object, SRI::VisitAction::Begin, SRI::VisitResponse::Skip),
RegistryEntry("/Test/Object2", "Object2", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Test/Object2/Field", "Field", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Object2", "Object2", SRI::Type::Object, SRI::VisitAction::End),
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::End)
};
Visit(expected, "/Test");
}
TEST_F(SettingsRegistryTest, VisitWithCallback_SkipArray_FirstAndThirdObjectOnly)
{
using SRI = AZ::SettingsRegistryInterface;
ASSERT_TRUE(m_registry->MergeSettings(
R"({
"Test":
{
"Array0": [ 142, 188 ],
"Array1": [ 242, 288 ],
"Array2": [ 342, 388 ]
}
})", SRI::Format::JsonMergePatch));
AZStd::vector<RegistryEntry> expected =
{
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::Begin),
RegistryEntry("/Test/Array0/0", "0", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Array0/1", "1", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::End),
RegistryEntry("/Test/Array1", "Array1", SRI::Type::Array, SRI::VisitAction::Begin, SRI::VisitResponse::Skip),
RegistryEntry("/Test/Array2", "Array2", SRI::Type::Array, SRI::VisitAction::Begin),
RegistryEntry("/Test/Array2/0", "0", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Array2/1", "1", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Array2", "Array2", SRI::Type::Array, SRI::VisitAction::End),
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::End)
};
Visit(expected, "/Test");
}
TEST_F(SettingsRegistryTest, VisitWithCallback_StopMidwayThroughObject_FirstObjectOnly)
{
using SRI = AZ::SettingsRegistryInterface;
ASSERT_TRUE(m_registry->MergeSettings(
R"({
"Test":
{
"Object0": { "Field" : 142 },
"Object1": { "Field" : 242 },
"Object2": { "Field" : 342 }
}
})", SRI::Format::JsonMergePatch));
AZStd::vector<RegistryEntry> expected =
{
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Test/Object0/Field", "Field", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::End),
RegistryEntry("/Test/Object1", "Object1", SRI::Type::Object, SRI::VisitAction::Begin, SRI::VisitResponse::Done)
};
Visit(expected, "/Test");
}
TEST_F(SettingsRegistryTest, VisitWithCallback_StopOnObjectEnd_FirstObjectOnly)
{
using SRI = AZ::SettingsRegistryInterface;
ASSERT_TRUE(m_registry->MergeSettings(
R"({
"Test":
{
"Object0": { "Field" : 142 },
"Object1": { "Field" : 242 },
"Object2": { "Field" : 342 }
}
})", SRI::Format::JsonMergePatch));
AZStd::vector<RegistryEntry> expected =
{
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Test/Object0/Field", "Field", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::End, SRI::VisitResponse::Done)
};
Visit(expected, "/Test");
}
TEST_F(SettingsRegistryTest, VisitWithCallback_StopMidwayThroughArray_FirstObjectOnly)
{
using SRI = AZ::SettingsRegistryInterface;
ASSERT_TRUE(m_registry->MergeSettings(
R"({
"Test":
{
"Array0": [ 142, 188 ],
"Array1": [ 242, 288 ],
"Array2": [ 342, 388 ]
}
})", SRI::Format::JsonMergePatch));
AZStd::vector<RegistryEntry> expected =
{
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::Begin),
RegistryEntry("/Test/Array0/0", "0", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Array0/1", "1", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::End),
RegistryEntry("/Test/Array1", "Array1", SRI::Type::Array, SRI::VisitAction::Begin),
RegistryEntry("/Test/Array1/0", "0", SRI::Type::Integer, SRI::VisitAction::Value, SRI::VisitResponse::Done),
};
Visit(expected, "/Test");
}
TEST_F(SettingsRegistryTest, VisitWithCallback_StopOnArrayEnd_FirstObjectOnly)
{
using SRI = AZ::SettingsRegistryInterface;
ASSERT_TRUE(m_registry->MergeSettings(
R"({
"Test":
{
"Array0": [ 142, 188 ],
"Array1": [ 242, 288 ],
"Array2": [ 342, 388 ]
}
})", SRI::Format::JsonMergePatch));
AZStd::vector<RegistryEntry> expected =
{
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::Begin),
RegistryEntry("/Test/Array0/0", "0", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Array0/1", "1", SRI::Type::Integer, SRI::VisitAction::Value),
RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::End, SRI::VisitResponse::Done)
};
Visit(expected, "/Test");
}
TEST_F(SettingsRegistryTest, VisitWithCallback_InvalidPath_ReturnsFalse)
{
auto callback = [](AZStd::string_view, AZStd::string_view,
AZ::SettingsRegistryInterface::VisitAction, AZ::SettingsRegistryInterface::Type)
{
return AZ::SettingsRegistryInterface::VisitResponse::Continue;
};
EXPECT_FALSE(m_registry->Visit(callback, "#$%"));
}
TEST_F(SettingsRegistryTest, VisitWithCallback_UnknownPath_ReturnsFalse)
{
auto callback = [](AZStd::string_view, AZStd::string_view,
AZ::SettingsRegistryInterface::VisitAction, AZ::SettingsRegistryInterface::Type)
{
return AZ::SettingsRegistryInterface::VisitResponse::Continue;
};
EXPECT_FALSE(m_registry->Visit(callback, "/Unknown/Path"));
}
TEST_F(SettingsRegistryTest, VisitWithCallback_WithEmptyStringViewPath_DoesNotCrash)
{
auto callback = [](AZStd::string_view, AZStd::string_view,
AZ::SettingsRegistryInterface::VisitAction, AZ::SettingsRegistryInterface::Type)
{
return AZ::SettingsRegistryInterface::VisitResponse::Continue;
};
EXPECT_TRUE(m_registry->Visit(callback, {}));
}
//
// MergeCommandLineArgument
//
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SetStringArgument_ValueAddedToRegistry)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=Value", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SetStringWithSpaceArgument_ValueAddedToRegistry)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=Value of test", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_STREQ("Value of test", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SetIntegerArgument_ValueAddedToRegistry)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=42", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Integer, m_registry->GetType("/Test"));
AZ::s64 value;
ASSERT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_EQ(42, value);
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SetFloatingPointArgument_ValueAddedToRegistry)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=42.0", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::FloatingPoint, m_registry->GetType("/Test"));
double value;
ASSERT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_EQ(42.0, value);
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SetTrueArgument_ValueAddedToRegistry)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=true", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Boolean, m_registry->GetType("/Test"));
bool value;
ASSERT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_TRUE(value);
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SetFalseArgument_ValueAddedToRegistry)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=false", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Boolean, m_registry->GetType("/Test"));
bool value;
ASSERT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_FALSE(value);
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyWithPath_ValueAddedToRegistry)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("/Parent/Test=Value", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Parent/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Parent/Test"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyAndPath_ValueAddedToRegistry)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=Value", "/Parent", {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Parent/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Parent/Test"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyWithDividerAndPath_ValueAddedToRegistry)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("/Test=Value", "/Parent", {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Parent/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Parent/Test"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyAndPathWithDivider_ValueAddedToRegistry)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=Value", "/Parent/", {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Parent/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Parent/Test"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyWithDividerAndPathWithDivider_ValueAddedToRegistry)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("/Test=Value", "/Parent/", {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Parent/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Parent/Test"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesBeforeKey_SpacesAreRemoved)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument(" Test=Value", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesAfterKey_SpacesAreRemoved)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test =Value", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesBeforeAndAfterKey_SpacesAreRemoved)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument(" Test =Value", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesInKey_SpacesAreNotRemoved)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument(" Test Key =Value", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test Key"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Test Key"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesBeforeValue_SpacesAreRemoved)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test= Value", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesAfterValue_SpacesAreRemoved)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=Value ", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesBeforeAndAfterValue_SpacesAreRemoved)
{
ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test= Value ", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
AZStd::string value;
ASSERT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_STREQ("Value", value.c_str());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyIsTooLong_ReturnsFalse)
{
constexpr int LongKeySize = 1024;
AZStd::string argument = AZStd::string::format("Te%*cst=Value", LongKeySize, ' ');
EXPECT_FALSE(m_registry->MergeCommandLineArgument(argument, {}, {}));
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyIsTooLongWithDivider_ReturnsFalse)
{
constexpr int LongKeySize = 1024;
AZStd::string argument = AZStd::string::format("/Te%*cst=Value", LongKeySize, ' ');
EXPECT_FALSE(m_registry->MergeCommandLineArgument(argument, "/Path", {}));
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_ValueIsTooLong_ReturnsFalse)
{
constexpr int LongValueSize = 1024;
AZStd::string argument = AZStd::string::format("Test=Val%*cue", LongValueSize, ' ');
EXPECT_FALSE(m_registry->MergeCommandLineArgument(argument, {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType("/Test"));
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_MissingValue_ReturnsEmptyString)
{
EXPECT_TRUE(m_registry->MergeCommandLineArgument("Test=", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
AZ::SettingsRegistryInterface::FixedValueString value;
EXPECT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_TRUE(value.empty());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_MissingKey_ReturnsFalse)
{
EXPECT_FALSE(m_registry->MergeCommandLineArgument("=Value", {}, {}));
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_MissingKeyAndValue_ReturnsFalse)
{
EXPECT_FALSE(m_registry->MergeCommandLineArgument("=", {}, {}));
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_EmptyString_ReturnsFalse)
{
EXPECT_FALSE(m_registry->MergeCommandLineArgument("", {}, {}));
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyIsSpaces_ReturnsFalse)
{
EXPECT_FALSE(m_registry->MergeCommandLineArgument(" =Value", {}, {}));
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_ValueIsSpaces_ReturnsEmptyString)
{
EXPECT_TRUE(m_registry->MergeCommandLineArgument("Key= ", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Key"));
AZ::SettingsRegistryInterface::FixedValueString value;
EXPECT_TRUE(m_registry->Get(value, "/Key"));
EXPECT_TRUE(value.empty());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyAndValueAreSpaces_ReturnsFalse)
{
EXPECT_FALSE(m_registry->MergeCommandLineArgument(" = ", {}, {}));
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_OnlySpaces_ReturnsFalse)
{
EXPECT_FALSE(m_registry->MergeCommandLineArgument(" ", {}, {}));
}
//
// MergeSettingsFile
//
TEST_F(SettingsRegistryTest, MergeSettingsFile_MergeTestFile_PatchAppliedAndReported)
{
AZStd::string path = CreateTestFile("test.setreg", R"({ "Test": 1 })");
auto callback = [this](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
EXPECT_TRUE(path.empty());
AZ::s64 value = -1;
bool result = m_registry->Get(value, "/Test");
EXPECT_TRUE(result);
EXPECT_EQ(1, value);
};
auto testNotifier1 = m_registry->RegisterNotifier(callback);
bool result = m_registry->MergeSettingsFile(path, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
ASSERT_TRUE(result);
AZStd::string history;
result = m_registry->Get(history, AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0");
ASSERT_TRUE(result);
EXPECT_STREQ(path.c_str(), history.c_str());
}
TEST_F(SettingsRegistryTest, MergeSettingsFile_MergeTestFileWithRootKey_PatchAppliedAndReported)
{
AZStd::string path = CreateTestFile("test.setreg", R"({ "Test": 1 })");
auto callback = [this](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
EXPECT_TRUE(path.empty());
AZ::s64 value = -1;
bool result = m_registry->Get(value, "/Path/Test");
EXPECT_TRUE(result);
EXPECT_EQ(1, value);
};
auto testNotifier1 = m_registry->RegisterNotifier(callback);
bool result = m_registry->MergeSettingsFile(path, AZ::SettingsRegistryInterface::Format::JsonMergePatch, "/Path", nullptr);
ASSERT_TRUE(result);
AZStd::string history;
result = m_registry->Get(history, AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0");
ASSERT_TRUE(result);
EXPECT_STREQ(path.c_str(), history.c_str());
}
TEST_F(SettingsRegistryTest, MergeSettingsFile_MergeTestFileWithBuffer_PatchAppliedAndReported)
{
AZStd::string path = CreateTestFile("test.setreg", R"({ "Test": 1 })");
AZStd::vector<char> buffer;
buffer.push_back(32);
buffer.push_back(64);
bool result = m_registry->MergeSettingsFile(path, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, &buffer);
EXPECT_TRUE(result);
EXPECT_TRUE(buffer.empty());
}
TEST_F(SettingsRegistryTest, MergeSettingsFile_EmptyPath_ReturnsFalse)
{
AZ_TEST_START_TRACE_SUPPRESSION;
bool result = m_registry->MergeSettingsFile("", AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_FALSE(result);
}
TEST_F(SettingsRegistryTest, MergeSettingsFile_PathAsSubString_ReturnsTrue)
{
AZStd::string path = AZStd::string::format("%s/%s/test.setreg1234",
m_testFolder->c_str(), AZ::SettingsRegistryInterface::RegistryFolder);
CreateTestFile("test.setreg", R"({ "Test": 1 })");
AZStd::string_view subPath(path.c_str(), path.length() - 4);
bool result = m_registry->MergeSettingsFile(subPath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
EXPECT_TRUE(result);
}
TEST_F(SettingsRegistryTest, MergeSettingsFile_PathAsSubStringThatsTooLong_ReturnsFalse)
{
constexpr AZStd::fixed_string<AZ::IO::MaxPathLength + 1> path(AZ::IO::MaxPathLength + 1, '1');
const AZStd::string_view subPath(path);
AZ_TEST_START_TRACE_SUPPRESSION;
bool result = m_registry->MergeSettingsFile(subPath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_FALSE(result);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Error"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Path"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFile_InvalidPath_ReturnsFalse)
{
AZ_TEST_START_TRACE_SUPPRESSION;
bool result = m_registry->MergeSettingsFile("InvalidPath", AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_FALSE(result);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Error"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Path"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFile_InvalidRootKey_ReturnsFalse)
{
AZStd::string path = CreateTestFile("test.setreg", R"({ "Test": 1 })");
AZ_TEST_START_TRACE_SUPPRESSION;
bool result = m_registry->MergeSettingsFile(path, AZ::SettingsRegistryInterface::Format::JsonMergePatch, "$", nullptr);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_FALSE(result);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Error"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Path"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFile_ParseError_ReturnsFalse)
{
AZStd::string path = CreateTestFile("test.setreg", "{ Test: 1 }");
AZ_TEST_START_TRACE_SUPPRESSION;
bool result = m_registry->MergeSettingsFile(path, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_FALSE(result);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Error"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Path"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFile_NonObjectRoot_EmptyRootKey_JsonMergePatch_ReturnsFalse)
{
AZStd::string path = CreateTestFile("test.setreg", R"("BooleanValue": false)");
AZ_TEST_START_TRACE_SUPPRESSION;
bool result = m_registry->MergeSettingsFile(path, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {});
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_FALSE(result);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Error"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Path"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFile_NonObjectRoot_NonEmptyRootKey_JsonMergePatch_ReturnsFalse)
{
// Because the root key isn't empty the setting registry will not be be completely overridden and therefore
// it is safe to merge the .setreg file with a boolean element at the root
AZStd::string path = CreateTestFile("test.setreg", R"("BooleanValue": false)");
AZ_TEST_START_TRACE_SUPPRESSION;
EXPECT_FALSE(m_registry->MergeSettingsFile(path, AZ::SettingsRegistryInterface::Format::JsonMergePatch, "/Test"));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
// There should be an error message about attempting to serialize a root json value that is not a Json Object
// to the settings registry
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Error"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Path"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFile_EmptyObjectRoot_EmptyRootKey_JsonMergePatch_PatchAppliedAndReported)
{
// In this scenario the object root is empty. The Merge should complete successfully
// but leave the settings registry unchanged.
AZStd::string path = CreateTestFile("test.setreg", R"({})");
// Initialize the Settings Registry with an Object at /TestObject/IntValue
m_registry->MergeSettings(R"({ "TestObject": { "IntValue": 7 } })", AZ::SettingsRegistryInterface::Format::JsonMergePatch);
// Merging a file with a empty JSON Object should be effectively a no-op.
// There are some changes in the settings registry to record the merge history for introspection purposes.
bool result = m_registry->MergeSettingsFile(path, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {});
EXPECT_TRUE(result);
AZStd::string history;
result = m_registry->Get(history, AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0");
EXPECT_TRUE(result);
EXPECT_STREQ(path.c_str(), history.c_str());
AZ::s64 intValue{};
EXPECT_TRUE(m_registry->Get(intValue, "/TestObject/IntValue"));
EXPECT_EQ(7, intValue);
}
//
// MergeSettingsFolder
//
TEST_F(SettingsRegistryTest, MergeSettingsFolder_MergeTestFiles_FilesAppliedInSpecializationOrder)
{
CreateTestFile("Memory.setreg", R"({ "Memory": 0, "MemoryRoot": true })");
CreateTestFile("Memory.editor.test.setreg", R"({ "Memory": 3, "MemoryEditorTest": true })");
CreateTestFile("Memory.editor.setreg", R"({ "Memory": 1, "MemoryEditor": true })");
CreateTestFile("Memory.test.setreg", R"({ "Memory": 2, "MemoryTest": true })");
size_t counter = 0;
auto callback = [this, &counter](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
const char* fileIds[] =
{
"/MemoryRoot",
"/MemoryEditor",
"/MemoryTest",
"/MemoryEditorTest"
};
MergeNotify(path, counter, AZ_ARRAY_SIZE(fileIds), "/Memory", fileIds);
counter++;
};
auto testNotifier1 = m_registry->RegisterNotifier(callback);
m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR);
*m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder;
bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, nullptr);
EXPECT_TRUE(result);
EXPECT_EQ(4, counter);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0")); // Folder and specialization settings.
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/3"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/4"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/5"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFolder_WithPlatformFiles_FilesAppliedInSpecializationOrder)
{
CreateTestFile("Memory.setreg", R"({ "Memory": 0, "MemoryRoot": true })");
CreateTestFile("Memory.editor.test.setreg", R"({ "Memory": 5, "MemoryEditorTest": true })");
CreateTestFile("Memory.editor.setreg", R"({ "Memory": 2, "MemoryEditor": true })");
CreateTestFile("Memory.test.setreg", R"({ "Memory": 4, "MemoryTest": true })");
CreateTestFile("Platform/Special/Memory.setreg", R"({ "Memory": 1, "MemorySpecial": true })");
CreateTestFile("Platform/Special/Memory.editor.setreg", R"({ "Memory": 3, "MemoryEditorSpecial": true })");
size_t counter = 0;
auto callback = [this, &counter](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
const char* fileIds[] =
{
"/MemoryRoot",
"/MemorySpecial",
"/MemoryEditor",
"/MemoryEditorSpecial",
"/MemoryTest",
"/MemoryEditorTest"
};
MergeNotify(path, counter, AZ_ARRAY_SIZE(fileIds), "/Memory", fileIds);
counter++;
};
auto testNotifier1 = m_registry->RegisterNotifier(callback);
m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR);
*m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder;
bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, "Special", nullptr);
EXPECT_TRUE(result);
EXPECT_EQ(6, counter);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0")); // Folder and specialization settings.
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/3"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/4"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/5"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/6"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/7"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFolder_DifferentlyNamedFiles_FilesAppliedInAlphabeticAndSpecializationOrder)
{
CreateTestFile("c.setreg", R"({ "Id": 2, "c": true })");
CreateTestFile("a.editor.test.setreg", R"({ "Id": 0, "a": true })");
CreateTestFile("b.editor.setreg", R"({ "Id": 1, "b": true })");
CreateTestFile("c.test.setreg", R"({ "Id": 3, "cTest": true })");
size_t counter = 0;
auto callback = [this, &counter](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
const char* fileIds[] =
{
"/a",
"/b",
"/c",
"/cTest"
};
MergeNotify(path, counter, AZ_ARRAY_SIZE(fileIds), "/Id", fileIds);
counter++;
};
auto testNotifier1 = m_registry->RegisterNotifier(callback);
m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR);
*m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder;
bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, nullptr);
EXPECT_TRUE(result);
EXPECT_EQ(4, counter);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0")); // Folder and specialization settings.
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/3"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/4"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/5"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFolder_JsonPatchFiles_FilesAppliedInAlphabeticAndSpecializationOrder)
{
CreateTestFile("Memory.setreg", R"({ "Memory": 0, "MemoryRoot": true })");
CreateTestFile("Memory.editor.test.setreg", R"({ "Memory": 3, "MemoryEditorTest": true })");
CreateTestFile("Memory.editor.setreg", R"({ "Memory": 1, "MemoryEditor": true })");
CreateTestFile("Memory.test.setregpatch", R"(
[
{ "op": "replace", "path": "/Memory", "value": 2 },
{ "op": "add", "path": "/MemoryTest", "value": true }
])");
size_t counter = 0;
auto callback = [this, &counter](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
const char* fileIds[] =
{
"/MemoryRoot",
"/MemoryEditor",
"/MemoryTest",
"/MemoryEditorTest"
};
MergeNotify(path, counter, AZ_ARRAY_SIZE(fileIds), "/Memory", fileIds);
counter++;
};
auto testNotifier1 = m_registry->RegisterNotifier(callback);
m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR);
*m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder;
bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, nullptr);
EXPECT_TRUE(result);
EXPECT_EQ(4, counter);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0")); // Folder and specialization settings.
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/3"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/4"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/5"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFolder_UnsupportedFilesAreIgnored_OneFileAppliedAndSuccessReturned)
{
CreateTestFile("Memory.setreg", R"({ "Memory": 0, "MemoryRoot": true })");
CreateTestFile("Memory.wrong", R"({ "Memory": 1, "Wrong": true })");
CreateTestFile("Platform/Special/Memory.wrong", R"({ "Memory": 2, "SpecialWrong": true })");
size_t counter = 0;
auto callback = [this, &counter](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
const char* fileIds[] =
{
"/MemoryRoot"
};
MergeNotify(path, counter, AZ_ARRAY_SIZE(fileIds), "/Memory", fileIds);
counter++;
};
auto testNotifier1 = m_registry->RegisterNotifier(callback);
m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR);
*m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder;
bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, "Special", nullptr);
EXPECT_TRUE(result);
EXPECT_EQ(1, counter);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0")); // Folder and specialization settings.
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFolder_WithScratchBuffer_FilesAppliedInSpecializationOrder)
{
CreateTestFile("Memory.setreg", R"({ "Memory": 0, "MemoryRoot": true })");
CreateTestFile("Memory.editor.test.setreg", R"({ "Memory": 3, "MemoryEditorTest": true })");
CreateTestFile("Memory.editor.setreg", R"({ "Memory": 1, "MemoryEditor": true })");
CreateTestFile("Memory.test.setreg", R"({ "Memory": 2, "MemoryTest": true })");
size_t counter = 0;
auto callback = [this, &counter](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
const char* fileIds[] =
{
"/MemoryRoot",
"/MemoryEditor",
"/MemoryTest",
"/MemoryEditorTest"
};
MergeNotify(path, counter, AZ_ARRAY_SIZE(fileIds), "/Memory", fileIds);
counter++;
};
auto testNotifier1 = m_registry->RegisterNotifier(callback);
AZStd::vector<char> buffer;
m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR);
*m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder;
bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, "", &buffer);
EXPECT_TRUE(result);
EXPECT_EQ(4, counter);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0")); // Folder and specialization settings.
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/3"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/4"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/5"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFolder_EmptyFolder_ReportsSuccessButNothingAdded)
{
bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, nullptr);
EXPECT_TRUE(result);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0")); // Folder and specialization settings.
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFolder_PathTooLong_ReportsErrorAndReturnsFalse)
{
constexpr AZStd::fixed_string<AZ::IO::MaxPathLength + 1> path(AZ::IO::MaxPathLength + 1, 'a');
AZ_TEST_START_TRACE_SUPPRESSION;
bool result = m_registry->MergeSettingsFolder(path, { "editor", "test" }, {}, nullptr);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_FALSE(result);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Error"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Path"));
}
TEST_F(SettingsRegistryTest, MergeSettingsFolder_ConflictingSpecializations_ReportsErrorAndReturnsFalse)
{
CreateTestFile("Memory.test.editor.setreg", "{}");
CreateTestFile("Memory.editor.test.setreg", "{}");
AZ_TEST_START_TRACE_SUPPRESSION;
m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR);
*m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder;
bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, nullptr);
EXPECT_GT(::UnitTest::TestRunner::Instance().StopAssertTests(), 0);
EXPECT_FALSE(result);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0")); // Folder and specialization settings.
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1/Error"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1/Path"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1/File1"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1/File2"));
}
} // namespace SettingsRegistryTests