Merge branch 'development' of https://github.com/o3de/o3de into sc-editor-asset-redux
This commit is contained in:
@@ -192,11 +192,86 @@ namespace AZ
|
||||
};
|
||||
|
||||
|
||||
//! SettingsRegistry notifier handler which is responsible for loading
|
||||
//! the project.json file at the new project path
|
||||
//! if an update to '<BootstrapSettingsRootKey>/project_path' key occurs.
|
||||
struct ProjectPathChangedEventHandler
|
||||
{
|
||||
ProjectPathChangedEventHandler(AZ::SettingsRegistryInterface& registry)
|
||||
: m_registry{ registry }
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
// Update the project settings when the project path is set
|
||||
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
|
||||
const auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
|
||||
|
||||
AZ::IO::FixedMaxPath newProjectPath;
|
||||
if (SettingsRegistryMergeUtils::IsPathAncestorDescendantOrEqual(projectPathKey, path)
|
||||
&& m_registry.Get(newProjectPath.Native(), projectPathKey) && newProjectPath != m_oldProjectPath)
|
||||
{
|
||||
// Update old Project path before attempting to merge in new Settings Registry values in order to prevent recursive calls
|
||||
m_oldProjectPath = newProjectPath;
|
||||
|
||||
// Merge the project.json file into settings registry under ProjectSettingsRootKey path.
|
||||
AZ::IO::FixedMaxPath projectMetadataFile{ AZ::SettingsRegistryMergeUtils::FindEngineRoot(m_registry) / newProjectPath };
|
||||
projectMetadataFile /= "project.json";
|
||||
m_registry.MergeSettingsFile(projectMetadataFile.Native(),
|
||||
AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey);
|
||||
|
||||
// Update all the runtime file paths based on the new "project_path" value.
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
AZ::IO::FixedMaxPath m_oldProjectPath;
|
||||
AZ::SettingsRegistryInterface& m_registry;
|
||||
};
|
||||
|
||||
//! SettingsRegistry notifier handler which adds the project name as a specialization tag
|
||||
//! to the registry
|
||||
//! if an update to '<ProjectSettingsRootKey>/project_name' key occurs.
|
||||
struct ProjectNameChangedEventHandler
|
||||
{
|
||||
ProjectNameChangedEventHandler(AZ::SettingsRegistryInterface& registry)
|
||||
: m_registry{ registry }
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
// Update the project specialization when the project name is set
|
||||
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
|
||||
const auto projectNameKey = FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey) + "/project_name";
|
||||
|
||||
FixedValueString newProjectName;
|
||||
if (SettingsRegistryMergeUtils::IsPathAncestorDescendantOrEqual(projectNameKey, path)
|
||||
&& m_registry.Get(newProjectName, projectNameKey) && newProjectName != m_oldProjectName)
|
||||
{
|
||||
// Add the project_name as a specialization for loading the build system dependency .setreg files
|
||||
auto newProjectNameSpecialization = FixedValueString::format("%s/%.*s", AZ::SettingsRegistryMergeUtils::SpecializationsRootKey,
|
||||
aznumeric_cast<int>(newProjectName.size()), newProjectName.data());
|
||||
auto oldProjectNameSpecialization = FixedValueString::format("%s/%s", AZ::SettingsRegistryMergeUtils::SpecializationsRootKey,
|
||||
m_oldProjectName.c_str());
|
||||
m_registry.Remove(oldProjectNameSpecialization);
|
||||
m_oldProjectName = newProjectName;
|
||||
m_registry.Set(newProjectNameSpecialization, true);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
AZ::SettingsRegistryInterface::FixedValueString m_oldProjectName;
|
||||
AZ::SettingsRegistryInterface& m_registry;
|
||||
};
|
||||
|
||||
//! SettingsRegistry notifier handler which updates relevant registry settings based
|
||||
//! on an update to '/Amazon/AzCore/Bootstrap/project_path' key.
|
||||
struct UpdateProjectSettingsEventHandler
|
||||
struct UpdateCommandLineEventHandler
|
||||
{
|
||||
UpdateProjectSettingsEventHandler(AZ::SettingsRegistryInterface& registry, AZ::CommandLine& commandLine)
|
||||
UpdateCommandLineEventHandler(AZ::SettingsRegistryInterface& registry, AZ::CommandLine& commandLine)
|
||||
: m_registry{ registry }
|
||||
, m_commandLine{ commandLine }
|
||||
{
|
||||
@@ -204,70 +279,14 @@ namespace AZ
|
||||
|
||||
void operator()(AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
|
||||
// #1 Update the project settings when the project path is set
|
||||
const auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
|
||||
AZ::IO::FixedMaxPath newProjectPath;
|
||||
if (SettingsRegistryMergeUtils::IsPathAncestorDescendantOrEqual(projectPathKey, path)
|
||||
&& m_registry.Get(newProjectPath.Native(), projectPathKey) && newProjectPath != m_oldProjectPath)
|
||||
{
|
||||
UpdateProjectSettingsFromProjectPath(AZ::IO::PathView(newProjectPath));
|
||||
}
|
||||
|
||||
// #2 Update the project specialization when the project name is set
|
||||
const auto projectNameKey = FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey) + "/project_name";
|
||||
FixedValueString newProjectName;
|
||||
if (SettingsRegistryMergeUtils::IsPathAncestorDescendantOrEqual(projectNameKey, path)
|
||||
&& m_registry.Get(newProjectName, projectNameKey) && newProjectName != m_oldProjectName)
|
||||
{
|
||||
UpdateProjectSpecializationFromProjectName(newProjectName);
|
||||
}
|
||||
|
||||
// #3 Update the ComponentApplication CommandLine instance when the command line settings are merged into the Settings Registry
|
||||
// Update the ComponentApplication CommandLine instance when the command line settings are merged into the Settings Registry
|
||||
if (path == AZ::SettingsRegistryMergeUtils::CommandLineValueChangedKey)
|
||||
{
|
||||
UpdateCommandLine();
|
||||
AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(m_registry, m_commandLine);
|
||||
}
|
||||
}
|
||||
|
||||
//! Add the project name as a specialization underneath the /Amazon/AzCore/Settings/Specializations path
|
||||
//! and remove the current project name specialization if one exists.
|
||||
void UpdateProjectSpecializationFromProjectName(AZStd::string_view newProjectName)
|
||||
{
|
||||
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
|
||||
// Add the project_name as a specialization for loading the build system dependency .setreg files
|
||||
auto newProjectNameSpecialization = FixedValueString::format("%s/%.*s", AZ::SettingsRegistryMergeUtils::SpecializationsRootKey,
|
||||
aznumeric_cast<int>(newProjectName.size()), newProjectName.data());
|
||||
auto oldProjectNameSpecialization = FixedValueString::format("%s/%s", AZ::SettingsRegistryMergeUtils::SpecializationsRootKey,
|
||||
m_oldProjectName.c_str());
|
||||
m_registry.Remove(oldProjectNameSpecialization);
|
||||
m_oldProjectName = newProjectName;
|
||||
m_registry.Set(newProjectNameSpecialization, true);
|
||||
}
|
||||
|
||||
void UpdateProjectSettingsFromProjectPath(AZ::IO::PathView newProjectPath)
|
||||
{
|
||||
// Update old Project path before attempting to merge in new Settings Registry values in order to prevent recursive calls
|
||||
m_oldProjectPath = newProjectPath;
|
||||
|
||||
// Merge the project.json file into settings registry under ProjectSettingsRootKey path.
|
||||
AZ::IO::FixedMaxPath projectMetadataFile{ AZ::SettingsRegistryMergeUtils::FindEngineRoot(m_registry) / newProjectPath };
|
||||
projectMetadataFile /= "project.json";
|
||||
m_registry.MergeSettingsFile(projectMetadataFile.Native(),
|
||||
AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey);
|
||||
|
||||
// Update all the runtime file paths based on the new "project_path" value.
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry);
|
||||
}
|
||||
|
||||
void UpdateCommandLine()
|
||||
{
|
||||
AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(m_registry, m_commandLine);
|
||||
}
|
||||
|
||||
private:
|
||||
AZ::IO::FixedMaxPath m_oldProjectPath;
|
||||
AZ::SettingsRegistryInterface::FixedValueString m_oldProjectName;
|
||||
AZ::SettingsRegistryInterface& m_registry;
|
||||
AZ::CommandLine& m_commandLine;
|
||||
};
|
||||
@@ -462,7 +481,12 @@ namespace AZ
|
||||
// 1. The 'project_path' key changes
|
||||
// 2. The project specialization when the 'project-name' key changes
|
||||
// 3. The ComponentApplication command line when the command line is stored to the registry
|
||||
m_projectChangedHandler = m_settingsRegistry->RegisterNotifier(UpdateProjectSettingsEventHandler{ *m_settingsRegistry, m_commandLine });
|
||||
m_projectPathChangedHandler = m_settingsRegistry->RegisterNotifier(ProjectPathChangedEventHandler{
|
||||
*m_settingsRegistry });
|
||||
m_projectNameChangedHandler = m_settingsRegistry->RegisterNotifier(ProjectNameChangedEventHandler{
|
||||
*m_settingsRegistry });
|
||||
m_commandLineUpdatedHandler = m_settingsRegistry->RegisterNotifier(UpdateCommandLineEventHandler{
|
||||
*m_settingsRegistry, m_commandLine });
|
||||
|
||||
// Merge Command Line arguments
|
||||
constexpr bool executeRegDumpCommands = false;
|
||||
@@ -515,11 +539,12 @@ namespace AZ
|
||||
Destroy();
|
||||
}
|
||||
|
||||
// The m_projectChangedHandler stores an AZStd::function internally
|
||||
// which allocates using the AZ SystemAllocator
|
||||
// m_projectChangedHandler is being default value initialized
|
||||
// to clear out the AZStd::function
|
||||
m_projectChangedHandler = {};
|
||||
// The SettingsRegistry Notify handlers stores an AZStd::function internally
|
||||
// which may allocates using the AZ SystemAllocator(if the functor > 16 bytes)
|
||||
// The handlers are being default value initialized to clear out the AZStd::function
|
||||
m_commandLineUpdatedHandler = {};
|
||||
m_projectNameChangedHandler = {};
|
||||
m_projectPathChangedHandler = {};
|
||||
|
||||
// Delete the AZ::IConsole if it was created by this application instance
|
||||
if (m_ownsConsole)
|
||||
|
||||
@@ -390,7 +390,9 @@ namespace AZ
|
||||
AZ::IO::FixedMaxPath m_engineRoot;
|
||||
AZ::IO::FixedMaxPath m_appRoot;
|
||||
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectChangedHandler;
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectPathChangedHandler;
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectNameChangedHandler;
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_commandLineUpdatedHandler;
|
||||
|
||||
// ConsoleFunctorHandle is responsible for unregistering the Settings Registry Console
|
||||
// from the m_console member when it goes out of scope
|
||||
|
||||
@@ -639,9 +639,10 @@ namespace AZ
|
||||
{
|
||||
// Make sure the there is a JSON object at the ConsoleRuntimeCommandKey or ConsoleAutoexecKey
|
||||
// So that JSON Patch is able to add values underneath that object (JSON Patch doesn't create intermediate objects)
|
||||
settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } } })"
|
||||
R"(,"O3DE": { "Autoexec": { "ConsoleCommands": {} } } })",
|
||||
SettingsRegistryInterface::Format::JsonMergePatch);
|
||||
settingsRegistry.MergeSettings(R"({})", SettingsRegistryInterface::Format::JsonMergePatch,
|
||||
IConsole::ConsoleRuntimeCommandKey);
|
||||
settingsRegistry.MergeSettings(R"({})", SettingsRegistryInterface::Format::JsonMergePatch,
|
||||
IConsole::ConsoleAutoexecCommandKey);
|
||||
m_consoleCommandKeyHandler = settingsRegistry.RegisterNotifier(ConsoleCommandKeyNotificationHandler{ settingsRegistry, *this });
|
||||
|
||||
JsonApplyPatchSettings applyPatchSettings;
|
||||
|
||||
@@ -40,6 +40,12 @@ namespace AZ
|
||||
static unsigned int Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned int suppressCount = 0, void* nativeThread = 0);
|
||||
};
|
||||
|
||||
class StackConverter
|
||||
{
|
||||
public:
|
||||
static unsigned int FromNative(StackFrame* frames, unsigned int maxNumOfFrames, void* nativeContext);
|
||||
};
|
||||
|
||||
class SymbolStorage
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace AZ
|
||||
{
|
||||
namespace Debug
|
||||
{
|
||||
struct StackFrame;
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
@@ -551,17 +553,19 @@ namespace AZ
|
||||
{
|
||||
StackFrame frames[25];
|
||||
|
||||
// Without StackFrame explicit alignment frames array is aligned to 4 bytes
|
||||
// which causes the stack tracing to fail.
|
||||
//size_t bla = AZStd::alignment_of<StackFrame>::value;
|
||||
//printf("Alignment value %d address 0x%08x : 0x%08x\n",bla,frames);
|
||||
SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)];
|
||||
unsigned int numFrames = 0;
|
||||
|
||||
if (!nativeContext)
|
||||
{
|
||||
suppressCount += 1; /// If we don't provide a context we will capture in the RecordFunction, so skip us (Trace::PrinCallstack).
|
||||
suppressCount += 1; /// If we don't provide a context we will capture in the RecordFunction, so skip us (Trace::PrintCallstack).
|
||||
numFrames = StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), suppressCount);
|
||||
}
|
||||
unsigned int numFrames = StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), suppressCount, nativeContext);
|
||||
else
|
||||
{
|
||||
numFrames = StackConverter::FromNative(frames, AZ_ARRAY_SIZE(frames), nativeContext);
|
||||
}
|
||||
|
||||
if (numFrames)
|
||||
{
|
||||
SymbolStorage::DecodeFrames(frames, numFrames, lines);
|
||||
|
||||
@@ -192,26 +192,34 @@ namespace AZ
|
||||
//! @param path An offset at which traversal should start.
|
||||
//! @return Whether or not entries could be visited.
|
||||
virtual bool Visit(const VisitorCallback& callback, AZStd::string_view path) const = 0;
|
||||
|
||||
//! Register a callback that will be called whenever an entry gets a new/updated value.
|
||||
//!
|
||||
//! @callback The function to call when an entry gets a new/updated value.
|
||||
[[nodiscard]] virtual NotifyEventHandler RegisterNotifier(const NotifyCallback& callback) = 0;
|
||||
//! Register a callback that will be called whenever an entry gets a new/updated value.
|
||||
//! @callback The function to call when an entry gets a new/updated value.
|
||||
[[nodiscard]] virtual NotifyEventHandler RegisterNotifier(NotifyCallback&& callback) = 0;
|
||||
//! @return NotifyEventHandler instance which must persist to receive event signal
|
||||
[[nodiscard]] virtual NotifyEventHandler RegisterNotifier(NotifyCallback callback) = 0;
|
||||
//! Register a notify event handler with the NotifyEvent.
|
||||
//! The handler will be called whenever an entry gets a new/updated value.
|
||||
//! @param handler The handler to register with the NotifyEvent.
|
||||
virtual void RegisterNotifier(NotifyEventHandler& handler) = 0;
|
||||
|
||||
//! Register a function that will be called before a file is merged.
|
||||
//! @callback The function to call before a file is merged.
|
||||
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(const PreMergeEventCallback& callback) = 0;
|
||||
//! Register a function that will be called before a file is merged.
|
||||
//! @callback The function to call before a file is merged.
|
||||
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(PreMergeEventCallback&& callback) = 0;
|
||||
//! @param callback The function to call before a file is merged.
|
||||
//! @return PreMergeEventHandler instance which must persist to receive event signal
|
||||
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(PreMergeEventCallback callback) = 0;
|
||||
//! Register a pre-merge handler with the PreMergeEvent.
|
||||
//! The handler will be called before a file is merged.
|
||||
//! @param handler The hanlder to register with the PreMergeEvent.
|
||||
virtual void RegisterPreMergeEvent(PreMergeEventHandler& handler) = 0;
|
||||
|
||||
//! Register a function that will be called after a file is merged.
|
||||
//! @callback The function to call after a file is merged.
|
||||
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(const PostMergeEventCallback& callback) = 0;
|
||||
//! Register a function that will be called after a file is merged.
|
||||
//! @callback The function to call after a file is merged.
|
||||
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(PostMergeEventCallback&& callback) = 0;
|
||||
//! @param callback The function to call after a file is merged.
|
||||
//! @return PostMergeEventHandler instance which must persist to receive event signal
|
||||
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(PostMergeEventCallback callback) = 0;
|
||||
//! Register a post-merge hahndler with the PostMergeEvent.
|
||||
//! The handler will be called after a file is merged.
|
||||
//! @param handler The handler to register with the PostmergeEVent.
|
||||
virtual void RegisterPostMergeEvent(PostMergeEventHandler& hanlder) = 0;
|
||||
|
||||
//! Gets the boolean value at the provided path.
|
||||
//! @param result The target to write the result to.
|
||||
@@ -326,23 +334,25 @@ namespace AZ
|
||||
//! - all digits and dot -> floating point number
|
||||
//! - Everything else is considered a string.
|
||||
//! @param argument The command line argument.
|
||||
//! @param structure which contains functors which determine what characters are delimiters
|
||||
//! @param anchorKey The key where the merged command line argument will be anchored under
|
||||
//! @param commandLineSettings structure which contains functors which determine what characters are delimiters
|
||||
//! @return True if the command line argument could be parsed, otherwise false.
|
||||
virtual bool MergeCommandLineArgument(AZStd::string_view argument, AZStd::string_view rootKey = "",
|
||||
virtual bool MergeCommandLineArgument(AZStd::string_view argument, AZStd::string_view anchorKey = "",
|
||||
const CommandLineArgumentSettings& commandLineSettings = {}) = 0;
|
||||
//! Merges the json data provided into the settings registry.
|
||||
//! @param data The json data stored in a string.
|
||||
//! @param format The format of the provided data.
|
||||
//! @param anchorKey The key where the merged json content will be anchored under.
|
||||
//! @return True if the data was successfully merged, otherwise false.
|
||||
virtual bool MergeSettings(AZStd::string_view data, Format format) = 0;
|
||||
virtual bool MergeSettings(AZStd::string_view data, Format format, AZStd::string_view anchorKey = "") = 0;
|
||||
//! Loads a settings file and merges it into the registry.
|
||||
//! @param path The path to the registry file.
|
||||
//! @param format The format of the text data in the file at the provided path.
|
||||
//! @param rootKey The key where the root of the settings file will be stored under.
|
||||
//! @param anchorKey The key where the content of the settings file will be anchored.
|
||||
//! @param scratchBuffer An optional buffer that's used to load the file into. Use this when loading multiple patches to
|
||||
//! reduce the number of intermediate memory allocations.
|
||||
//! @return True if the registry file was successfully merged, otherwise false.
|
||||
virtual bool MergeSettingsFile(AZStd::string_view path, Format format, AZStd::string_view rootKey = "",
|
||||
virtual bool MergeSettingsFile(AZStd::string_view path, Format format, AZStd::string_view anchorKey = "",
|
||||
AZStd::vector<char>* scratchBuffer = nullptr) = 0;
|
||||
//! Loads all settings files in a folder and merges them into the registry.
|
||||
//! With the specializations "a" and "b" and platform "c" the files would be loaded in the order:
|
||||
@@ -357,11 +367,12 @@ namespace AZ
|
||||
//! @param platform An optional name of a platform. Platform overloads are located at <path>/Platform/<platform>/
|
||||
//! Files in a platform are applied in the same order as for the main folder but always after the same file
|
||||
//! in the main folder.
|
||||
//! @param anchorKey The registry path location where the settings will be anchored
|
||||
//! @param scratchBuffer An optional buffer that's used to load the file into. Use this when loading multiple patches to
|
||||
//! reduce the number of intermediate memory allocations.
|
||||
//! @return True if the registry folder was successfully merged, otherwise false.
|
||||
virtual bool MergeSettingsFolder(AZStd::string_view path, const Specializations& specializations,
|
||||
AZStd::string_view platform = {}, AZStd::string_view rootKey = "", AZStd::vector<char>* scratchBuffer = nullptr) = 0;
|
||||
AZStd::string_view platform = {}, AZStd::string_view anchorKey = "", AZStd::vector<char>* scratchBuffer = nullptr) = 0;
|
||||
|
||||
//! Stores the settings structure which is used when merging settings to the Settings Registry
|
||||
//! using JSON Merge Patch or JSON Merge Patch.
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <AzCore/IO/FileReader.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/JSON/error/en.h>
|
||||
#include <AzCore/NativeUI//NativeUIRequests.h>
|
||||
#include <AzCore/NativeUI/NativeUIRequests.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Serialization/Json/StackedString.h>
|
||||
#include <AzCore/Settings/SettingsRegistryImpl.h>
|
||||
@@ -21,6 +21,34 @@
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzCore/std/parallel/scoped_lock.h>
|
||||
|
||||
namespace AZ::SettingsRegistryImplInternal
|
||||
{
|
||||
AZ::SettingsRegistryInterface::Type RapidjsonToSettingsRegistryType(const rapidjson::Value& value)
|
||||
{
|
||||
using Type = AZ::SettingsRegistryInterface::Type;
|
||||
switch (value.GetType())
|
||||
{
|
||||
case rapidjson::Type::kNullType:
|
||||
return Type::Null;
|
||||
case rapidjson::Type::kFalseType:
|
||||
return Type::Boolean;
|
||||
case rapidjson::Type::kTrueType:
|
||||
return Type::Boolean;
|
||||
case rapidjson::Type::kObjectType:
|
||||
return Type::Object;
|
||||
case rapidjson::Type::kArrayType:
|
||||
return Type::Array;
|
||||
case rapidjson::Type::kStringType:
|
||||
return Type::String;
|
||||
case rapidjson::Type::kNumberType:
|
||||
return value.IsDouble() ? Type::FloatingPoint :
|
||||
Type::Integer;
|
||||
}
|
||||
|
||||
return Type::NoType;
|
||||
}
|
||||
}
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
template<typename T>
|
||||
@@ -28,7 +56,7 @@ namespace AZ
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// rapidjson::Pointer assets that the supplied string
|
||||
// rapidjson::Pointer asserts that the supplied string
|
||||
// is not nullptr even if the supplied size is 0
|
||||
// Setting to empty string to prevent assert
|
||||
path = "";
|
||||
@@ -70,7 +98,7 @@ namespace AZ
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// rapidjson::Pointer assets that the supplied string
|
||||
// rapidjson::Pointer asserts that the supplied string
|
||||
// is not nullptr even if the supplied size is 0
|
||||
// Setting to empty string to prevent assert
|
||||
path = "";
|
||||
@@ -161,7 +189,7 @@ namespace AZ
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// rapidjson::Pointer assets that the supplied string
|
||||
// rapidjson::Pointer asserts that the supplied string
|
||||
// is not nullptr even if the supplied size is 0
|
||||
// Setting to empty string to prevent assert
|
||||
path = "";
|
||||
@@ -212,17 +240,7 @@ namespace AZ
|
||||
return Visit(visitor, path);
|
||||
}
|
||||
|
||||
auto SettingsRegistryImpl::RegisterNotifier(const NotifyCallback& callback) -> NotifyEventHandler
|
||||
{
|
||||
NotifyEventHandler notifyHandler{ callback };
|
||||
{
|
||||
AZStd::scoped_lock lock(m_notifierMutex);
|
||||
notifyHandler.Connect(m_notifiers);
|
||||
}
|
||||
return notifyHandler;
|
||||
}
|
||||
|
||||
auto SettingsRegistryImpl::RegisterNotifier(NotifyCallback&& callback) -> NotifyEventHandler
|
||||
auto SettingsRegistryImpl::RegisterNotifier(NotifyCallback callback) -> NotifyEventHandler
|
||||
{
|
||||
NotifyEventHandler notifyHandler{ AZStd::move(callback) };
|
||||
{
|
||||
@@ -232,23 +250,19 @@ namespace AZ
|
||||
return notifyHandler;
|
||||
}
|
||||
|
||||
auto SettingsRegistryImpl::RegisterNotifier(NotifyEventHandler& notifyHandler) -> void
|
||||
{
|
||||
AZStd::scoped_lock lock(m_notifierMutex);
|
||||
notifyHandler.Connect(m_notifiers);
|
||||
}
|
||||
|
||||
void SettingsRegistryImpl::ClearNotifiers()
|
||||
{
|
||||
AZStd::scoped_lock lock(m_notifierMutex);
|
||||
m_notifiers.DisconnectAllHandlers();
|
||||
}
|
||||
|
||||
auto SettingsRegistryImpl::RegisterPreMergeEvent(const PreMergeEventCallback& callback) -> PreMergeEventHandler
|
||||
{
|
||||
PreMergeEventHandler preMergeHandler{ callback };
|
||||
{
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
preMergeHandler.Connect(m_preMergeEvent);
|
||||
}
|
||||
return preMergeHandler;
|
||||
}
|
||||
|
||||
auto SettingsRegistryImpl::RegisterPreMergeEvent(PreMergeEventCallback&& callback) -> PreMergeEventHandler
|
||||
auto SettingsRegistryImpl::RegisterPreMergeEvent(PreMergeEventCallback callback) -> PreMergeEventHandler
|
||||
{
|
||||
PreMergeEventHandler preMergeHandler{ AZStd::move(callback) };
|
||||
{
|
||||
@@ -258,17 +272,13 @@ namespace AZ
|
||||
return preMergeHandler;
|
||||
}
|
||||
|
||||
auto SettingsRegistryImpl::RegisterPostMergeEvent(const PostMergeEventCallback& callback) -> PostMergeEventHandler
|
||||
auto SettingsRegistryImpl::RegisterPreMergeEvent(PreMergeEventHandler& preMergeHandler) -> void
|
||||
{
|
||||
PostMergeEventHandler postMergeHandler{ callback };
|
||||
{
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
postMergeHandler.Connect(m_postMergeEvent);
|
||||
}
|
||||
return postMergeHandler;
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
preMergeHandler.Connect(m_preMergeEvent);
|
||||
}
|
||||
|
||||
auto SettingsRegistryImpl::RegisterPostMergeEvent(PostMergeEventCallback&& callback) -> PostMergeEventHandler
|
||||
auto SettingsRegistryImpl::RegisterPostMergeEvent(PostMergeEventCallback callback) -> PostMergeEventHandler
|
||||
{
|
||||
PostMergeEventHandler postMergeHandler{ AZStd::move(callback) };
|
||||
{
|
||||
@@ -278,6 +288,12 @@ namespace AZ
|
||||
return postMergeHandler;
|
||||
}
|
||||
|
||||
auto SettingsRegistryImpl::RegisterPostMergeEvent(PostMergeEventHandler& postMergeHandler) -> void
|
||||
{
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
postMergeHandler.Connect(m_postMergeEvent);
|
||||
}
|
||||
|
||||
void SettingsRegistryImpl::ClearMergeEvents()
|
||||
{
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
@@ -297,7 +313,36 @@ namespace AZ
|
||||
localNotifierEvent = AZStd::move(m_notifiers);
|
||||
}
|
||||
|
||||
localNotifierEvent.Signal(jsonPath, type);
|
||||
// Signal the NotifyEvent for each queued argument
|
||||
decltype(m_signalNotifierQueue) localNotifierQueue;
|
||||
{
|
||||
AZStd::scoped_lock signalLock(m_signalMutex);
|
||||
m_signalNotifierQueue.push_back({ FixedValueString{jsonPath}, type });
|
||||
// If the signal count was 0, then a dispatch is in progress
|
||||
if (m_signalCount++ == 0)
|
||||
{
|
||||
AZStd::swap(localNotifierQueue, m_signalNotifierQueue);
|
||||
}
|
||||
}
|
||||
|
||||
while (!localNotifierQueue.empty())
|
||||
{
|
||||
for (SignalNotifierArgs notifierArgs : localNotifierQueue)
|
||||
{
|
||||
localNotifierEvent.Signal(notifierArgs.m_jsonPath, notifierArgs.m_type);
|
||||
}
|
||||
// Clear the local notifier queue and check if more notifiers have been added
|
||||
localNotifierQueue = {};
|
||||
{
|
||||
AZStd::scoped_lock signalLock(m_signalMutex);
|
||||
AZStd::swap(localNotifierQueue, m_signalNotifierQueue);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::scoped_lock signalLock(m_signalMutex);
|
||||
--m_signalCount;
|
||||
}
|
||||
|
||||
{
|
||||
// Swap the local handlers with the current m_notifiers which
|
||||
@@ -314,39 +359,19 @@ namespace AZ
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
//rapidjson::Pointer assets that the supplied string
|
||||
//rapidjson::Pointer asserts that the supplied string
|
||||
// is not nullptr even if the supplied size is 0
|
||||
// Setting to empty string to prevent assert
|
||||
path = "";
|
||||
}
|
||||
|
||||
|
||||
rapidjson::Pointer pointer(path.data(), path.length());
|
||||
if (pointer.IsValid())
|
||||
{
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
const rapidjson::Value* value = pointer.Get(m_settings);
|
||||
if (value)
|
||||
if (const rapidjson::Value* value = pointer.Get(m_settings); value != nullptr)
|
||||
{
|
||||
switch (value->GetType())
|
||||
{
|
||||
case rapidjson::Type::kNullType:
|
||||
return Type::Null;
|
||||
case rapidjson::Type::kFalseType:
|
||||
return Type::Boolean;
|
||||
case rapidjson::Type::kTrueType:
|
||||
return Type::Boolean;
|
||||
case rapidjson::Type::kObjectType:
|
||||
return Type::Object;
|
||||
case rapidjson::Type::kArrayType:
|
||||
return Type::Array;
|
||||
case rapidjson::Type::kStringType:
|
||||
return Type::String;
|
||||
case rapidjson::Type::kNumberType:
|
||||
return
|
||||
value->IsDouble() ? Type::FloatingPoint :
|
||||
Type::Integer;
|
||||
}
|
||||
return SettingsRegistryImplInternal::RapidjsonToSettingsRegistryType(*value);
|
||||
}
|
||||
}
|
||||
return Type::NoType;
|
||||
@@ -392,7 +417,7 @@ namespace AZ
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// rapidjson::Pointer assets that the supplied string
|
||||
// rapidjson::Pointer asserts that the supplied string
|
||||
// is not nullptr even if the supplied size is 0
|
||||
// Setting to empty string to prevent assert
|
||||
path = "";
|
||||
@@ -471,13 +496,12 @@ namespace AZ
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
//rapidjson::Pointer assets that the supplied string
|
||||
// rapidjson::Pointer asserts that the supplied string
|
||||
// is not nullptr even if the supplied size is 0
|
||||
// Setting to empty string to prevent assert
|
||||
path = "";
|
||||
}
|
||||
|
||||
|
||||
rapidjson::Pointer pointer(path.data(), path.length());
|
||||
if (pointer.IsValid())
|
||||
{
|
||||
@@ -486,10 +510,14 @@ namespace AZ
|
||||
value, nullptr, valueTypeID, m_serializationSettings);
|
||||
if (jsonResult.GetProcessing() != JsonSerializationResult::Processing::Halted)
|
||||
{
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
rapidjson::Value& setting = pointer.Create(m_settings, m_settings.GetAllocator());
|
||||
setting = AZStd::move(store);
|
||||
SignalNotifier(path, Type::Object);
|
||||
auto anchorType = Type::NoType;
|
||||
{
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
rapidjson::Value& setting = pointer.Create(m_settings, m_settings.GetAllocator());
|
||||
setting = AZStd::move(store);
|
||||
anchorType = SettingsRegistryImplInternal::RapidjsonToSettingsRegistryType(setting);
|
||||
}
|
||||
SignalNotifier(path, anchorType);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -500,7 +528,7 @@ namespace AZ
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// rapidjson::Pointer assets that the supplied string
|
||||
// rapidjson::Pointer asserts that the supplied string
|
||||
// is not nullptr even if the supplied size is 0
|
||||
// Setting to empty string to prevent assert
|
||||
path = "";
|
||||
@@ -605,7 +633,7 @@ namespace AZ
|
||||
return Set(key, value);
|
||||
}
|
||||
|
||||
bool SettingsRegistryImpl::MergeSettings(AZStd::string_view data, Format format)
|
||||
bool SettingsRegistryImpl::MergeSettings(AZStd::string_view data, Format format, AZStd::string_view anchorKey)
|
||||
{
|
||||
rapidjson::Document jsonPatch;
|
||||
constexpr int flags = rapidjson::kParseStopWhenDoneFlag | rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag;
|
||||
@@ -631,17 +659,43 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
|
||||
JsonSerializationResult::ResultCode mergeResult =
|
||||
JsonSerialization::ApplyPatch(m_settings, m_settings.GetAllocator(), jsonPatch, mergeApproach);
|
||||
if (mergeResult.GetProcessing() != JsonSerializationResult::Processing::Completed)
|
||||
rapidjson::Pointer anchorPath;
|
||||
if (!anchorKey.empty())
|
||||
{
|
||||
AZ_Error("Settings Registry", false, "Failed to fully merge data into registry.");
|
||||
return false;
|
||||
anchorPath = rapidjson::Pointer(anchorKey.data(), anchorKey.size());
|
||||
if (!anchorPath.IsValid())
|
||||
{
|
||||
rapidjson::Pointer pointer(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/-");
|
||||
AZ_Error("Settings Registry", false, R"(Anchor path "%.*s" is invalid.)", AZ_STRING_ARG(anchorKey));
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
pointer.Create(m_settings, m_settings.GetAllocator()).SetObject()
|
||||
.AddMember(rapidjson::StringRef("Error"), rapidjson::StringRef("Invalid anchor key."), m_settings.GetAllocator())
|
||||
.AddMember(rapidjson::StringRef("Path"),
|
||||
rapidjson::Value(anchorKey.data(), aznumeric_caster(anchorKey.size()), m_settings.GetAllocator()),
|
||||
m_settings.GetAllocator());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SignalNotifier("", Type::Object);
|
||||
auto anchorType = AZ::SettingsRegistryInterface::Type::NoType;
|
||||
{
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
rapidjson::Value& anchorRoot = anchorPath.IsValid() ? anchorPath.Create(m_settings, m_settings.GetAllocator())
|
||||
: m_settings;
|
||||
|
||||
JsonSerializationResult::ResultCode mergeResult =
|
||||
JsonSerialization::ApplyPatch(anchorRoot, m_settings.GetAllocator(), jsonPatch, mergeApproach);
|
||||
if (mergeResult.GetProcessing() != JsonSerializationResult::Processing::Completed)
|
||||
{
|
||||
AZ_Error("Settings Registry", false, "Failed to fully merge data into registry.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The settings have been successfully merged, query the type at the anchor key
|
||||
anchorType = SettingsRegistryImplInternal::RapidjsonToSettingsRegistryType(anchorRoot);
|
||||
}
|
||||
|
||||
SignalNotifier(anchorKey, anchorType);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1225,10 +1279,12 @@ namespace AZ
|
||||
ScopedMergeEvent scopedMergeEvent(m_preMergeEvent, m_postMergeEvent, path, rootKey);
|
||||
|
||||
JsonSerializationResult::ResultCode mergeResult(JsonSerializationResult::Tasks::Merge);
|
||||
auto anchorType = Type::NoType;
|
||||
if (rootKey.empty())
|
||||
{
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
mergeResult = JsonSerialization::ApplyPatch(m_settings, m_settings.GetAllocator(), jsonPatch, mergeApproach, m_applyPatchSettings);
|
||||
anchorType = SettingsRegistryImplInternal::RapidjsonToSettingsRegistryType(m_settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1238,6 +1294,7 @@ namespace AZ
|
||||
AZStd::scoped_lock lock(m_settingMutex);
|
||||
Value& rootValue = root.Create(m_settings, m_settings.GetAllocator());
|
||||
mergeResult = JsonSerialization::ApplyPatch(rootValue, m_settings.GetAllocator(), jsonPatch, mergeApproach, m_applyPatchSettings);
|
||||
anchorType = SettingsRegistryImplInternal::RapidjsonToSettingsRegistryType(rootValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1265,7 +1322,7 @@ namespace AZ
|
||||
pointer.Create(m_settings, m_settings.GetAllocator()).SetString(path, m_settings.GetAllocator());
|
||||
}
|
||||
|
||||
SignalNotifier("", Type::Object);
|
||||
SignalNotifier(rootKey, anchorType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -48,14 +48,14 @@ namespace AZ
|
||||
Type GetType(AZStd::string_view path) const override;
|
||||
bool Visit(Visitor& visitor, AZStd::string_view path) const override;
|
||||
bool Visit(const VisitorCallback& callback, AZStd::string_view path) const override;
|
||||
[[nodiscard]] NotifyEventHandler RegisterNotifier(const NotifyCallback& callback) override;
|
||||
[[nodiscard]] NotifyEventHandler RegisterNotifier(NotifyCallback&& callback) override;
|
||||
[[nodiscard]] NotifyEventHandler RegisterNotifier(NotifyCallback callback) override;
|
||||
void RegisterNotifier(NotifyEventHandler& hanlder) override;
|
||||
void ClearNotifiers();
|
||||
|
||||
[[nodiscard]] PreMergeEventHandler RegisterPreMergeEvent(const PreMergeEventCallback& callback) override;
|
||||
[[nodiscard]] PreMergeEventHandler RegisterPreMergeEvent(PreMergeEventCallback&& callback) override;
|
||||
[[nodiscard]] PostMergeEventHandler RegisterPostMergeEvent(const PostMergeEventCallback& callback) override;
|
||||
[[nodiscard]] PostMergeEventHandler RegisterPostMergeEvent(PostMergeEventCallback&& callback) override;
|
||||
[[nodiscard]] PreMergeEventHandler RegisterPreMergeEvent(PreMergeEventCallback callback) override;
|
||||
void RegisterPreMergeEvent(PreMergeEventHandler& handler) override;
|
||||
[[nodiscard]] PostMergeEventHandler RegisterPostMergeEvent(PostMergeEventCallback callback) override;
|
||||
void RegisterPostMergeEvent(PostMergeEventHandler& handler) override;
|
||||
void ClearMergeEvents();
|
||||
|
||||
bool Get(bool& result, AZStd::string_view path) const override;
|
||||
@@ -76,13 +76,13 @@ namespace AZ
|
||||
|
||||
bool Remove(AZStd::string_view path) override;
|
||||
|
||||
bool MergeCommandLineArgument(AZStd::string_view argument, AZStd::string_view rootKey,
|
||||
bool MergeCommandLineArgument(AZStd::string_view argument, AZStd::string_view anchorKey,
|
||||
const CommandLineArgumentSettings& commandLineSettings) override;
|
||||
bool MergeSettings(AZStd::string_view data, Format format) override;
|
||||
bool MergeSettingsFile(AZStd::string_view path, Format format, AZStd::string_view rootKey,
|
||||
bool MergeSettings(AZStd::string_view data, Format format, AZStd::string_view anchorKey = "") override;
|
||||
bool MergeSettingsFile(AZStd::string_view path, Format format, AZStd::string_view anchorKey = "",
|
||||
AZStd::vector<char>* scratchBuffer = nullptr) override;
|
||||
bool MergeSettingsFolder(AZStd::string_view path, const Specializations& specializations,
|
||||
AZStd::string_view platform, AZStd::string_view rootKey = "", AZStd::vector<char>* scratchBuffer = nullptr) override;
|
||||
AZStd::string_view platform, AZStd::string_view anchorKey = "", AZStd::vector<char>* scratchBuffer = nullptr) override;
|
||||
|
||||
void SetApplyPatchSettings(const AZ::JsonApplyPatchSettings& applyPatchSettings) override;
|
||||
void GetApplyPatchSettings(AZ::JsonApplyPatchSettings& applyPatchSettings) override;
|
||||
@@ -121,6 +121,19 @@ namespace AZ
|
||||
PreMergeEvent m_preMergeEvent;
|
||||
PostMergeEvent m_postMergeEvent;
|
||||
|
||||
//! NOTE: During SignalNotifier, the registered notify event handlers are moved to a local NotifyEvent
|
||||
//! Therefore setting a value within the registry during signaling will queue future SignalNotifer calls
|
||||
//! These calls will then be invoked after the current signaling has completex
|
||||
//! This is done to avoid deadlock if another thread attempts to access register a notifier or signal one
|
||||
mutable AZStd::mutex m_signalMutex;
|
||||
struct SignalNotifierArgs
|
||||
{
|
||||
FixedValueString m_jsonPath;
|
||||
Type m_type;
|
||||
};
|
||||
AZStd::deque<SignalNotifierArgs> m_signalNotifierQueue;
|
||||
AZStd::atomic_int m_signalCount{};
|
||||
|
||||
rapidjson::Document m_settings;
|
||||
JsonSerializerSettings m_serializationSettings;
|
||||
JsonDeserializerSettings m_deserializationSettings;
|
||||
|
||||
@@ -23,12 +23,12 @@ namespace AZ
|
||||
MOCK_CONST_METHOD1(GetType, Type(AZStd::string_view));
|
||||
MOCK_CONST_METHOD2(Visit, bool(Visitor&, AZStd::string_view));
|
||||
MOCK_CONST_METHOD2(Visit, bool(const VisitorCallback&, AZStd::string_view));
|
||||
MOCK_METHOD1(RegisterNotifier, NotifyEventHandler(const NotifyCallback&));
|
||||
MOCK_METHOD1(RegisterNotifier, NotifyEventHandler(NotifyCallback&&));
|
||||
MOCK_METHOD1(RegisterPreMergeEvent, PreMergeEventHandler(const PreMergeEventCallback&));
|
||||
MOCK_METHOD1(RegisterPreMergeEvent, PreMergeEventHandler(PreMergeEventCallback&&));
|
||||
MOCK_METHOD1(RegisterPostMergeEvent, PostMergeEventHandler(const PostMergeEventCallback&));
|
||||
MOCK_METHOD1(RegisterPostMergeEvent, PostMergeEventHandler(PostMergeEventCallback&&));
|
||||
MOCK_METHOD1(RegisterNotifier, NotifyEventHandler(NotifyCallback));
|
||||
MOCK_METHOD1(RegisterNotifier, void(NotifyEventHandler&));
|
||||
MOCK_METHOD1(RegisterPreMergeEvent, PreMergeEventHandler(PreMergeEventCallback));
|
||||
MOCK_METHOD1(RegisterPreMergeEvent, void(PreMergeEventHandler&));
|
||||
MOCK_METHOD1(RegisterPostMergeEvent, PostMergeEventHandler(PostMergeEventCallback));
|
||||
MOCK_METHOD1(RegisterPostMergeEvent, void(PostMergeEventHandler&));
|
||||
|
||||
MOCK_CONST_METHOD2(Get, bool(bool&, AZStd::string_view));
|
||||
MOCK_CONST_METHOD2(Get, bool(s64&, AZStd::string_view));
|
||||
@@ -49,7 +49,7 @@ namespace AZ
|
||||
MOCK_METHOD1(Remove, bool(AZStd::string_view));
|
||||
|
||||
MOCK_METHOD3(MergeCommandLineArgument, bool(AZStd::string_view, AZStd::string_view, const CommandLineArgumentSettings&));
|
||||
MOCK_METHOD2(MergeSettings, bool(AZStd::string_view, Format));
|
||||
MOCK_METHOD3(MergeSettings, bool(AZStd::string_view, Format, AZStd::string_view));
|
||||
MOCK_METHOD4(MergeSettingsFile, bool(AZStd::string_view, Format, AZStd::string_view, AZStd::vector<char>*));
|
||||
MOCK_METHOD5(
|
||||
MergeSettingsFolder,
|
||||
|
||||
+5
@@ -17,6 +17,11 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int StackConverter::FromNative(StackFrame*, unsigned int, void*)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SymbolStorage::LoadModuleData(const void*, unsigned int)
|
||||
{}
|
||||
|
||||
|
||||
@@ -78,6 +78,12 @@ StackRecorder::Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned
|
||||
return count;
|
||||
}
|
||||
|
||||
unsigned int StackConverter::FromNative([[maybe_unused]] StackFrame* frames, [[maybe_unused]] unsigned int maxNumOfFrames, [[maybe_unused]] void* nativeContext)
|
||||
{
|
||||
AZ_Assert(false, "StackConverter::FromNative() is not supported for UnixLike platform yet");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
SymbolStorage::DecodeFrames(const StackFrame* frames, unsigned int numFrames, StackLine* textLines)
|
||||
{
|
||||
|
||||
@@ -1048,9 +1048,9 @@ cleanup:
|
||||
unsigned int
|
||||
StackRecorder::Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned int suppressCount, void* nativeThread)
|
||||
{
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
unsigned int numFrames = 0;
|
||||
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
if (nativeThread == NULL)
|
||||
{
|
||||
++suppressCount; // Skip current call
|
||||
@@ -1079,9 +1079,8 @@ cleanup:
|
||||
|
||||
STACKFRAME64 sf;
|
||||
memset(&sf, 0, sizeof(STACKFRAME64));
|
||||
DWORD imageType;
|
||||
DWORD imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
|
||||
imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
sf.AddrPC.Offset = context.Rip;
|
||||
sf.AddrPC.Mode = AddrModeFlat;
|
||||
sf.AddrFrame.Offset = context.Rsp;
|
||||
@@ -1090,8 +1089,7 @@ cleanup:
|
||||
sf.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
EnterCriticalSection(&g_csDbgHelpDll);
|
||||
s32 frame = -(s32)suppressCount;
|
||||
for (; frame < (s32)maxNumOfFrames; ++frame)
|
||||
for (s32 frame = -static_cast<s32>(suppressCount); frame < static_cast<s32>(maxNumOfFrames); ++frame)
|
||||
{
|
||||
if (!g_StackWalk64(imageType, g_currentProcess, hThread, &sf, &context, 0, g_SymFunctionTableAccess64, g_SymGetModuleBase64, 0))
|
||||
{
|
||||
@@ -1111,15 +1109,68 @@ cleanup:
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&g_csDbgHelpDll);
|
||||
}
|
||||
return numFrames;
|
||||
}
|
||||
#else
|
||||
(void)frames;
|
||||
(void)maxNumOfFrames;
|
||||
(void)suppressCount;
|
||||
(void)nativeThread;
|
||||
return 0;
|
||||
AZ_UNUSED(frames);
|
||||
AZ_UNUSED(maxNumOfFrames);
|
||||
AZ_UNUSED(suppressCount);
|
||||
AZ_UNUSED(nativeThread);
|
||||
#endif // AZ_ENABLE_DEBUG_TOOLS
|
||||
|
||||
return numFrames;
|
||||
}
|
||||
|
||||
unsigned int StackConverter::FromNative(StackFrame* frames, unsigned int maxNumOfFrames, void* nativeContext)
|
||||
{
|
||||
unsigned int numFrames = 0;
|
||||
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
if (!g_dbgHelpLoaded)
|
||||
{
|
||||
LoadDbgHelp();
|
||||
}
|
||||
|
||||
HANDLE hThread;
|
||||
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS);
|
||||
|
||||
PCONTEXT nativeContextType = reinterpret_cast<PCONTEXT>(nativeContext);
|
||||
STACKFRAME64 sf;
|
||||
memset(&sf, 0, sizeof(STACKFRAME64));
|
||||
|
||||
DWORD imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
|
||||
sf.AddrPC.Offset = nativeContextType->Rip;
|
||||
sf.AddrPC.Mode = AddrModeFlat;
|
||||
sf.AddrFrame.Offset = nativeContextType->Rsp;
|
||||
sf.AddrFrame.Mode = AddrModeFlat;
|
||||
sf.AddrStack.Offset = nativeContextType->Rsp;
|
||||
sf.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
EnterCriticalSection(&g_csDbgHelpDll);
|
||||
for (unsigned int frame = 0; frame < maxNumOfFrames; ++frame)
|
||||
{
|
||||
if (!g_StackWalk64(imageType, g_currentProcess, hThread, &sf, nativeContext, 0, g_SymFunctionTableAccess64, g_SymGetModuleBase64, 0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (sf.AddrPC.Offset == sf.AddrReturn.Offset)
|
||||
{
|
||||
// "StackWalk64-Endless-Callstack!"
|
||||
break;
|
||||
}
|
||||
|
||||
frames[numFrames++].m_programCounter = sf.AddrPC.Offset;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&g_csDbgHelpDll);
|
||||
#else
|
||||
AZ_UNUSED(frames);
|
||||
AZ_UNUSED(maxNumOfFrames);
|
||||
AZ_UNUSED(nativeContext);
|
||||
#endif
|
||||
|
||||
return numFrames;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1413,7 +1413,7 @@ namespace UnitTest
|
||||
>;
|
||||
TYPED_TEST_CASE(HashedSetDifferentAllocatorFixture, SetTemplateConfigs);
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TYPED_TEST(HashedSetDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
|
||||
{
|
||||
using ContainerType = typename TypeParam::ContainerType;
|
||||
@@ -1435,7 +1435,7 @@ namespace UnitTest
|
||||
}
|
||||
}, ".*");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
template<typename ContainerType>
|
||||
class HashedMapContainers
|
||||
@@ -1811,7 +1811,7 @@ namespace UnitTest
|
||||
>;
|
||||
TYPED_TEST_CASE(HashedMapDifferentAllocatorFixture, MapTemplateConfigs);
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TYPED_TEST(HashedMapDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
|
||||
{
|
||||
using ContainerType = typename TypeParam::ContainerType;
|
||||
@@ -1833,7 +1833,7 @@ namespace UnitTest
|
||||
}
|
||||
} , ".*");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
namespace HashedContainerTransparentTestInternal
|
||||
{
|
||||
|
||||
@@ -1095,7 +1095,7 @@ namespace UnitTest
|
||||
>;
|
||||
TYPED_TEST_CASE(TreeSetDifferentAllocatorFixture, SetTemplateConfigs);
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TYPED_TEST(TreeSetDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
|
||||
{
|
||||
using ContainerType = typename TypeParam::ContainerType;
|
||||
@@ -1117,7 +1117,7 @@ namespace UnitTest
|
||||
}
|
||||
}, ".*");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
TYPED_TEST(TreeSetDifferentAllocatorFixture, SwapMovesElementsWhenAllocatorsDiffer)
|
||||
{
|
||||
@@ -1516,7 +1516,7 @@ namespace UnitTest
|
||||
>;
|
||||
TYPED_TEST_CASE(TreeMapDifferentAllocatorFixture, MapTemplateConfigs);
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TYPED_TEST(TreeMapDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
|
||||
{
|
||||
using ContainerType = typename TypeParam::ContainerType;
|
||||
@@ -1538,7 +1538,7 @@ namespace UnitTest
|
||||
}
|
||||
}, ".*");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
TYPED_TEST(TreeMapDifferentAllocatorFixture, SwapMovesElementsWhenAllocatorsDiffer)
|
||||
{
|
||||
|
||||
@@ -1595,7 +1595,7 @@ namespace UnitTest
|
||||
}
|
||||
};
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TEST_F(ThreadEventsDeathTest, UsingClientBus_AvoidsDeadlock)
|
||||
{
|
||||
EXPECT_EXIT(
|
||||
@@ -1608,5 +1608,5 @@ namespace UnitTest
|
||||
, ::testing::ExitedWithCode(0),".*");
|
||||
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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/UnitTest/TestTypes.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class UnhandledExceptions
|
||||
: public ScopedAllocatorSetupFixture
|
||||
{
|
||||
|
||||
public:
|
||||
void causeAccessViolation()
|
||||
{
|
||||
int* someVariable = reinterpret_cast<int*>(0);
|
||||
*someVariable = 0;
|
||||
}
|
||||
};
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TEST_F(UnhandledExceptions, Handle)
|
||||
{
|
||||
EXPECT_DEATH(causeAccessViolation(), "");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -144,14 +144,13 @@ namespace UnitTest
|
||||
}
|
||||
};
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
// SPEC-2669: Disabled since it is causing hangs on Linux
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TEST_F(AllocatorsTestFixtureLeakDetectionDeathTest_SKIPCODECOVERAGE, AllocatorLeak)
|
||||
{
|
||||
// testing that the TraceBusHook will fail on cause the test to die
|
||||
EXPECT_DEATH(TestAllocatorLeak(), "");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Testing ScopedAllocatorSetupFixture. Testing that detects leaks
|
||||
|
||||
@@ -327,7 +327,7 @@ namespace JsonSerializationTests
|
||||
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
|
||||
}
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
using JsonSerializationDeathTests = JsonRegistrationContextTests;
|
||||
TEST_F(JsonSerializationDeathTests, DoubleUnregisterSerializer_Asserts)
|
||||
{
|
||||
@@ -338,5 +338,6 @@ namespace JsonSerializationTests
|
||||
}, ".*"
|
||||
);
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
} //namespace JsonSerializationTests
|
||||
|
||||
@@ -1299,6 +1299,35 @@ namespace SettingsRegistryTests
|
||||
EXPECT_FALSE(m_registry->MergeCommandLineArgument(" ", {}, {}));
|
||||
}
|
||||
|
||||
//
|
||||
// MergeSettings
|
||||
//
|
||||
TEST_F(SettingsRegistryTest, MergeSettings_MergeJsonWithAnchorKey_StoresSettingsUnderneathKey)
|
||||
{
|
||||
constexpr AZStd::string_view anchorKey = "/Anchor/Root/0";
|
||||
constexpr auto mergeFormat = AZ::SettingsRegistryInterface::Format::JsonMergePatch;
|
||||
EXPECT_TRUE(m_registry->MergeSettings(R"({ "Test": "1" })", mergeFormat, anchorKey));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Array, m_registry->GetType("/Anchor/Root"));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType("/Anchor/Root/0"));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Anchor/Root/0/Test"));
|
||||
}
|
||||
|
||||
TEST_F(SettingsRegistryTest, MergeSettings_NotifierSignals_AtAnchorKeyAndStoresMergeType)
|
||||
{
|
||||
AZStd::string_view anchorKey = "/Anchor/Root";
|
||||
bool callbackInvoked{};
|
||||
auto callback = [anchorKey, &callbackInvoked](AZStd::string_view path, AZ::SettingsRegistryInterface::Type type)
|
||||
{
|
||||
EXPECT_EQ(anchorKey, path);
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Array, type);
|
||||
callbackInvoked = true;
|
||||
};
|
||||
auto testNotifier1 = m_registry->RegisterNotifier(callback);
|
||||
constexpr auto mergeFormat = AZ::SettingsRegistryInterface::Format::JsonMergePatch;
|
||||
EXPECT_TRUE(m_registry->MergeSettings(R"([ "Test" ])", mergeFormat, anchorKey));
|
||||
EXPECT_TRUE(callbackInvoked);
|
||||
}
|
||||
|
||||
//
|
||||
// MergeSettingsFile
|
||||
//
|
||||
@@ -1331,7 +1360,7 @@ namespace SettingsRegistryTests
|
||||
|
||||
auto callback = [this](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
EXPECT_TRUE(path.empty());
|
||||
EXPECT_EQ("/Path", path);
|
||||
AZ::s64 value = -1;
|
||||
bool result = m_registry->Get(value, "/Path/Test");
|
||||
EXPECT_TRUE(result);
|
||||
|
||||
@@ -72,6 +72,7 @@ set(FILES
|
||||
Debug/AssetTracking.cpp
|
||||
Debug/LocalFileEventLoggerTests.cpp
|
||||
Debug/Trace.cpp
|
||||
Debug/UnhandledExceptions.cpp
|
||||
Name/NameJsonSerializerTests.cpp
|
||||
Name/NameTests.cpp
|
||||
RTTI/TypeSafeIntegralTests.cpp
|
||||
|
||||
Reference in New Issue
Block a user