merge from development

Signed-off-by: greerdv <greerdv@amazon.com>
This commit is contained in:
greerdv
2021-10-11 10:23:42 +01:00
134 changed files with 1480 additions and 249 deletions
@@ -6,12 +6,7 @@
#
#
################################################################################
# Atom Renderer: Automated Tests
# Runs EditorPythonBindings (hydra) scripts inside the Editor to verify test results for the Atom renderer.
################################################################################
if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedTesting IN_LIST LY_PROJECTS)
if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_pytest(
NAME AutomatedTesting::Atom_TestSuite_Main
TEST_SUITE main
@@ -25,6 +25,7 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests")
class TestAtomEditorComponentsMain(object):
"""Holds tests for Atom components."""
@pytest.mark.xfail(reason="This test is being marked xfail as it failed during an unrelated development run. See LYN-7530 for more details.")
def test_AtomEditorComponents_AddedToEntity(self, request, editor, level, workspace, project, launcher_platform):
"""
Please review the hydra script run by this test for more specific test info.
@@ -220,20 +220,18 @@ class TestPerformanceBenchmarkSuite(object):
@pytest.mark.system
class TestMaterialEditor(object):
@pytest.mark.parametrize("cfg_args", ["-rhi=dx12", "-rhi=Vulkan"])
@pytest.mark.parametrize("cfg_args,expected_lines", [
pytest.param("-rhi=dx12", ["Registering dx12 RHI"]),
pytest.param("-rhi=Vulkan", ["Registering vulkan RHI"])
])
@pytest.mark.parametrize("exe_file_name", ["MaterialEditor"])
def test_MaterialEditorLaunch_AllRHIOptionsSucceed(
self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args):
self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args,
expected_lines):
"""
Tests each valid RHI option (Null RHI excluded) can be launched with the MaterialEditor.
Checks for the "Finished loading viewport configurations." success message post launch.
Checks for the specific expected_lines messaging for each RHI type.
"""
expected_lines = ["Finished loading viewport configurations."]
unexpected_lines = [
# "Trace::Assert",
# "Trace::Error",
"Traceback (most recent call last):",
]
hydra.launch_and_validate_results(
request,
@@ -243,7 +241,7 @@ class TestMaterialEditor(object):
run_python="--runpython",
timeout=60,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
unexpected_lines=[],
halt_on_unexpected=False,
null_renderer=False,
cfg_args=[cfg_args],
@@ -14,36 +14,50 @@ from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAutomation(EditorTestSuite):
@pytest.mark.test_case_id("C32078118")
class AtomEditorComponents_DecalAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_DecalAdded as test_module
@pytest.mark.test_case_id("C32078119")
class AtomEditorComponents_DepthOfFieldAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module
@pytest.mark.test_case_id("C32078120")
class AtomEditorComponents_DirectionalLightAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_DirectionalLightAdded as test_module
@pytest.mark.test_case_id("C32078121")
class AtomEditorComponents_ExposureControlAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_ExposureControlAdded as test_module
@pytest.mark.test_case_id("C32078115")
class AtomEditorComponents_GlobalSkylightIBLAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_GlobalSkylightIBLAdded as test_module
@pytest.mark.test_case_id("C32078125")
class AtomEditorComponents_PhysicalSkyAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_PhysicalSkyAdded as test_module
@pytest.mark.test_case_id("C32078131")
class AtomEditorComponents_PostFXRadiusWeightModifierAdded(EditorSharedTest):
from Atom.tests import (
hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded as test_module)
@pytest.mark.test_case_id("C32078117")
class AtomEditorComponents_LightAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_LightAdded as test_module
@pytest.mark.test_case_id("C36525660")
class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module
@pytest.mark.test_case_id("C32078128")
class AtomEditorComponents_ReflectionProbeAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_ReflectionProbeAdded as test_module
@pytest.mark.test_case_id("C32078124")
class AtomEditorComponents_MeshAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_MeshAdded as test_module
class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest):
from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module
@@ -0,0 +1,171 @@
"""
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
"""
class Tests:
creation_undo = (
"UNDO Entity creation success",
"UNDO Entity creation failed")
creation_redo = (
"REDO Entity creation success",
"REDO Entity creation failed")
mesh_entity_creation = (
"Mesh Entity successfully created",
"Mesh Entity failed to be created")
mesh_component_added = (
"Entity has a Mesh component",
"Entity failed to find Mesh component")
mesh_asset_specified = (
"Mesh asset set",
"Mesh asset not set")
enter_game_mode = (
"Entered game mode",
"Failed to enter game mode")
exit_game_mode = (
"Exited game mode",
"Couldn't exit game mode")
is_visible = (
"Entity is visible",
"Entity was not visible")
is_hidden = (
"Entity is hidden",
"Entity was not hidden")
entity_deleted = (
"Entity deleted",
"Entity was not deleted")
deletion_undo = (
"UNDO deletion success",
"UNDO deletion failed")
deletion_redo = (
"REDO deletion success",
"REDO deletion failed")
def AtomEditorComponents_Mesh_AddedToEntity():
"""
Summary:
Tests the Mesh component can be added to an entity and has the expected functionality.
Test setup:
- Wait for Editor idle loop.
- Open the "Base" level.
Expected Behavior:
The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
Creation and deletion undo/redo should also work.
Test Steps:
1) Create a Mesh entity with no components.
2) Add a Mesh component to Mesh entity.
3) UNDO the entity creation and component addition.
4) REDO the entity creation and component addition.
5) Specify the Mesh component asset
6) Enter/Exit game mode.
7) Test IsHidden.
8) Test IsVisible.
9) Delete Mesh entity.
10) UNDO deletion.
11) REDO deletion.
12) Look for errors.
:return: None
"""
import os
import azlmbr.legacy.general as general
from editor_python_test_tools.asset_utils import Asset
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
helper.init_idle()
helper.open_level("", "Base")
# Test steps begin.
# 1. Create a Mesh entity with no components.
mesh_name = "Mesh"
mesh_entity = EditorEntity.create_editor_entity(mesh_name)
Report.critical_result(Tests.mesh_entity_creation, mesh_entity.exists())
# 2. Add a Mesh component to Mesh entity.
mesh_component = mesh_entity.add_component(mesh_name)
Report.critical_result(
Tests.mesh_component_added,
mesh_entity.has_component(mesh_name))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
general.undo()
# -> UNDO naming entity.
general.undo()
# -> UNDO selecting entity.
general.undo()
# -> UNDO entity creation.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.creation_undo, not mesh_entity.exists())
# 4. REDO the entity creation and component addition.
# -> REDO entity creation.
general.redo()
# -> REDO selecting entity.
general.redo()
# -> REDO naming entity.
general.redo()
# -> REDO component addition.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.creation_redo, mesh_entity.exists())
# 5. Set Mesh component asset property
mesh_property_asset = 'Controller|Configuration|Mesh Asset'
model_path = os.path.join('Objects', 'shaderball', 'shaderball_default_1m.azmodel')
model = Asset.find_asset_by_path(model_path)
mesh_component.set_component_property_value(mesh_property_asset, model.id)
Report.result(Tests.mesh_asset_specified,
mesh_component.get_component_property_value(mesh_property_asset) == model.id)
# 6. Enter/Exit game mode.
helper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
helper.exit_game_mode(Tests.exit_game_mode)
# 7. Test IsHidden.
mesh_entity.set_visibility_state(False)
Report.result(Tests.is_hidden, mesh_entity.is_hidden() is True)
# 8. Test IsVisible.
mesh_entity.set_visibility_state(True)
general.idle_wait_frames(1)
Report.result(Tests.is_visible, mesh_entity.is_visible() is True)
# 9. Delete Mesh entity.
mesh_entity.delete()
Report.result(Tests.entity_deleted, not mesh_entity.exists())
# 10. UNDO deletion.
general.undo()
Report.result(Tests.deletion_undo, mesh_entity.exists())
# 11. REDO deletion.
general.redo()
Report.result(Tests.deletion_redo, not mesh_entity.exists())
# 12. Look for errors or asserts.
helper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in error_tracer.asserts:
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AtomEditorComponents_Mesh_AddedToEntity)
@@ -173,6 +173,7 @@ namespace AZ
if (assetTracker)
{
assetTracker->FixUpAsset(*instance);
assetTracker->AddAsset(*instance);
}
@@ -185,7 +186,20 @@ namespace AZ
return context.Report(result, message);
}
void SerializedAssetTracker::AddAsset(Asset<AssetData>& asset)
void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback)
{
m_assetFixUpCallback = AZStd::move(assetFixUpCallback);
}
void SerializedAssetTracker::FixUpAsset(Asset<AssetData>& asset)
{
if (m_assetFixUpCallback)
{
m_assetFixUpCallback(asset);
}
}
void SerializedAssetTracker::AddAsset(Asset<AssetData> asset)
{
m_serializedAssets.emplace_back(asset);
}
@@ -199,5 +213,6 @@ namespace AZ
{
return m_serializedAssets;
}
} // namespace Data
} // namespace AZ
@@ -39,13 +39,18 @@ namespace AZ
{
public:
AZ_RTTI(SerializedAssetTracker, "{1E067091-8C0A-44B1-A455-6E97663F6963}");
using AssetFixUp = AZStd::function<void(Asset<AssetData>& asset)>;
void AddAsset(Asset<AssetData>& asset);
void SetAssetFixUp(AssetFixUp assetFixUpCallback);
void FixUpAsset(Asset<AssetData>& asset);
void AddAsset(Asset<AssetData> asset);
AZStd::vector<Asset<AssetData>>& GetTrackedAssets();
const AZStd::vector<Asset<AssetData>>& GetTrackedAssets() const;
private:
AZStd::vector<Asset<AssetData>> m_serializedAssets;
AssetFixUp m_assetFixUpCallback;
};
} // namespace Data
} // namespace AZ
@@ -476,15 +476,16 @@ namespace AZ
// Responsible for using the Json Serialization Issue Callback system
// to determine when a JSON Patch or JSON Merge Patch modifies a value
// at a path underneath the IConsole::ConsoleRootCommandKey JSON pointer
// at a path underneath the IConsole::ConsoleRuntimeCommandKey JSON pointer
JsonSerializationResult::ResultCode operator()(AZStd::string_view message,
JsonSerializationResult::ResultCode result, AZStd::string_view path)
{
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
constexpr AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator };
constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator };
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
if (result.GetTask() == JsonSerializationResult::Tasks::Merge
&& result.GetProcessing() == JsonSerializationResult::Processing::Completed
&& inputKey.IsRelativeTo(consoleRootCommandKey))
&& (inputKey.IsRelativeTo(consoleRootCommandKey) || inputKey.IsRelativeTo(consoleAutoexecCommandKey)))
{
if (auto type = m_settingsRegistry.GetType(path); type != SettingsRegistryInterface::Type::NoType)
{
@@ -510,12 +511,24 @@ namespace AZ
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
constexpr AZ::IO::PathView consoleRuntimeCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator };
constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator };
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
// The ConsoleRootComamndKey is not a command itself so strictly children keys are being examined
if (inputKey.IsRelativeTo(consoleRootCommandKey) && inputKey != consoleRootCommandKey)
// Abuses the IsRelativeToFuncton function of the path class to extract the console
// command from the settings registry objects
FixedValueString command;
if (inputKey != consoleRuntimeCommandKey && inputKey.IsRelativeTo(consoleRuntimeCommandKey))
{
command = inputKey.LexicallyRelative(consoleRuntimeCommandKey).Native();
}
else if (inputKey != consoleAutoexecCommandKey && inputKey.IsRelativeTo(consoleAutoexecCommandKey))
{
command = inputKey.LexicallyRelative(consoleAutoexecCommandKey).Native();
}
if (!command.empty())
{
FixedValueString command = inputKey.LexicallyRelative(consoleRootCommandKey).Native();
ConsoleCommandContainer commandArgs;
// Argument string which stores the value from the Settings Registry long enough
// to pass into the PerformCommand. The ConsoleCommandContainer stores string_views
@@ -603,9 +616,10 @@ namespace AZ
void Console::RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry)
{
// Make sure the there is a JSON object at the path of AZ::IConsole::ConsoleRootCommandKey
// 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": {} } }}})",
settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } } })"
R"(,"O3DE": { "Autoexec": { "ConsoleCommands": {} } } })",
SettingsRegistryInterface::Format::JsonMergePatch);
m_consoleCommandKeyHandler = settingsRegistry.RegisterNotifier(ConsoleCommandKeyNotificationHandler{ settingsRegistry, *this });
@@ -31,7 +31,8 @@ namespace AZ
using FunctorVisitor = AZStd::function<void(ConsoleFunctorBase*)>;
inline static constexpr AZStd::string_view ConsoleRootCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
inline static constexpr AZStd::string_view ConsoleRuntimeCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
inline static constexpr AZStd::string_view ConsoleAutoexecCommandKey = "/O3DE/Autoexec/ConsoleCommands";
IConsole() = default;
virtual ~IConsole() = default;
+17 -9
View File
@@ -160,8 +160,8 @@ namespace AZ
/**
* Locking primitive that is used when executing events in the event queue.
*/
using EventQueueMutexType = typename AZStd::Utils::if_c<AZStd::is_same<typename Traits::EventQueueMutexType, NullMutex>::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
MutexType, typename Traits::EventQueueMutexType>::type;
using EventQueueMutexType = AZStd::conditional_t<AZStd::is_same<typename Traits::EventQueueMutexType, NullMutex>::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
MutexType, typename Traits::EventQueueMutexType>;
/**
* Pointer to an address on the bus.
@@ -180,14 +180,22 @@ namespace AZ
* `<BusName>::ExecuteQueuedEvents()`.
* By default, the event queue is disabled.
*/
static const bool EnableEventQueue = Traits::EnableEventQueue;
static const bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
static const bool EnableQueuedReferences = Traits::EnableQueuedReferences;
static constexpr bool EnableEventQueue = Traits::EnableEventQueue;
static constexpr bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
static constexpr bool EnableQueuedReferences = Traits::EnableQueuedReferences;
/**
* True if the EBus supports more than one address. Otherwise, false.
*/
static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
static constexpr bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
/**
* Template Lock Guard class that wraps around the Mutex
* The EBus uses for Dispatching Events.
* This is not the EBus Context Mutex if LocklessDispatch is true
*/
template <typename DispatchMutex>
using DispatchLockGuard = typename Traits::template DispatchLockGuard<DispatchMutex, Traits::LocklessDispatch>;
};
/**
@@ -460,7 +468,7 @@ namespace AZ
using BusPtr = typename Traits::BusPtr;
/**
* Helper to queue an event by BusIdType only when function queueing is enabled
* Helper to queue an event by BusIdType only when function queueing is enabled
* @param id Address ID. Handlers that are connected to this ID will receive the event.
* @param func Function pointer of the event to dispatch.
* @param args Function arguments that are passed to each handler.
@@ -581,7 +589,7 @@ namespace AZ
, public EBusBroadcaster<Bus, Traits>
, public EBusEventer<Bus, Traits>
, public EBusEventEnumerator<Bus, Traits>
, public AZStd::Utils::if_c<Traits::EnableEventQueue, EBusEventQueue<Bus, Traits>, EBusNullQueue>::type
, public AZStd::conditional_t<Traits::EnableEventQueue, EBusEventQueue<Bus, Traits>, EBusNullQueue>
{
};
@@ -599,7 +607,7 @@ namespace AZ
: public EventDispatcher<Bus, Traits>
, public EBusBroadcaster<Bus, Traits>
, public EBusBroadcastEnumerator<Bus, Traits>
, public AZStd::Utils::if_c<Traits::EnableEventQueue, EBusBroadcastQueue<Bus, Traits>, EBusNullQueue>::type
, public AZStd::conditional_t<Traits::EnableEventQueue, EBusBroadcastQueue<Bus, Traits>, EBusNullQueue>
{
};
+33 -2
View File
@@ -236,6 +236,17 @@ namespace AZ
* code before or after an event.
*/
using EventProcessingPolicy = EBusEventProcessingPolicy;
/**
* Template Lock Guard class that wraps around the Mutex
* The EBus Context uses the LockGuard when dispatching
* (either AZStd::scoped_lock<MutexType> or NullLockGuard<MutexType>)
* The IsLocklessDispatch bool is there to defer evaluation of the LocklessDispatch constant
* Otherwise the value above in EBusTraits.h is always used and not the value
* that the derived trait class sets.
*/
template <typename DispatchMutex, bool IsLocklessDispatch>
using DispatchLockGuard = AZStd::conditional_t<IsLocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
};
namespace Internal
@@ -496,6 +507,14 @@ namespace AZ
*/
static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
/**
* Template Lock Guard class that wraps around the Mutex
* The EBus uses for Dispatching Events.
* This is not EBus Context Mutex when LocklessDispatch is set
*/
template <typename DispatchMutex>
using DispatchLockGuard = typename ImplTraits::template DispatchLockGuard<DispatchMutex>;
//////////////////////////////////////////////////////////////////////////
// Check to help identify common mistakes
/// @cond EXCLUDE_DOCS
@@ -620,11 +639,11 @@ namespace AZ
using ContextMutexType = AZStd::conditional_t<BusTraits::LocklessDispatch && AZStd::is_same_v<MutexType, AZ::NullMutex>, AZStd::shared_mutex, MutexType>;
/**
* The scoped lock guard to use (either AZStd::scoped_lock<MutexType> or NullLockGuard<MutexType>
* The scoped lock guard to use
* during broadcast/event dispatch.
* @see EBusTraits::LocklessDispatch
*/
using DispatchLockGuard = AZStd::conditional_t<BusTraits::LocklessDispatch, AZ::Internal::NullLockGuard<ContextMutexType>, AZStd::scoped_lock<ContextMutexType>>;
using DispatchLockGuard = DispatchLockGuard<ContextMutexType>;
/**
* The scoped lock guard to use during connection. Some specialized policies execute handler methods which
@@ -704,6 +723,11 @@ namespace AZ
static Context& GetOrCreateContext(bool trackCallstack=true);
static bool IsInDispatch(Context* context = GetContext(false));
/**
* Returns whether the EBus context is in the middle of a dispatch on the current thread
*/
static bool IsInDispatchThisThread(Context* context = GetContext(false));
/// @cond EXCLUDE_DOCS
struct RouterCallstackEntry
: public CallstackEntry
@@ -1208,6 +1232,13 @@ AZ_POP_DISABLE_WARNING
return context != nullptr && context->m_dispatches > 0;
}
template<class Interface, class Traits>
bool EBus<Interface, Traits>::IsInDispatchThisThread(Context* context)
{
return context != nullptr && context->s_callstack != nullptr
&& context->s_callstack->m_prev != nullptr;
}
//=========================================================================
template<class Interface, class Traits>
EBus<Interface, Traits>::RouterCallstackEntry::RouterCallstackEntry(Iterator it, const BusIdType* busId, bool isQueued, bool isReverse)
@@ -204,14 +204,14 @@ namespace AZ
[[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;
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(PreMergeEventCallback&& 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(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;
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(PostMergeEventCallback&& callback) = 0;
//! Gets the boolean value at the provided path.
//! @param result The target to write the result to.
@@ -0,0 +1,136 @@
/*
* 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/Settings/SettingsRegistryVisitorUtils.h>
namespace AZ::SettingsRegistryVisitorUtils
{
// Field Visitor implementation
FieldVisitor::FieldVisitor() = default;
FieldVisitor::FieldVisitor(VisitFieldType visitFieldType)
: m_visitFieldType{ visitFieldType }
{
}
auto FieldVisitor::Traverse(AZStd::string_view path, AZStd::string_view valueName,
VisitAction action, Type type) -> VisitResponse
{
// A default response skip prevents visiting grand children(depth 2 or lower)
VisitResponse visitResponse = VisitResponse::Skip;
if (action == VisitAction::Begin)
{
// Invoke FieldVisitor override if the root path has been set
if (m_rootPath.has_value())
{
Visit(path, valueName, type);
}
// To make sure only the direct children are visited(depth 1)
// set the root path once and set the VisitReponsoe
// to Continue to recurse into is fields
if (!m_rootPath.has_value())
{
bool visitableFieldType{};
switch (m_visitFieldType)
{
case VisitFieldType::Array:
visitableFieldType = type == Type::Array;
break;
case VisitFieldType::Object:
visitableFieldType = type == Type::Object;
break;
case VisitFieldType::ArrayOrObject:
visitableFieldType = type == Type::Array || type ==Type::Object;
break;
default:
AZ_Error("FieldVisitor", false, "The field visitation type value is invalid");
break;
}
if (visitableFieldType)
{
m_rootPath = path;
visitResponse = VisitResponse::Continue;
}
}
}
else if (action == VisitAction::Value)
{
// Invoke FieldVisitor override if the root path has been set
if (m_rootPath.has_value())
{
Visit(path, valueName, type);
}
}
else if (action == VisitAction::End)
{
// Reset m_rootPath back to null when the root path has finished being visited
if (m_rootPath.has_value() && *m_rootPath == path)
{
m_rootPath = AZStd::nullopt;
}
}
return visitResponse;
}
// Array Visitor implementation
ArrayVisitor::ArrayVisitor()
: FieldVisitor(VisitFieldType::Array)
{
}
// Object Visitor implementation
ObjectVisitor::ObjectVisitor()
: FieldVisitor(VisitFieldType::Object)
{
}
// Generic VisitField Callback implemention
template <typename BaseVisitor>
bool VisitFieldCallback(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
{
struct VisitFieldVisitor
: BaseVisitor
{
using BaseVisitor::Visit;
VisitFieldVisitor(const VisitorCallback& visitCallback)
: m_visitCallback{ visitCallback }
{}
void Visit(AZStd::string_view path, AZStd::string_view fieldIndex, typename BaseVisitor::Type type) override
{
m_visitCallback(path, fieldIndex, type);
}
const VisitorCallback& m_visitCallback;
};
VisitFieldVisitor visitor{ visitCallback };
return settingsRegistry.Visit(visitor, path);
}
// VisitField implementation
bool VisitField(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
{
return VisitFieldCallback<FieldVisitor>(settingsRegistry, visitCallback, path);
}
// VisitArray implementation
bool VisitArray(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
{
return VisitFieldCallback<ArrayVisitor>(settingsRegistry, visitCallback, path);
}
// VisitObject implementation
bool VisitObject(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
{
return VisitFieldCallback<ObjectVisitor>(settingsRegistry, visitCallback, path);
}
}
@@ -0,0 +1,83 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Settings/SettingsRegistry.h>
namespace AZ::SettingsRegistryVisitorUtils
{
//! Interface for visiting the fields of an array or object
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
struct FieldVisitor
: public AZ::SettingsRegistryInterface::Visitor
{
using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse;
using VisitAction = AZ::SettingsRegistryInterface::VisitAction;
using Type = AZ::SettingsRegistryInterface::Type;
FieldVisitor();
// Bring the base class visitor functions into scope
using AZ::SettingsRegistryInterface::Visitor::Visit;
virtual void Visit(AZStd::string_view path, AZStd::string_view arrayIndex, Type type) = 0;
protected:
// VisitFieldType is used for filtering the type of referenced by the root path
enum class VisitFieldType
{
Array,
Object,
ArrayOrObject
};
FieldVisitor(const VisitFieldType visitFieldType);
private:
VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName,
VisitAction action, Type type) override;
VisitFieldType m_visitFieldType{ VisitFieldType::ArrayOrObject };
AZStd::optional<AZ::SettingsRegistryInterface::FixedValueString> m_rootPath;
};
//! Interface for visiting the fields of an array
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
struct ArrayVisitor
: public FieldVisitor
{
ArrayVisitor();
};
//! Interface for visiting the fields of an object
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
struct ObjectVisitor
: public FieldVisitor
{
ObjectVisitor();
};
//! Signature of callback funcition invoked when visiting an element of an array or object
using VisitorCallback = AZStd::function<void(AZStd::string_view path, AZStd::string_view fieldName,
AZ::SettingsRegistryInterface::Type)>;
//! Invokes the visitor callback for each element of either the array or object at @path
//! If @path is not an array or object, then no elements are visited
//! This function will not recurse into children of elements
//! @visitCallback functor that is invoked for each array or object element found
bool VisitField(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
//! Invokes the visitor callback for each element of the array at @path
//! If @path is not an array, then no elements are visited
//! This function will not recurse into children of elements
//! @visitCallback functor that is invoked for each array element found
bool VisitArray(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
//! Invokes the visitor callback for each element of the object at @path
//! If @path is not an object, then no elements are visited
//! This function will not recurse into children of elements
//! @visitCallback functor that is invoked for each object element found
bool VisitObject(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
}
@@ -566,6 +566,8 @@ set(FILES
Settings/SettingsRegistryMergeUtils.h
Settings/SettingsRegistryScriptUtils.cpp
Settings/SettingsRegistryScriptUtils.h
Settings/SettingsRegistryVisitorUtils.cpp
Settings/SettingsRegistryVisitorUtils.h
State/HSM.cpp
State/HSM.h
Statistics/NamedRunningStatistic.h
+193 -8
View File
@@ -2088,7 +2088,7 @@ namespace UnitTest
DisconnectNextHandlerByIdImpl multiHandler2;
multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::firstBusAddress);
multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::secondBusAddress);
// Set the first handler m_nextHandler field to point to the second handler
multiHandler1.m_nextHandler = &multiHandler2;
@@ -2807,7 +2807,7 @@ namespace UnitTest
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(m_val % m_maxSleep));
}
}
void DoConnect() override
{
MyEventGroupBus::Handler::BusConnect(m_id);
@@ -2854,7 +2854,7 @@ namespace UnitTest
}
MyEventGroupBus::Event(id, &MyEventGroupBus::Events::Calculate, i, i * 2, i << 4);
LocklessConnectorBus::Event(id, &LocklessConnectorBus::Events::DoDisconnect);
bool failed = (AZStd::find_if(&sentinel[0], end, [](char s) { return s != 0; }) != end);
@@ -2891,7 +2891,7 @@ namespace UnitTest
{
MyEventGroupImpl()
{
}
~MyEventGroupImpl() override
@@ -3614,7 +3614,7 @@ namespace UnitTest
{
AZStd::this_thread::yield();
}
EXPECT_GE(AZStd::chrono::system_clock::now(), endTime);
};
AZStd::thread connectThread([&connectHandler, &waitHandler]()
@@ -3813,7 +3813,7 @@ namespace UnitTest
struct LastHandlerDisconnectHandler
: public LastHandlerDisconnectBus::Handler
{
void OnEvent() override
void OnEvent() override
{
++m_numOnEvents;
BusDisconnect();
@@ -3854,7 +3854,7 @@ namespace UnitTest
struct DisconnectAssertHandler
: public DisconnectAssertBus::Handler
{
};
TEST_F(EBus, HandlerDestroyedWithoutDisconnect_Asserts)
@@ -3995,6 +3995,191 @@ namespace UnitTest
idTestRequest.Disconnect();
}
// IsInDispatchThisThread
struct IsInThreadDispatchRequests
: AZ::EBusTraits
{
using MutexType = AZStd::recursive_mutex;
};
using IsInThreadDispatchBus = AZ::EBus<IsInThreadDispatchRequests>;
class IsInThreadDispatchHandler
: public IsInThreadDispatchBus::Handler
{};
TEST_F(EBus, InvokingIsInThisThread_ReturnsSuccess_OnlyIfThreadIsInDispatch)
{
IsInThreadDispatchHandler handler;
handler.BusConnect();
auto ThreadDispatcher = [](IsInThreadDispatchRequests*)
{
EXPECT_TRUE(IsInThreadDispatchBus::IsInDispatchThisThread());
auto PerThreadBusDispatch = []()
{
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
};
AZStd::array threads{ AZStd::thread(PerThreadBusDispatch), AZStd::thread(PerThreadBusDispatch) };
for (AZStd::thread& thread : threads)
{
thread.join();
}
};
static constexpr size_t ThreadDispatcherIterations = 4;
for (size_t iteration = 0; iteration < ThreadDispatcherIterations; ++iteration)
{
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
IsInThreadDispatchBus::Broadcast(ThreadDispatcher);
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
}
}
// Thread Dispatch Policy
struct ThreadDispatchTestBusTraits
: AZ::EBusTraits
{
using MutexType = AZStd::recursive_mutex;
struct PostThreadDispatchTestInvoker
{
~PostThreadDispatchTestInvoker();
};
template <typename DispatchMutex>
struct ThreadDispatchTestLockGuard
{
ThreadDispatchTestLockGuard(DispatchMutex& contextMutex)
: m_lock{ contextMutex }
{}
ThreadDispatchTestLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock)
: m_lock{ contextMutex, adopt_lock }
{}
ThreadDispatchTestLockGuard(const ThreadDispatchTestLockGuard&) = delete;
ThreadDispatchTestLockGuard& operator=(const ThreadDispatchTestLockGuard&) = delete;
private:
PostThreadDispatchTestInvoker m_threadPolicyInvoker;
using LockType = AZStd::conditional_t<LocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
LockType m_lock;
};
template <typename DispatchMutex, bool IsLocklessDispatch>
using DispatchLockGuard = ThreadDispatchTestLockGuard<DispatchMutex>;
static inline AZStd::atomic<int32_t> s_threadPostDispatchCalls;
};
class ThreadDispatchTestRequests
{
public:
virtual void FirstCall() = 0;
virtual void SecondCall() = 0;
virtual void ThirdCall() = 0;
};
using ThreadDispatchTestBus = AZ::EBus<ThreadDispatchTestRequests, ThreadDispatchTestBusTraits>;
ThreadDispatchTestBusTraits::PostThreadDispatchTestInvoker::~PostThreadDispatchTestInvoker()
{
if (!ThreadDispatchTestBus::IsInDispatchThisThread())
{
++s_threadPostDispatchCalls;
}
}
class ThreadDispatchTestHandler
: public ThreadDispatchTestBus::Handler
{
public:
void Connect()
{
ThreadDispatchTestBus::Handler::BusConnect();
}
void Disconnect()
{
ThreadDispatchTestBus::Handler::BusDisconnect();
}
void FirstCall() override
{
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall);
}
void SecondCall() override
{
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall);
}
void ThirdCall() override
{
}
};
template <typename ParamType>
class EBusParamFixture
: public ScopedAllocatorSetupFixture
, public ::testing::WithParamInterface<ParamType>
{};
struct ThreadDispatchParams
{
size_t m_threadCount{};
size_t m_handlerCount{};
};
using ThreadDispatchParamFixture = EBusParamFixture<ThreadDispatchParams>;
INSTANTIATE_TEST_CASE_P(
ThreadDispatch,
ThreadDispatchParamFixture,
::testing::Values(
ThreadDispatchParams{ 1, 1 },
ThreadDispatchParams{ 2, 1 },
ThreadDispatchParams{ 1, 2 },
ThreadDispatchParams{ 2, 2 },
ThreadDispatchParams{ 16, 8 }
)
);
TEST_P(ThreadDispatchParamFixture, CustomDispatchLockGuard_InvokesPostDispatchFunction_AfterThreadHasFinishedDispatch)
{
ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0;
ThreadDispatchParams threadDispatchParams = GetParam();
AZStd::vector<AZStd::thread> testThreads;
AZStd::vector<ThreadDispatchTestHandler> testHandlers(threadDispatchParams.m_handlerCount);
for (ThreadDispatchTestHandler& testHandler : testHandlers)
{
testHandler.Connect();
}
static constexpr size_t DispatchThreadCalls = 3;
const size_t totalThreadDispatchCalls = threadDispatchParams.m_threadCount * DispatchThreadCalls;
auto DispatchThreadWorker = []()
{
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::FirstCall);
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall);
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall);
};
for (size_t threadIndex = 0; threadIndex < threadDispatchParams.m_threadCount; ++threadIndex)
{
testThreads.emplace_back(DispatchThreadWorker);
}
for (AZStd::thread& thread : testThreads)
{
thread.join();
}
for (ThreadDispatchTestHandler& testHandler : testHandlers)
{
testHandler.Disconnect();
}
EXPECT_EQ(totalThreadDispatchCalls, ThreadDispatchTestBusTraits::s_threadPostDispatchCalls);
ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0;
}
} // namespace UnitTest
#if defined(HAVE_BENCHMARK)
@@ -4370,7 +4555,7 @@ namespace Benchmark
Bus::ExecuteQueuedEvents();
}
s_benchmarkEBusEnv<Bus>.Disconnect(state);
}
BUS_BENCHMARK_REGISTER_ALL(BM_EBus_ExecuteBroadcast);
@@ -0,0 +1,196 @@
/*
* 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/Settings/SettingsRegistryImpl.h>
#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace SettingsRegistryVisitorUtilsTests
{
struct VisitCallbackParams
{
AZStd::string_view m_inputJsonDocument;
using VisitFieldFunction = bool(*)(AZ::SettingsRegistryInterface&,
const AZ::SettingsRegistryVisitorUtils::VisitorCallback&,
AZStd::string_view);
static inline constexpr size_t MaxFieldCount = 10;
using ObjectFields = AZStd::fixed_vector<AZStd::pair<AZStd::string_view, AZStd::string_view>, MaxFieldCount>;
using ArrayFields = AZStd::fixed_vector<AZStd::string_view, MaxFieldCount>;
ObjectFields m_objectFields;
ArrayFields m_arrayFields;
};
template <typename VisitorParams>
class SettingsRegistryVisitorUtilsParamFixture
: public UnitTest::ScopedAllocatorSetupFixture
, public ::testing::WithParamInterface<VisitorParams>
{
public:
void SetUp() override
{
m_registry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
}
void TearDown() override
{
m_registry.reset();
}
AZStd::unique_ptr<AZ::SettingsRegistryImpl> m_registry;
};
using SettingsRegistryVisitCallbackFixture = SettingsRegistryVisitorUtilsParamFixture<VisitCallbackParams>;
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitFieldsOfArrayType_ReturnsFields)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testArrayFields.emplace_back(AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Array");
const AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> expectedFields{
visitParams.m_arrayFields.begin(), visitParams.m_arrayFields.end() };
EXPECT_THAT(testArrayFields, ::testing::ContainerEq(expectedFields));
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitFieldsOfObjectType_ReturnsFields)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Object");
const AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> expectedFields{
visitParams.m_objectFields.begin(), visitParams.m_objectFields.end() };
EXPECT_THAT(testObjectFields, ::testing::ContainerEq(expectedFields));
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitArrayOfArrayType_ReturnsFields)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testArrayFields.emplace_back(AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Array");
const AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> expectedArrayFields{
visitParams.m_arrayFields.begin(), visitParams.m_arrayFields.end() };
EXPECT_THAT(testArrayFields, ::testing::ContainerEq(expectedArrayFields));
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitArrayOfObjectType_ReturnsEmpty)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testArrayFields.emplace_back(AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Object");
EXPECT_TRUE(testArrayFields.empty());
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitObjectOfArrayType_ReturnsEmpty)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Array");
EXPECT_TRUE(testObjectFields.empty());
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitObjectOfObjectType_ReturnsFields)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Object");
const AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> expectedObjectFields{
visitParams.m_objectFields.begin(), visitParams.m_objectFields.end() };
EXPECT_THAT(testObjectFields, ::testing::ContainerEq(expectedObjectFields));
}
INSTANTIATE_TEST_CASE_P(
VisitField,
SettingsRegistryVisitCallbackFixture,
::testing::Values(
VisitCallbackParams
{
R"({)" "\n"
R"( "Test":)" "\n"
R"( {)" "\n"
R"( "Array": [ "Hello", "World" ],)" "\n"
R"( "Object": { "Foo": "Hello", "Bar": "World"})" "\n"
R"( })" "\n"
R"(})" "\n",
VisitCallbackParams::ObjectFields{{"Foo", "Hello"}, {"Bar", "World"}},
VisitCallbackParams::ArrayFields{"Hello", "World"}
}
)
);
}
@@ -75,11 +75,12 @@ set(FILES
Name/NameJsonSerializerTests.cpp
Name/NameTests.cpp
RTTI/TypeSafeIntegralTests.cpp
SettingsRegistryTests.cpp
SettingsRegistryMergeUtilsTests.cpp
Settings/CommandLineTests.cpp
Settings/SettingsRegistryTests.cpp
Settings/SettingsRegistryConsoleUtilsTests.cpp
Settings/SettingsRegistryMergeUtilsTests.cpp
Settings/SettingsRegistryScriptUtilsTests.cpp
Settings/SettingsRegistryVisitorUtilsTests.cpp
Streamer/BlockCacheTests.cpp
Streamer/DedicatedCacheTests.cpp
Streamer/FullDecompressorTests.cpp
@@ -6,10 +6,11 @@
*
*/
#include <AzQtComponents/AzQtComponents_Traits_Platform.h>
#include <AzQtComponents/Components/Widgets/FileDialog.h>
#include <QMessageBox>
#include <QRegExp>
#include <QRegularExpression>
namespace AzQtComponents
{
@@ -24,7 +25,12 @@ namespace AzQtComponents
// Trigger Qt's save filename dialog
// If filePath isn't empty, it means we are prompting again because the filename was invalid,
// so pass it instead of the directory so the filename is pre-filled in for the user
filePath = QFileDialog::getSaveFileName(parent, caption, (filePath.isEmpty()) ? dir : filePath, filter, selectedFilter, options);
QString localSelectedFilter;
filePath = QFileDialog::getSaveFileName(parent, caption, (filePath.isEmpty()) ? dir : filePath, filter, &localSelectedFilter, options);
if (selectedFilter)
{
*selectedFilter = localSelectedFilter;
}
if (!filePath.isEmpty())
{
@@ -32,15 +38,39 @@ namespace AzQtComponents
QString fileName = fileInfo.fileName();
// Check if the filename has any invalid characters
QRegExp validFileNameRegex("^[a-zA-Z0-9_\\-./]*$");
shouldPromptAgain = !validFileNameRegex.exactMatch(fileName);
QRegularExpression validFileNameRegex("^[a-zA-Z0-9_\\-./]*$");
QRegularExpressionMatch validFileNameMatch = validFileNameRegex.match(fileName);
// If the filename had invalid characters, then show a warning message and then we will re-prompt the save filename dialog
if (shouldPromptAgain)
if (!validFileNameMatch.hasMatch())
{
QMessageBox::warning(parent, QObject::tr("Invalid filename"),
QObject::tr("O3DE assets are restricted to alphanumeric characters, hyphens (-), underscores (_), and dots (.)\n\n%1").arg(fileName));
shouldPromptAgain = true;
continue;
}
else
{
shouldPromptAgain = false;
}
#if AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION
// If a filter was selected, then make sure that the resulting filename ends with that extension. On systems that use the default QFileDialog,
// the extension is not guaranteed to be set in the resulting filename
if (FileDialog::ApplyMissingExtension(localSelectedFilter, filePath))
{
// If an extension had to be applied, then the file dialog did not handle the case of overwriting existing files.
// We need to check that condition before we proceed
QFileInfo updatedFilePath(filePath);
if (updatedFilePath.exists())
{
QMessageBox::StandardButton overwriteSelection = QMessageBox::question(parent,
QObject::tr("File exists"),
QObject::tr("%1 exists. Do you want to overwrite the existing file?").arg(updatedFilePath.fileName()));
shouldPromptAgain = (overwriteSelection == QMessageBox::No);
}
}
#endif // AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION
}
else
{
@@ -51,4 +81,56 @@ namespace AzQtComponents
return filePath;
}
bool FileDialog::ApplyMissingExtension(const QString& selectedFilter, QString& filePath)
{
if (selectedFilter.isEmpty())
{
return false;
}
// According to the QT documentation for QFileDialog, the selected filter will come in the form
// <Filter Name> (<filter pattern1> <filter pattern2> .. <filter patternN> )
//
// For example:
// "Images (*.gif *.png *.jpg)"
//
// Extract the contents of the <filter pattern>(s) inside the parenthesis and split them based on a whitespace or comma
const QRegularExpression filterContent(".*\\((?<filters>[^\\)]+)\\)");
QRegularExpressionMatch filterContentMatch = filterContent.match(selectedFilter);
if (!filterContentMatch.hasMatch())
{
return false;
}
QString filterExtensionsString = filterContentMatch.captured("filters");
QStringList filterExtensionsFull = filterExtensionsString.split(" ", Qt::SkipEmptyParts);
if (filterExtensionsFull.length() <= 0)
{
return false;
}
// If there are multiple suffixes in the selected filter, then default to the first one if a suffix needs to be appended
QString defaultSuffix = filterExtensionsFull[0].mid(1);
// Iterate through the filter patterns to see if the current filename matches
QFileInfo fileInfo(filePath);
bool extensionNeeded = true;
for (const QString& filterExtensionFull : filterExtensionsFull)
{
QString wildcardExpression = QRegularExpression::wildcardToRegularExpression(filterExtensionFull);
QRegularExpression filterPattern(wildcardExpression, AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY);
QRegularExpressionMatch filterPatternMatch = filterPattern.match(fileInfo.fileName());
if (filterPatternMatch.hasMatch())
{
// The filename matches one of the filter patterns already, the extension does not need to be added to the filename
extensionNeeded = false;
}
}
if (extensionNeeded)
{
// If the current (if any) suffix does not match, automatically add the default suffix for the selected filter
filePath.append(defaultSuffix);
}
return extensionNeeded;
}
} // namespace AzQtComponents
@@ -24,6 +24,12 @@ namespace AzQtComponents
static QString GetSaveFileName(QWidget* parent = nullptr, const QString& caption = QString(),
const QString& dir = QString(), const QString& filter = QString(),
QString* selectedFilter = nullptr, QFileDialog::Options options = QFileDialog::Options());
//! Helper method that parses a selected filter from Qt's QFileDialog::getSaveFileName and applies the
//! selected filter's extension to the filePath if it doesnt already have the extension. This is needed
//! on platforms that do not have a default file dialog (These platforms uses Qt's custom file dialog which will
//! not apply the filter's extension automatically on user entered filenames)
static bool ApplyMissingExtension(const QString& selectedFilter, QString& filePath);
};
} // namespace AzQtComponents
@@ -12,4 +12,6 @@ set(FILES
../../Utilities/QtWindowUtilities_linux.cpp
../../Utilities/ScreenGrabber_linux.cpp
../../../Platform/Linux/AzQtComponents/Components/StyledDockWidget_Linux.cpp
../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Linux.h
../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Platform.h
)
@@ -12,4 +12,6 @@ set(FILES
../../Utilities/QtWindowUtilities_mac.mm
../../Utilities/ScreenGrabber_mac.mm
../../../Platform/Mac/AzQtComponents/Components/StyledDockWidget_Mac.cpp
../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Mac.h
../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Platform.h
)
@@ -17,4 +17,6 @@ set(FILES
../../Components/TitleBarOverdrawScreenHandler_win.h
../../Components/TitleBarOverdrawScreenHandler_win.cpp
../../../Platform/Windows/AzQtComponents/Components/StyledDockWidget_Windows.cpp
../../../Platform/Windows/AzQtComponents/AzQtComponents_Traits_Windows.h
../../../Platform/Windows/AzQtComponents/AzQtComponents_Traits_Platform.h
)
@@ -0,0 +1,65 @@
/*
* 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 <AzTest/AzTest.h>
#include <AzQtComponents/Components/Widgets/FileDialog.h>
#include <QString>
TEST(AzQtComponents, ApplyMissingExtension_UpdateMissingExtension_Success)
{
const QString textFiler{"Text Files (*.txt)"};
QString testPath{"testFile"};
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
EXPECT_TRUE(result);
EXPECT_STRCASEEQ("testFile.txt", testPath.toUtf8().constData());
}
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateExistingExtension_Success)
{
const QString textFiler{"Text Files (*.txt)"};
QString testPath{"testFile.txt"};
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
EXPECT_FALSE(result);
EXPECT_STRCASEEQ("testFile.txt", testPath.toUtf8().constData());
}
TEST(AzQtComponents, ApplyMissingExtension_UpdateMissingExtensionMultipleExtensionFilter_Success)
{
const QString textFiler{"Image Files (*.jpg *.bmp *.png)"};
QString testPath{"testFile"};
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
EXPECT_TRUE(result);
EXPECT_STRCASEEQ("testFile.jpg", testPath.toUtf8().constData());
}
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionMultipleExtensionFilter_Success)
{
const QString textFiler{"Image Files (*.jpg *.bmp *.png)"};
QString testPath{"testFile.png"};
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
EXPECT_FALSE(result);
EXPECT_STRCASEEQ("testFile.png", testPath.toUtf8().constData());
}
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionEmptyFilter_Success)
{
const QString textFiler{""};
QString testPath{"testFile"};
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
EXPECT_FALSE(result);
EXPECT_STRCASEEQ("testFile", testPath.toUtf8().constData());
}
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionInvalidFilter_Success)
{
const QString textFiler{"Bad Filter!!"};
QString testPath{"testFile"};
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
EXPECT_FALSE(result);
EXPECT_STRCASEEQ("testFile", testPath.toUtf8().constData());
}
@@ -9,6 +9,7 @@
set(FILES
Tests/AzQtComponentTests.cpp
Tests/ColorControllerTests.cpp
Tests/FileDialogTests.cpp
Tests/FloatToStringConversionTests.cpp
Tests/HexParsingTests.cpp
Tests/StyleSheetCacheTests.cpp
@@ -10,6 +10,8 @@ if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
ly_add_target(
NAME AzQtComponents SHARED
NAMESPACE AZ
@@ -26,6 +28,7 @@ ly_add_target(
AzQtComponents
PUBLIC
.
${pal_dir}
COMPILE_DEFINITIONS
PRIVATE
AZ_QT_COMPONENTS_EXPORT_SYMBOLS
@@ -53,6 +56,7 @@ ly_add_target(
.
AzQtComponents
AzQtComponents/Gallery
${pal_dir}
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Svg
@@ -86,6 +90,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
PRIVATE
Tests
AzQtComponents
${pal_dir}
BUILD_DEPENDENCIES
PRIVATE
AZ::AzQtComponents
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 1
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::NoPatternOption
@@ -0,0 +1,10 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzQtComponents/AzQtComponents_Traits_Linux.h>
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 0
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::NoPatternOption
@@ -0,0 +1,10 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzQtComponents/AzQtComponents_Traits_Mac.h>
@@ -0,0 +1,10 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzQtComponents/AzQtComponents_Traits_Windows.h>
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 0
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::CaseInsensitiveOption
@@ -410,7 +410,7 @@ namespace AzToolsFramework
filter.append(ext);
if (i < n - 1)
{
filter.append(", ");
filter.append(" ");
}
}
filter.append(")");
@@ -224,6 +224,7 @@ namespace AzToolsFramework
return false;
}
AZ::Data::SerializedAssetTracker* assetTracker = settings.m_metadata.Find<AZ::Data::SerializedAssetTracker>();
referencedAssets = AZStd::move(assetTracker->GetTrackedAssets());
@@ -245,6 +246,30 @@ namespace AzToolsFramework
entityIdMapper.SetEntityIdGenerationApproach(InstanceEntityIdMapper::EntityIdGenerationApproach::Random);
}
// some assets may come in from the JSON serialzier with no AssetID, but have an asset hint
// this attempts to fix up the assets using the assetHint field
auto fixUpInvalidAssets = [](AZ::Data::Asset<AZ::Data::AssetData>& asset)
{
if (!asset.GetId().IsValid() && !asset.GetHint().empty())
{
AZ::Data::AssetId assetId;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
assetId,
&AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
asset.GetHint().c_str(),
AZ::Data::s_invalidAssetType,
false);
if (assetId.IsValid())
{
asset.Create(assetId, true);
}
}
};
auto tracker = AZ::Data::SerializedAssetTracker{};
tracker.SetAssetFixUp(fixUpInvalidAssets);
AZ::JsonDeserializerSettings settings;
// The InstanceEntityIdMapper is registered twice because it's used in several places during deserialization where one is
// specific for the InstanceEntityIdMapper and once for the generic JsonEntityIdMapper. Because the Json Serializer's meta
@@ -252,16 +277,17 @@ namespace AzToolsFramework
settings.m_metadata.Add(static_cast<AZ::JsonEntityIdSerializer::JsonEntityIdMapper*>(&entityIdMapper));
settings.m_metadata.Add(&entityIdMapper);
settings.m_metadata.Create<InstanceEntityScrubber>(newlyAddedEntities);
settings.m_metadata.Add(tracker);
AZStd::string scratchBuffer;
auto issueReportingCallback = [&scratchBuffer](
AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result,
AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode
AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result,
AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode
{
return Internal::JsonIssueReporter(scratchBuffer, message, result, path);
};
settings.m_reporting = AZStd::move(issueReportingCallback);
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(instance, prefabDom, settings);
AZ::Data::AssetManager::Instance().ResumeAssetRelease();
@@ -1025,7 +1025,7 @@ namespace AzToolsFramework
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/LuaScript.svg")
->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid())
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Script.png")
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/lua-script/")
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/scripting/lua-script/")
->DataElement("AssetRef", &ScriptEditorComponent::m_scriptAsset, "Script", "Which script to use")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &ScriptEditorComponent::ScriptHasChanged)
->Attribute("BrowseIcon", ":/stylesheet/img/UI20/browse-edit-select-files.svg")
@@ -22,22 +22,33 @@
#include <LyShine/ILyShine.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Script/ScriptSystemBus.h>
namespace LegacyLevelSystem
{
constexpr AZStd::string_view DeferredLoadLevelKey = "/O3DE/Runtime/SpawnableLevelSystem/DeferredLoadLevel";
//------------------------------------------------------------------------
static void LoadLevel(const AZ::ConsoleCommandContainer& arguments)
{
AZ_Error("SpawnableLevelSystem", !arguments.empty(), "LoadLevel requires a level file name to be provided.");
AZ_Error("SpawnableLevelSystem", arguments.size() == 1, "LoadLevel requires a single level file name to be provided.");
if (!arguments.empty() && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
if (!arguments.empty() && gEnv && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
{
gEnv->pSystem->GetILevelSystem()->LoadLevel(arguments[0].data());
}
else if (!arguments.empty())
{
// The SpawnableLevelSystem isn't available yet.
// Defer the level load until later by storing it in the SettingsRegistry
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
settingsRegistry->Set(DeferredLoadLevelKey, arguments.front());
}
}
}
//------------------------------------------------------------------------
@@ -45,7 +56,7 @@ namespace LegacyLevelSystem
{
AZ_Warning("SpawnableLevelSystem", !arguments.empty(), "UnloadLevel doesn't use any arguments.");
if (gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
if (gEnv && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
{
gEnv->pSystem->GetILevelSystem()->UnloadLevel();
}
@@ -73,6 +84,24 @@ namespace LegacyLevelSystem
}
AzFramework::RootSpawnableNotificationBus::Handler::BusConnect();
// If there were LoadLevel command invocations before the creation of the level system
// then those invocations were queued.
// load the last level in the queue, since only one level can be loaded at a time
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
if (AZ::SettingsRegistryInterface::FixedValueString deferredLevelName;
settingsRegistry->Get(deferredLevelName, DeferredLoadLevelKey) && !deferredLevelName.empty())
{
// since this is the constructor any derived classes vtables aren't setup yet
// call this class LoadLevel function
AZ_TracePrintf("SpawnableLevelSystem", "The Level System is now available."
" Loading level %s which could not be loaded earlier\n", deferredLevelName.c_str());
SpawnableLevelSystem::LoadLevel(deferredLevelName.c_str());
// Delete the key with the deferred level name
settingsRegistry->Remove(DeferredLoadLevelKey);
}
}
}
//------------------------------------------------------------------------
@@ -173,7 +202,7 @@ namespace LegacyLevelSystem
}
// Make sure a spawnable level exists that matches levelname
AZStd::string validLevelName = "";
AZStd::string validLevelName;
AZ::Data::AssetId rootSpawnableAssetId;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
rootSpawnableAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, levelName, nullptr, false);
@@ -395,7 +395,7 @@ namespace LUAEditor
void LUAEditorMainWindow::OnLuaDocumentation()
{
QDesktopServices::openUrl(QUrl("http://docs.aws.amazon.com/lumberyard/latest/developerguide/lua-scripting-intro.html"));
QDesktopServices::openUrl(QUrl("https://o3de.org/docs/user-guide/scripting/lua/"));
}
void LUAEditorMainWindow::OnMenuCloseCurrentWindow()
@@ -24,7 +24,7 @@ namespace AWSCore
/**
* Add required SystemComponents to the SystemEntity.
*/
virtual AZ::ComponentTypeList GetRequiredSystemComponents() const override;
AZ::ComponentTypeList GetRequiredSystemComponents() const override;
};
}
@@ -42,7 +42,7 @@ namespace AWSCore
AWSCoreConfiguration();
~AWSCoreConfiguration() = default;
~AWSCoreConfiguration() override = default;
void ActivateConfig();
void DeactivateConfig();
@@ -23,7 +23,7 @@ namespace AWSCore
{
public:
AWSDefaultCredentialHandler();
~AWSDefaultCredentialHandler() = default;
~AWSDefaultCredentialHandler() override = default;
//! Activate handler and its credentials provider, make sure activation
//! invoked after AWSNativeSDK init to avoid memory leak
@@ -15,13 +15,12 @@
namespace AWSCore
{
//! Defines AWSCoreAttributionConsent QT dialog as QT message box.
class AWSCoreAttributionConsentDialog :
public QMessageBox
class AWSCoreAttributionConsentDialog
: public QMessageBox
{
public:
AZ_CLASS_ALLOCATOR(AWSCoreAttributionConsentDialog, AZ::SystemAllocator, 0);
AWSCoreAttributionConsentDialog();
virtual ~AWSCoreAttributionConsentDialog() = default;
~AWSCoreAttributionConsentDialog() override = default;
};
} // namespace AWSCore
@@ -18,4 +18,4 @@ namespace AWSCore
static constexpr char AwsAttributionAttributeKeyActiveAWSGems[] = "aws_gems";
static constexpr char AwsAttributionAttributeKeyTimestamp[] = "timestamp";
} // namespace AWSCOre
} // namespace AWSCore
@@ -34,7 +34,7 @@ namespace AWSCore
"Failed to launch Resource Mapping Tool, please check <a href=\"file:///%s\">logs</a> for details.";
AWSCoreEditorMenu(const QString& text);
~AWSCoreEditorMenu();
~AWSCoreEditorMenu() override;
private:
QAction* AddExternalLinkAction(const AZStd::string& name, const AZStd::string& url, const AZStd::string& icon = "");
@@ -71,7 +71,7 @@ namespace AWSCore
};
AWSResourceMappingManager();
~AWSResourceMappingManager() = default;
~AWSResourceMappingManager() override = default;
void ActivateManager();
void DeactivateManager();
@@ -64,9 +64,6 @@ namespace AWSCore
/// Initialize an AwsApiClientJobConfig object.
///
/// \param DefaultConfigType - the type of the config object from which
/// default values will be taken.
///
/// \param defaultConfig - the config object that provides values when
/// no override has been set in this object. The default is nullptr, which
/// will cause a default value to be used.
@@ -83,10 +80,10 @@ namespace AWSCore
}
}
virtual ~AwsApiClientJobConfig() = default;
~AwsApiClientJobConfig() override = default;
/// Gets a client initialized used currently applied settings. If
/// any settings change after first use, code must call
/// any settings change after first use, code must call
/// ApplySettings before those changes will take effect.
std::shared_ptr<ClientType> GetClient() override
{
@@ -112,7 +109,7 @@ namespace AWSCore
}
else
{
// If no explict credenitals are provided then AWS C++ SDK will perform standard search
// If no explicit credentials are provided then AWS C++ SDK will perform standard search
return std::make_shared<ClientType>(Aws::Auth::AWSCredentials(), GetClientConfiguration());
}
}
@@ -14,14 +14,12 @@
namespace AWSCore
{
/// Base class for all AWS jobs. Primarily exists so that
/// Base class for all AWS jobs. Primarily exists so that
/// AwsApiJob::s_config can be used for settings that apply to
/// all AWS jobs.
class AwsApiJob
: public AZ::Job
{
public:
// To use a different allocator, extend this class and use this macro.
AZ_CLASS_ALLOCATOR(AwsApiJob, AZ::SystemAllocator, 0);
@@ -33,11 +31,10 @@ namespace AWSCore
protected:
AwsApiJob(bool isAutoDelete, IConfig* config = GetDefaultConfig());
virtual ~AwsApiJob();
~AwsApiJob() override = default;
/// Used for error messages.
static const char* COMPONENT_DISPLAY_NAME;
};
} // namespace AWSCore
@@ -96,9 +96,6 @@ namespace AWSCore
/// Initialize an AwsApiClientJobConfig object.
///
/// \param DefaultConfigType - the type of the config object from which
/// default values will be taken.
///
/// \param defaultConfig - the config object that provides values when
/// no override has been set in this object. The default is nullptr, which
/// will cause a default value to be used.
@@ -146,7 +143,7 @@ namespace AWSCore
#endif
Override<Aws::String> caFile;
/// Applys settings changes made after first use.
/// Applies settings changes made after first use.
virtual void ApplySettings();
//////////////////////////////////////////////////////////////////////////
@@ -217,7 +214,7 @@ namespace AWSCore
: protected AWSCoreNotificationsBus::Handler
{
public:
~AwsApiJobConfigHolder()
~AwsApiJobConfigHolder() override
{
AWSCoreNotificationsBus::Handler::BusDisconnect();
}
@@ -145,10 +145,10 @@ namespace AWSCore
AWS_API_REQUEST_TRAITS_TEMPLATE_DEFINITION_HELPER
typename AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER::AsyncFunctionType AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER::AsyncFunction = _AsyncFunction;
/// Macro that simplifies the declaration of an AwsRequstJob that has a result.
/// Macro that simplifies the declaration of an AwsRequestJob that has a result.
#define AWS_API_REQUEST_JOB(SERVICE, REQUEST) AWSCore::AwsApiRequestJob<AWS_API_REQUEST_TRAITS(SERVICE, REQUEST)>
/// Macro that simplifies the declaration of an AwsRequstJob that has no result.
/// Macro that simplifies the declaration of an AwsRequestJob that has no result.
#define AWS_API_REQUEST_JOB_NO_RESULT(SERVICE, REQUEST) AWSCore::AwsApiRequestJob<AWS_API_REQUEST_TRAITS_NO_RESULT(SERVICE, REQUEST)>
/// An Az::Job that that executes a specific AWS request.
@@ -257,7 +257,7 @@ namespace AWSCore
/// of request data until your running on the job's worker thread,
/// instead of setting the request data before calling Start.
///
/// \param true if the request should be made.
/// \return true if the request should be made.
virtual bool PrepareRequest()
{
return true;
@@ -39,7 +39,7 @@ namespace AWSCore
: public AZ::ComponentBus
{
public:
virtual ~HttpClientComponentNotifications() {}
~HttpClientComponentNotifications() override = default;
virtual void OnHttpRequestSuccess(int responseCode, AZStd::string responseBody) {}
virtual void OnHttpRequestFailure(int responseCode) {}
};
@@ -55,7 +55,7 @@ namespace AWSCore
{
public:
AZ_COMPONENT(HttpClientComponent, "{23ECDBDF-129A-4670-B9B4-1E0B541ACD61}");
virtual ~HttpClientComponent() = default;
~HttpClientComponent() override = default;
void Init() override;
void Activate() override;
@@ -178,7 +178,7 @@ namespace AWSCore
};
/// Override to process the response to the HTTP request before callbacks are fired.
/// WARNING: This gets called on the job's thread, so observe thread safety precations.
/// WARNING: This gets called on the job's thread, so observe thread safety precautions.
virtual void ProcessResponse(const std::shared_ptr<Aws::Http::HttpResponse>& response)
{
AZ_UNUSED(response);
@@ -29,24 +29,24 @@ namespace AWSCore
Ch Peek() const
{
int c = m_is.peek();
return c == std::char_traits<char>::eof() ? '\0' : (Ch)c;
return c == std::char_traits<char>::eof() ? '\0' : static_cast<Ch>(c);
}
Ch Take()
{
int c = m_is.get();
return c == std::char_traits<char>::eof() ? '\0' : (Ch)c;
return c == std::char_traits<char>::eof() ? '\0' : static_cast<Ch>(c);
}
size_t Tell() const
{
return (size_t)m_is.tellg();
return static_cast<size_t>(m_is.tellg());
}
Ch* PutBegin()
{
AZ_Assert(false, "Not Implemented");
return 0;
return nullptr;
}
void Put(Ch)
@@ -161,7 +161,7 @@ namespace AWSCore
}
/// Write JSON format content directly to the writer's output stream.
/// This can be used to efficently output static content.
/// This can be used to efficiently output static content.
bool WriteJson(const Ch* json)
{
if (json)
@@ -182,7 +182,7 @@ namespace AWSCore
}
/// Write an object. The object can implement a WriteJson function
/// or you can provide an GobalWriteJson template function
/// or you can provide an GlobalWriteJson template function
/// specialization.
template<class ObjectType>
bool Object(const ObjectType& obj)
@@ -36,7 +36,7 @@ namespace AWSCore
class RequestBuilder
{
public:
RequestBuilder() = default;
RequestBuilder();
/// Converts the provided object to JSON and sends it as the
/// body of the request. The object can implement the following
@@ -20,7 +20,7 @@ namespace AWSCore
{
public:
virtual const AZStd::string GetServiceUrl() = 0;
virtual AZStd::string GetServiceUrl() = 0;
};
/// Encapsulates what code needs to know about a service in order to
@@ -81,9 +81,6 @@ namespace AWSCore
/// Initialize an ServiceClientJobConfig object.
///
/// \param DefaultConfigType - the type of the config object from which
/// default values will be taken.
///
/// \param defaultConfig - the config object that provides values when
/// no override has been set in this object. The default is nullptr, which
/// will cause a default value to be used.
@@ -102,7 +99,7 @@ namespace AWSCore
/// This implementation assumes the caller will cache this value as
/// needed. See it's use in ServiceRequestJobConfig.
const AZStd::string GetServiceUrl() override
AZStd::string GetServiceUrl() override
{
if (endpointOverride.has_value())
{
@@ -119,7 +119,7 @@ namespace AWSCore
Error error;
/// Determines if the AWS credentials, as supplied by the credentialsProvider from
/// the ServiceReqestJobConfig object (which defaults to the user's credentials),
/// the ServiceRequestJobConfig object (which defaults to the user's credentials),
/// are used to sign the request. The default is true. Override this and return false
/// if calling a public API and want to avoid the overhead of signing requests.
bool UseAWSCredentials() {
@@ -565,13 +565,11 @@ namespace AWSCore
}
AZStd::string requestContent;
AZStd::string responseContent;
std::istreambuf_iterator<AZStd::string::value_type> eos;
std::shared_ptr<Aws::IOStream> requestStream = response->GetOriginatingRequest().GetContentBody();
if (requestStream)
{
std::istreambuf_iterator<AZStd::string::value_type> eos;
requestStream->clear();
requestStream->seekg(0);
requestContent = AZStd::string{ std::istreambuf_iterator<AZStd::string::value_type>(*requestStream.get()),eos };
@@ -584,7 +582,7 @@ namespace AWSCore
Aws::IOStream& responseStream = response->GetResponseBody();
responseStream.clear();
responseStream.seekg(0);
responseContent = AZStd::string{ std::istreambuf_iterator<AZStd::string::value_type>(responseStream),responseEos };
AZStd::string responseContent = AZStd::string{ std::istreambuf_iterator<AZStd::string::value_type>(responseStream), responseEos };
responseContent = EscapePercentCharsInString(responseContent);
responseStream.seekg(0);
@@ -44,9 +44,6 @@ namespace AWSCore
/// Initialize an ServiceRequestJobConfig object.
///
/// \param DefaultConfigType - the type of the config object from which
/// default values will be taken.
///
/// \param defaultConfig - the config object that provides values when
/// no override has been set in this object. The default is nullptr, which
/// will cause a default value to be used.
@@ -79,7 +79,7 @@ namespace AWSCore
QMenuBar* menuBar = mainWindow->menuBar();
QList<QAction*> actionList = menuBar->actions();
QAction* insertPivot = nullptr;
for (QList<QAction*>::iterator itr = actionList.begin(); itr != actionList.end(); itr++)
for (QList<QAction*>::iterator itr = actionList.begin(); itr != actionList.end(); ++itr)
{
if (QString::compare((*itr)->text(), EDITOR_HELP_MENU_TEXT) == 0)
{
@@ -88,7 +88,7 @@ namespace AWSCore
}
}
auto menu = m_awsCoreEditorManager->GetAWSCoreEditorMenu();
const auto menu = m_awsCoreEditorManager->GetAWSCoreEditorMenu();
if (insertPivot)
{
menuBar->insertMenu(insertPivot, menu);
@@ -35,8 +35,7 @@ namespace AWSCore
this->setDefaultButton(QMessageBox::Save);
this->button(QMessageBox::Cancel)->hide();
this->setIcon(QMessageBox::Information);
QGridLayout* layout = (QGridLayout*)this->layout();
if (layout)
if (QGridLayout* layout = static_cast<QGridLayout*>(this->layout()))
{
layout->setVerticalSpacing(20);
layout->setHorizontalSpacing(10);
@@ -68,19 +68,19 @@ namespace AWSCore
AZ_Assert(fileIO, "File IO is not initialized.");
// Resolve path to editor_aws_preferences.setreg
AZStd::string editorAWSPreferencesFilePath =
const AZStd::string editorAWSPreferencesFilePath =
AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName);
AZStd::array<char, AZ::IO::MaxPathLength> resolvedPathAWSPreference{};
if (!fileIO->ResolvePath(editorAWSPreferencesFilePath.c_str(), resolvedPathAWSPreference.data(), resolvedPathAWSPreference.size()))
AZ::IO::FixedMaxPath resolvedPathAWSPreference;
if (!fileIO->ResolvePath(resolvedPathAWSPreference, AZ::IO::PathView(editorAWSPreferencesFilePath)))
{
AZ_Warning("AWSAttributionManager", false, "Error resolving path %s", resolvedPathAWSPreference.data());
AZ_Warning("AWSAttributionManager", false, "Error resolving path %s", resolvedPathAWSPreference.c_str());
return;
}
if (fileIO->Exists(resolvedPathAWSPreference.data()))
if (fileIO->Exists(resolvedPathAWSPreference.c_str()))
{
m_settingsRegistry->MergeSettingsFile(
resolvedPathAWSPreference.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, "");
resolvedPathAWSPreference.String(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, "");
}
}
@@ -136,8 +136,8 @@ namespace AWSCore
return true;
}
AZStd::chrono::seconds lastSendTimeStamp = AZStd::chrono::seconds(lastSendTimeStampSeconds);
AZStd::chrono::seconds secondsSinceLastSend =
const AZStd::chrono::seconds lastSendTimeStamp = AZStd::chrono::seconds(lastSendTimeStampSeconds);
const AZStd::chrono::seconds secondsSinceLastSend =
AZStd::chrono::duration_cast<AZStd::chrono::seconds>(AZStd::chrono::system_clock::now().time_since_epoch()) - lastSendTimeStamp;
if (static_cast<AZ::u64>(secondsSinceLastSend.count()) >= delayInSeconds)
{
@@ -154,7 +154,7 @@ namespace AWSCore
if (credentialResult.result)
{
std::shared_ptr<Aws::Auth::AWSCredentialsProvider> provider = credentialResult.result;
auto creds = provider->GetAWSCredentials();
const auto creds = provider->GetAWSCredentials();
if (!creds.IsEmpty())
{
return true;
@@ -200,9 +200,13 @@ namespace AWSCore
AZ_Assert(fileIO, "File IO is not initialized.");
// Resolve path to editor_aws_preferences.setreg
AZStd::string editorPreferencesFilePath = AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName);
AZStd::array<char, AZ::IO::MaxPathLength> resolvedPath {};
fileIO->ResolvePath(editorPreferencesFilePath.c_str(), resolvedPath.data(), resolvedPath.size());
const AZStd::string editorPreferencesFilePath = AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName);
AZ::IO::FixedMaxPath resolvedPathAWSPreference;
if (!fileIO->ResolvePath(resolvedPathAWSPreference, AZ::IO::PathView(editorPreferencesFilePath)))
{
AZ_Warning("AWSAttributionManager", false, "Error resolving path %s", editorPreferencesFilePath.c_str());
return;
}
AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
dumperSettings.m_prettifyOutput = true;
@@ -215,14 +219,14 @@ namespace AWSCore
{
AZ_Warning(
"AWSAttributionManager", false, R"(Unable to save changes to the Editor AWS Preferences registry file at "%s"\n)",
resolvedPath.data());
resolvedPathAWSPreference.c_str());
return;
}
bool saved {};
constexpr auto configurationMode =
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPath.data(), configurationMode))
if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPathAWSPreference.c_str(), configurationMode))
{
saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size();
}
@@ -54,7 +54,7 @@ namespace AWSCore
{
if (m_resourceMappingToolWatcher->IsProcessRunning())
{
m_resourceMappingToolWatcher->TerminateProcess(AZ::u32(-1));
m_resourceMappingToolWatcher->TerminateProcess(static_cast<AZ::u32>(-1));
}
m_resourceMappingToolWatcher.reset();
}
@@ -214,7 +214,7 @@ namespace AWSCore
QMenu* AWSCoreEditorMenu::SetAWSFeatureSubMenu(const AZStd::string& menuText)
{
auto actionList = this->actions();
for (QList<QAction*>::iterator itr = actionList.begin(); itr != actionList.end(); itr++)
for (QList<QAction*>::iterator itr = actionList.begin(); itr != actionList.end(); ++itr)
{
if (QString::compare((*itr)->text(), menuText.c_str()) == 0)
{
@@ -22,10 +22,6 @@ namespace AWSCore
{
}
AwsApiJob::~AwsApiJob()
{
}
AwsApiJob::Config* AwsApiJob::GetDefaultConfig()
{
static AwsApiJobConfigHolder<AwsApiJob::Config> s_configHolder{};
@@ -49,7 +49,7 @@ namespace AWSCore
{
m_fileFields.emplace_back(FileField{ std::move(fieldName), std::move(fileName) , AZStd::vector<char>{} });
m_fileFields.back().m_fileData.reserve(length);
m_fileFields.back().m_fileData.assign((const char*)bytes, (const char*)bytes + length);
m_fileFields.back().m_fileData.assign(static_cast<const char*>(bytes), static_cast<const char*>(bytes) + length);
}
void MultipartFormData::SetCustomBoundary(AZStd::string boundary)
@@ -10,6 +10,10 @@
namespace AWSCore
{
RequestBuilder::RequestBuilder()
: m_httpMethod(Aws::Http::HttpMethod::HTTP_GET)
{
}
bool RequestBuilder::SetPathParameterUnescaped(const char* key, const char* value)
{
@@ -26,7 +26,6 @@ namespace AWSCore
: m_status(Status::NotLoaded)
, m_defaultAccountId("")
, m_defaultRegion("")
, m_resourceMappings()
{
}
@@ -164,7 +163,7 @@ namespace AWSCore
m_defaultRegion = jsonDocument.FindMember(ResourceMappingRegionKeyName)->value.GetString();
auto resourceMappings = jsonDocument.FindMember(ResourceMappingResourcesKeyName)->value.GetObject();
for (auto mappingIter = resourceMappings.MemberBegin(); mappingIter != resourceMappings.MemberEnd(); mappingIter++)
for (auto mappingIter = resourceMappings.MemberBegin(); mappingIter != resourceMappings.MemberEnd(); ++mappingIter)
{
auto mappingValue = mappingIter->value.GetObject();
if (mappingValue.MemberCount() != 0)
@@ -71,7 +71,7 @@ namespace AWSCore
[](DynamoDBGetItemRequestJob* job) // OnSuccess handler
{
auto item = job->result.GetItem();
if (item.size() > 0)
if (!item.empty())
{
DynamoDBAttributeValueMap result;
for (const auto& itermPair : item)
@@ -40,7 +40,7 @@ public:
AWSCoreNotificationsBus::Handler::BusConnect();
}
~AWSCoreNotificationsBusMock()
~AWSCoreNotificationsBusMock() override
{
AWSCoreNotificationsBus::Handler::BusDisconnect();
}
@@ -18,7 +18,7 @@ class AWSCVarCredentialHandlerTest
{
public:
AWSCVarCredentialHandlerTest() = default;
virtual ~AWSCVarCredentialHandlerTest() = default;
~AWSCVarCredentialHandlerTest() override = default;
void SetUp() override
{
@@ -36,14 +36,14 @@ public:
m_credentialsProvider.reset();
}
int GetCredentialHandlerOrder() const
int GetCredentialHandlerOrder() const override
{
return 1;
}
std::shared_ptr<Aws::Auth::AWSCredentialsProvider> GetCredentialsProvider()
std::shared_ptr<Aws::Auth::AWSCredentialsProvider> GetCredentialsProvider() override
{
m_handlerCounter++;
++m_handlerCounter;
return m_credentialsProvider;
}
@@ -72,14 +72,14 @@ public:
m_credentialsProvider.reset();
}
int GetCredentialHandlerOrder() const
int GetCredentialHandlerOrder() const override
{
return 2;
}
std::shared_ptr<Aws::Auth::AWSCredentialsProvider> GetCredentialsProvider()
std::shared_ptr<Aws::Auth::AWSCredentialsProvider> GetCredentialsProvider() override
{
m_handlerCounter++;
++m_handlerCounter;
return m_credentialsProvider;
}
@@ -115,10 +115,10 @@ public:
TEST_F(AWSCredentialBusTest, GetCredentialsProvider_CallFromMultithread_GetExpectedCredentialsProviderAndNumberOfCalls)
{
int testThreadNumber = 10;
constexpr int testThreadNumber = 10;
AZStd::atomic<int> actualEbusCalls = 0;
AZStd::vector<AZStd::thread> testThreadPool;
for (int index = 0; index < testThreadNumber; index++)
for (int index = 0; index < testThreadNumber; ++index)
{
testThreadPool.emplace_back(AZStd::thread([&]() {
AWSCredentialResult result;
@@ -49,7 +49,7 @@ class AWSDefaultCredentialHandlerTest
{
public:
AWSDefaultCredentialHandlerTest() = default;
virtual ~AWSDefaultCredentialHandlerTest() = default;
~AWSDefaultCredentialHandlerTest() override = default;
void SetUp() override
{
@@ -23,6 +23,11 @@ class AWSApiClientJobConfigTest
, public AWSCredentialRequestBus::Handler
{
public:
AWSApiClientJobConfigTest()
: m_credentialHandlerCounter(0)
{
}
void SetUp() override
{
AWSNativeSDKInit::InitializationManager::InitAwsApi();
@@ -84,7 +84,7 @@ class ServiceClientJobConfigTest
void ReloadConfigFile(bool reloadConfigFileName = false) override
{
AZ_UNUSED(reloadConfigFileName);
};
}
};
TEST_F(ServiceClientJobConfigTest, GetServiceUrl_CreateServiceWithServiceNameOnly_GetExpectedFeatureServiceUrl)
@@ -217,7 +217,7 @@ TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_Confi
CreateTestConfigFile(TEST_VALID_RESOURCE_MAPPING_CONFIG_FILE);
m_resourceMappingManager->ActivateManager();
int testThreadNumber = 10;
constexpr int testThreadNumber = 10;
AZStd::atomic<int> actualEbusCalls = 0;
AZStd::vector<AZStd::thread> testThreadPool;
for (int index = 0; index < testThreadNumber; index++)
@@ -226,7 +226,7 @@ TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_Confi
AZStd::string actualAccountId;
AWSResourceMappingRequestBus::BroadcastResult(actualAccountId, &AWSResourceMappingRequests::GetDefaultAccountId);
EXPECT_FALSE(actualAccountId.empty());
actualEbusCalls++;
++actualEbusCalls;
}));
}
@@ -44,19 +44,19 @@ TEST_F(AWSResourceMappingUtilsTest, FormatRESTApiUrl_PassingInvalidRESTApiId_Ret
{
auto actualUrl = AWSResourceMappingUtils::FormatRESTApiUrl("", TEST_VALID_RESTAPI_REGION, TEST_VALID_RESTAPI_STAGE);
EXPECT_TRUE(actualUrl == "");
EXPECT_TRUE(actualUrl.empty());
}
TEST_F(AWSResourceMappingUtilsTest, FormatRESTApiUrl_PassingInvalidRESTApiRegion_ReturnEmptyResult)
{
auto actualUrl = AWSResourceMappingUtils::FormatRESTApiUrl(TEST_VALID_RESTAPI_ID, "", TEST_VALID_RESTAPI_STAGE);
EXPECT_TRUE(actualUrl == "");
EXPECT_TRUE(actualUrl.empty());
}
TEST_F(AWSResourceMappingUtilsTest, FormatRESTApiUrl_PassingInvalidRESTApiStage_ReturnEmptyResult)
{
auto actualUrl = AWSResourceMappingUtils::FormatRESTApiUrl(TEST_VALID_RESTAPI_ID, TEST_VALID_RESTAPI_REGION, "");
EXPECT_TRUE(actualUrl == "");
EXPECT_TRUE(actualUrl.empty());
}
@@ -23,7 +23,7 @@ public:
AWSScriptBehaviorDynamoDBNotificationBus::Handler::BusConnect();
}
~AWSScriptBehaviorDynamoDBNotificationBusHandlerMock()
~AWSScriptBehaviorDynamoDBNotificationBusHandlerMock() override
{
AWSScriptBehaviorDynamoDBNotificationBus::Handler::BusDisconnect();
}
@@ -22,7 +22,7 @@ public:
AWSScriptBehaviorLambdaNotificationBus::Handler::BusConnect();
}
~AWSScriptBehaviorLambdaNotificationBusHandlerMock()
~AWSScriptBehaviorLambdaNotificationBusHandlerMock() override
{
AWSScriptBehaviorLambdaNotificationBus::Handler::BusDisconnect();
}
@@ -24,7 +24,7 @@ public:
AWSScriptBehaviorS3NotificationBus::Handler::BusConnect();
}
~AWSScriptBehaviorS3NotificationBusHandlerMock()
~AWSScriptBehaviorS3NotificationBusHandlerMock() override
{
AWSScriptBehaviorS3NotificationBus::Handler::BusDisconnect();
}
@@ -107,8 +107,8 @@ class AWSCoreFixture
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
AWSCoreFixture() {}
virtual ~AWSCoreFixture() = default;
AWSCoreFixture() = default;
~AWSCoreFixture() override = default;
void SetUp() override
{
+10
View File
@@ -64,6 +64,16 @@ To add additional dependencies, for example other CDK libraries, just add
them to your `setup.py` file and rerun the `pip install -r requirements.txt`
command.
## Optional Features
Server access logging is enabled by default. To disable the feature, use the following commands to synthesize and deploy this CDK application.
```
$ cdk synth -c disable_access_log=true --all
$ cdk deploy -c disable_access_log=true --all
```
See https://docs.aws.amazon.com/AmazonS3/latest/userguide/ServerLogs.html for more information about server access logging.
## Useful commands
* `cdk ls` list all stacks in the app
+3 -2
View File
@@ -57,8 +57,9 @@ example_stack = ExampleResources(
tags={Constants.O3DE_PROJECT_TAG_NAME: PROJECT_NAME, Constants.O3DE_FEATURE_TAG_NAME: FEATURE_NAME},
env=env
)
#
# Add the common stack as a dependency of the feature stack
# Add the core stack as a dependency of the feature stack since the feature stack
# requires the core stack outputs for deployment.
example_stack.add_dependency(core_construct.common_stack)
app.synth()
+19 -18
View File
@@ -60,17 +60,6 @@ class CoreStack(core.Stack):
type='TAG_FILTERS_1_0')
)
# Create an S3 bucket for Amazon S3 server access logging
# See https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html
self._server_access_logs_bucket = s3.Bucket(
self,
f'{self._project_name}-{self._feature_name}-Access-Log-Bucket',
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
encryption=s3.BucketEncryption.S3_MANAGED,
access_control=s3.BucketAccessControl.LOG_DELIVERY_WRITE
)
self._server_access_logs_bucket.grant_read(self._admin_group)
# Define exports
# Export resource group
self._resource_group_output = core.CfnOutput(
@@ -94,10 +83,22 @@ class CoreStack(core.Stack):
export_name=f"{self._project_name}:AdminGroup",
value=self._admin_group.group_arn)
# Export access log bucket name
self._server_access_logs_bucket_output = core.CfnOutput(
self,
id=f'ServerAccessLogsBucketOutput',
description='Name of the S3 bucket for storing server access logs generated by the sample CDK application(s)',
export_name=f"{self._project_name}:ServerAccessLogsBucket",
value=self._server_access_logs_bucket.bucket_name)
# Create an S3 bucket for Amazon S3 server access logging
# See https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html
if self.node.try_get_context('disable_access_log') != 'true':
self._server_access_logs_bucket = s3.Bucket(
self,
f'{self._project_name}-{self._feature_name}-Access-Log-Bucket',
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
encryption=s3.BucketEncryption.S3_MANAGED,
access_control=s3.BucketAccessControl.LOG_DELIVERY_WRITE
)
self._server_access_logs_bucket.grant_read(self._admin_group)
# Export access log bucket name
self._server_access_logs_bucket_output = core.CfnOutput(
self,
id=f'ServerAccessLogsBucketOutput',
description='Name of the S3 bucket for storing server access logs generated by the sample CDK application(s)',
export_name=f"{self._project_name}:ServerAccessLogsBucket",
value=self._server_access_logs_bucket.bucket_name)
@@ -118,19 +118,23 @@ class ExampleResources(core.Stack):
# https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html
# 3. Enable Amazon S3 server access logging
# https://docs.aws.amazon.com/AmazonS3/latest/userguide/ServerLogs.html
server_access_logs_bucket = s3.Bucket.from_bucket_name(
self,
f'{self._project_name}-{self._feature_name}-ImportedAccessLogsBucket',
core.Fn.import_value(f"{self._project_name}:ServerAccessLogsBucket")
)
server_access_logs_bucket = None
if self.node.try_get_context('disable_access_log') != 'true':
server_access_logs_bucket = s3.Bucket.from_bucket_name(
self,
f'{self._project_name}-{self._feature_name}-ImportedAccessLogsBucket',
core.Fn.import_value(f"{self._project_name}:ServerAccessLogsBucket")
)
example_bucket = s3.Bucket(
self,
f'{self._project_name}-{self._feature_name}-Example-S3bucket',
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
encryption=s3.BucketEncryption.S3_MANAGED,
server_access_logs_bucket=server_access_logs_bucket,
server_access_logs_prefix=f'{self._project_name}-{self._feature_name}-{self.region}-AccessLogs'
server_access_logs_bucket=
server_access_logs_bucket if server_access_logs_bucket else None,
server_access_logs_prefix=
f'{self._project_name}-{self._feature_name}-{self.region}-AccessLogs' if server_access_logs_bucket else None
)
s3_deployment.BucketDeployment(
@@ -51,7 +51,7 @@ namespace AZ
}
//! Returns the buffer asset that is used for all skinned mesh outputs
virtual Data::Asset<RPI::BufferAsset> GetBufferAsset() const = 0;
virtual Data::Asset<RPI::BufferAsset> GetBufferAsset() = 0;
//! Returns the buffer that is used for all skinned mesh outputs
virtual Data::Instance<RPI::Buffer> GetBuffer() = 0;
@@ -8,6 +8,8 @@
#include <SkinnedMesh/SkinnedMeshOutputStreamManager.h>
#include <AzCore/Console/IConsole.h>
#include <Atom/Feature/SkinnedMesh/SkinnedMeshVertexStreams.h>
#include <Atom/Feature/SkinnedMesh/SkinnedMeshFeatureProcessorBus.h>
@@ -72,11 +74,32 @@ namespace AZ
creator.End(m_bufferAsset);
}
// default value of 256mb supports roughly 42 character instances at 100,000 vertices per character x 64 bytes per vertex (12 byte position + 12 byte previous frame position + 12 byte normal + 16 byte tangent + 12 byte bitangent)
// This includes only the output of the skinning compute shader, not the input buffers or bone transforms
AZ_CVAR(
int,
r_skinnedMeshInstanceMemoryPoolSize,
256,
nullptr,
AZ::ConsoleFunctorFlags::NeedsReload,
"The amount of memory in Mb available for all actor skinning data. Note that this must only be set once at application startup"
);
void SkinnedMeshOutputStreamManager::Init()
{
// 256mb supports roughly 42 character instances at 100,000 vertices per character x 64 bytes per vertex (12 byte position + 12 byte previous frame position + 12 byte normal + 16 byte tangent + 12 byte bitangent)
// This includes only the output of the skinning compute shader, not the input buffers or bone transforms
m_sizeInBytes = 256u * (1024u * 1024u);
}
void SkinnedMeshOutputStreamManager::EnsureInit()
{
if (!m_needsInit)
{
return;
}
m_needsInit = false;
const AZ::u64 sizeInMb = r_skinnedMeshInstanceMemoryPoolSize;
m_sizeInBytes = sizeInMb * (1024u * 1024u);
CalculateAlignment();
@@ -90,6 +113,8 @@ namespace AZ
RHI::VirtualAddress result;
{
AZStd::lock_guard<AZStd::mutex> lock(m_allocatorMutex);
EnsureInit();
result = m_freeListAllocator.Allocate(byteCount, m_alignment);
}
@@ -127,13 +152,15 @@ namespace AZ
}
}
Data::Asset<RPI::BufferAsset> SkinnedMeshOutputStreamManager::GetBufferAsset() const
Data::Asset<RPI::BufferAsset> SkinnedMeshOutputStreamManager::GetBufferAsset()
{
EnsureInit();
return m_bufferAsset;
}
Data::Instance<RPI::Buffer> SkinnedMeshOutputStreamManager::GetBuffer()
{
EnsureInit();
if (!m_buffer)
{
m_buffer = RPI::Buffer::FindOrCreate(m_bufferAsset);
@@ -39,13 +39,14 @@ namespace AZ
AZStd::intrusive_ptr<SkinnedMeshOutputStreamAllocation> Allocate(size_t byteCount) override;
void DeAllocate(RHI::VirtualAddress allocation) override;
void DeAllocateNoSignal(RHI::VirtualAddress allocation) override;
Data::Asset<RPI::BufferAsset> GetBufferAsset() const override;
Data::Asset<RPI::BufferAsset> GetBufferAsset() override;
Data::Instance<RPI::Buffer> GetBuffer() override;
private:
// SystemTickBus
void OnSystemTick() override;
void EnsureInit();
void GarbageCollect();
void CalculateAlignment();
void CreateBufferAsset();
@@ -58,6 +59,7 @@ namespace AZ
size_t m_sizeInBytes = 0;
bool m_memoryWasFreed = false;
bool m_broadcastMemoryAvailableEvent = false;
bool m_needsInit = true;
};
} // namespace Render
} // namespace AZ
@@ -71,7 +71,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(
AZ::Edit::Attributes::HelpPageURL,
"https://o3de.org/docs/user-guide/components/reference/attachment/")
"https://o3de.org/docs/user-guide/components/reference/animation/attachment/")
->DataElement(0, &EditorAttachmentComponent::m_targetId, "Target entity", "Attach to this entity.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnTargetIdChanged)
->DataElement(
@@ -49,7 +49,7 @@ namespace AZ
->Attribute(Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/AreaLight.svg")
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(Edit::Attributes::AutoExpand, true)
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/area-light/")
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/light/")
;
editContext->Class<AreaLightComponentController>(
@@ -44,7 +44,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO][ATOM-1998] create icons.
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(Edit::Attributes::AutoExpand, true)
->Attribute(Edit::Attributes::HelpPageURL, "https://") // [GFX TODO][ATOM-1998] create page
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/directional-light/") // [GFX TODO][ATOM-1998] create page
;
editContext->Class<DirectionalLightComponentController>(
@@ -54,6 +54,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/diffuse-probe-grid/")
->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<RPI::ModelAsset>::Uuid())
->ClassElement(AZ::Edit::ClassElements::Group, "Probe Spacing")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
@@ -34,7 +34,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/")
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/grid/")
;
editContext->Class<GridComponentController>(
@@ -34,7 +34,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/")
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/global-skylight-ibl/")
;
editContext->Class<ImageBasedLightComponentController>(
@@ -86,7 +86,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/")
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/material/")
->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<RPI::MaterialAsset>::Uuid())
->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &EditorMaterialComponent::m_message, "Message", "")
->Attribute(AZ_CRC("PlaceholderText", 0xa23ec278), "Component cannot be edited with multiple entities selected")
@@ -48,7 +48,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Mesh.svg")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/")
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/mesh/")
->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<RPI::ModelAsset>::Uuid())
->DataElement(AZ::Edit::UIHandlers::Button, &EditorMeshComponent::m_addMaterialComponentFlag, "Add Material Component", "Add Material Component")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
@@ -37,6 +37,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/occlusion-culling-plane/")
;
editContext->Class<OcclusionCullingPlaneComponentController>(
@@ -32,7 +32,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing.
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(Edit::Attributes::AutoExpand, true)
->Attribute(Edit::Attributes::HelpPageURL, "https://") // [TODO ATOM-2672][PostFX] need create page for PostProcessing.
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/bloom/") // [TODO ATOM-2672][PostFX] need create page for PostProcessing.
;
editContext->Class<BloomComponentController>(
@@ -32,7 +32,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing.y
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(Edit::Attributes::AutoExpand, true)
->Attribute(Edit::Attributes::HelpPageURL, "https://") // [GFX TODO][ATOM-2672][PostFX] need create page for PostProcessing.
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/depth-of-field/") // [GFX TODO][ATOM-2672][PostFX] need create page for PostProcessing.
;
editContext->Class<DepthOfFieldComponentController>(
@@ -34,7 +34,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO][ATOM-2672][PostFX] need to create icons for PostProcessing.
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector<AZ::Crc32>({ AZ_CRC("Level", 0x9aeacc13), AZ_CRC("Game", 0x232b318c) }))
->Attribute(Edit::Attributes::AutoExpand, true)
->Attribute(Edit::Attributes::HelpPageURL, "https://") // [GFX TODO][ATOM-2672][PostFX] need to create page for PostProcessing.
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/display-mapper/") // [GFX TODO][ATOM-2672][PostFX] need to create page for PostProcessing.
;
editContext->Class<DisplayMapperComponentController>(
@@ -32,7 +32,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/")
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/postfx-layer/")
;
editContext->Class<PostFxLayerComponentController>(
@@ -32,7 +32,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing.
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(Edit::Attributes::AutoExpand, true)
->Attribute(Edit::Attributes::HelpPageURL, "https://") // [TODO ATOM-2672][PostFX] need create page for PostProcessing.
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/exposure-control/") // [TODO ATOM-2672][PostFX] need create page for PostProcessing.
;
editContext->Class<ExposureControlComponentController>(

Some files were not shown because too many files have changed in this diff Show More