diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py index 905722d103..7051b9983c 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py @@ -4,10 +4,18 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ +import logging +import os import pytest +import ly_test_tools.environment.file_system as file_system +import editor_python_test_tools.hydra_test_utils as hydra + from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite +logger = logging.getLogger(__name__) +TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests") + @pytest.mark.parametrize("project", ["AutomatedTesting"]) @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @@ -114,3 +122,73 @@ class TestAutomation(EditorTestSuite): class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest): from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module + + +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +@pytest.mark.parametrize("launcher_platform", ['windows_generic']) +class TestMaterialEditorBasicTests(object): + @pytest.fixture(autouse=True) + def setup_teardown(self, request, workspace, project): + def delete_files(): + file_system.delete( + [ + os.path.join(workspace.paths.project(), "Materials", "test_material.material"), + os.path.join(workspace.paths.project(), "Materials", "test_material_1.material"), + os.path.join(workspace.paths.project(), "Materials", "test_material_2.material"), + ], + True, + True, + ) + # Cleanup our newly created materials + delete_files() + + def teardown(): + # Cleanup our newly created materials + delete_files() + + request.addfinalizer(teardown) + + @pytest.mark.parametrize("exe_file_name", ["MaterialEditor"]) + @pytest.mark.test_case_id("C34448113") # Creating a New Asset. + @pytest.mark.test_case_id("C34448114") # Opening an Existing Asset. + @pytest.mark.test_case_id("C34448115") # Closing Selected Material. + @pytest.mark.test_case_id("C34448116") # Closing All Materials. + @pytest.mark.test_case_id("C34448117") # Closing all but Selected Material. + @pytest.mark.test_case_id("C34448118") # Saving Material. + @pytest.mark.test_case_id("C34448119") # Saving as a New Material. + @pytest.mark.test_case_id("C34448120") # Saving as a Child Material. + @pytest.mark.test_case_id("C34448121") # Saving all Open Materials. + def test_MaterialEditorBasicTests( + self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name): + + expected_lines = [ + "Material opened: True", + "Test asset doesn't exist initially: True", + "New asset created: True", + "New Material opened: True", + "Material closed: True", + "All documents closed: True", + "Close All Except Selected worked as expected: True", + "Actual Document saved with changes: True", + "Document saved as copy is saved with changes: True", + "Document saved as child is saved with changes: True", + "Save All worked as expected: True", + ] + unexpected_lines = [ + "Traceback (most recent call last):" + ] + + hydra.launch_and_validate_results( + request, + TEST_DIRECTORY, + generic_launcher, + "hydra_AtomMaterialEditor_BasicTests.py", + run_python="--runpython", + timeout=43, + expected_lines=expected_lines, + unexpected_lines=unexpected_lines, + halt_on_unexpected=True, + null_renderer=True, + log_file_name="MaterialEditor.log", + enable_prefab_system=False, + ) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py index 29f90e6807..c9182070f6 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py @@ -83,77 +83,6 @@ class TestAtomEditorComponentsMain(object): ) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("launcher_platform", ['windows_generic']) -@pytest.mark.system -class TestMaterialEditorBasicTests(object): - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project): - def delete_files(): - file_system.delete( - [ - os.path.join(workspace.paths.project(), "Materials", "test_material.material"), - os.path.join(workspace.paths.project(), "Materials", "test_material_1.material"), - os.path.join(workspace.paths.project(), "Materials", "test_material_2.material"), - ], - True, - True, - ) - # Cleanup our newly created materials - delete_files() - - def teardown(): - # Cleanup our newly created materials - delete_files() - - request.addfinalizer(teardown) - - @pytest.mark.parametrize("exe_file_name", ["MaterialEditor"]) - @pytest.mark.test_case_id("C34448113") # Creating a New Asset. - @pytest.mark.test_case_id("C34448114") # Opening an Existing Asset. - @pytest.mark.test_case_id("C34448115") # Closing Selected Material. - @pytest.mark.test_case_id("C34448116") # Closing All Materials. - @pytest.mark.test_case_id("C34448117") # Closing all but Selected Material. - @pytest.mark.test_case_id("C34448118") # Saving Material. - @pytest.mark.test_case_id("C34448119") # Saving as a New Material. - @pytest.mark.test_case_id("C34448120") # Saving as a Child Material. - @pytest.mark.test_case_id("C34448121") # Saving all Open Materials. - def test_MaterialEditorBasicTests( - self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name): - - expected_lines = [ - "Material opened: True", - "Test asset doesn't exist initially: True", - "New asset created: True", - "New Material opened: True", - "Material closed: True", - "All documents closed: True", - "Close All Except Selected worked as expected: True", - "Actual Document saved with changes: True", - "Document saved as copy is saved with changes: True", - "Document saved as child is saved with changes: True", - "Save All worked as expected: True", - ] - unexpected_lines = [ - "Traceback (most recent call last):" - ] - - hydra.launch_and_validate_results( - request, - TEST_DIRECTORY, - generic_launcher, - "hydra_AtomMaterialEditor_BasicTests.py", - run_python="--runpython", - timeout=120, - expected_lines=expected_lines, - unexpected_lines=unexpected_lines, - halt_on_unexpected=True, - null_renderer=True, - log_file_name="MaterialEditor.log", - enable_prefab_system=False, - ) - - @pytest.mark.parametrize("project", ["AutomatedTesting"]) @pytest.mark.parametrize("launcher_platform", ['windows_editor']) class TestAutomation(EditorTestSuite): diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py index b21c74de19..f7ff970541 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py @@ -162,6 +162,13 @@ def select_model_config(configname): azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SelectModelPresetByName", configname) +def destroy_main_window(): + """ + Closes the Material Editor window + """ + azlmbr.atomtools.AtomToolsMainWindowFactoryRequestBus(azlmbr.bus.Broadcast, "DestroyMainWindow") + + def wait_for_condition(function, timeout_in_seconds=1.0): # type: (function, float) -> bool """ diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py index 9f8f6c44b2..baad02318d 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py @@ -186,6 +186,11 @@ def run(): material_editor.set_property(document2_id, property2_name, initial_color) material_editor.save_all() material_editor.close_all_documents() + material_editor.wait_for_condition(lambda: + (not material_editor.is_open(document1_id)) and + (not material_editor.is_open(document2_id)) and + (not material_editor.is_open(document3_id)), 2.0) + material_editor.destroy_main_window() if __name__ == "__main__": diff --git a/Code/Editor/NewLevelDialog.cpp b/Code/Editor/NewLevelDialog.cpp index c773acdb6f..a97eb30f57 100644 --- a/Code/Editor/NewLevelDialog.cpp +++ b/Code/Editor/NewLevelDialog.cpp @@ -115,7 +115,6 @@ CNewLevelDialog::~CNewLevelDialog() void CNewLevelDialog::OnStartup() { UpdateData(false); - setFocus(); } void CNewLevelDialog::UpdateData(bool fromUi) diff --git a/Code/Editor/NewLevelDialog.ui b/Code/Editor/NewLevelDialog.ui index 14227fbb53..93a88dc897 100644 --- a/Code/Editor/NewLevelDialog.ui +++ b/Code/Editor/NewLevelDialog.ui @@ -133,6 +133,9 @@ 1 + + LEVEL + diff --git a/Code/Framework/AzTest/AzTest/Platform/Windows/ScopedAutoTempDirectory_Windows.cpp b/Code/Framework/AzTest/AzTest/Platform/Windows/ScopedAutoTempDirectory_Windows.cpp index fcda5d351a..b5314eda14 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Windows/ScopedAutoTempDirectory_Windows.cpp +++ b/Code/Framework/AzTest/AzTest/Platform/Windows/ScopedAutoTempDirectory_Windows.cpp @@ -8,57 +8,41 @@ #include +#include #include #include #include -#include +#include -namespace AZ +namespace AZ::Test { - namespace Test + ScopedAutoTempDirectory::ScopedAutoTempDirectory() { - ScopedAutoTempDirectory::ScopedAutoTempDirectory() - { - constexpr const DWORD bufferSize = static_cast(AZ::IO::MaxPathLength); + using UuidString = AZStd::fixed_string; + constexpr DWORD bufferSize = static_cast(AZ::IO::MaxPathLength); - char tempDir[bufferSize] = {0}; - GetTempPathA(bufferSize, tempDir); + wchar_t tempDirW[AZ::IO::MaxPathLength]{}; + GetTempPathW(bufferSize, tempDirW); - char workingTempPathBuffer[bufferSize] = {'\0'}; + AZ::IO::FixedMaxPath tempDirectoryRoot; + AZStd::to_string(tempDirectoryRoot.Native(), tempDirW); - int maxAttempts = 2000; // Prevent an infinite loop by setting an arbitrary maximum attempts at finding an available temp folder name - while (maxAttempts > 0) + constexpr int MaxAttempts = 255; + for (int i = 0; i < MaxAttempts; ++i) + { + AZ::IO::FixedMaxPath testPath = tempDirectoryRoot / + AZ::IO::FixedMaxPathString::format("UnitTest-%s", + AZ::Uuid::CreateRandom().ToString().c_str()); + // Try to create the temp directory if it doesn't exist + if (!AZ::IO::SystemFile::Exists(testPath.c_str()) && AZ::IO::SystemFile::CreateDir(testPath.c_str())) { - // Use the system's tick count to base the folder name - ULONGLONG currentTick = GetTickCount64(); - azsnprintf(workingTempPathBuffer, bufferSize, "%sUnitTest-%X", tempDir, aznumeric_cast(currentTick)); - - // Check if the requested directory name is available and re-generate if it already exists - bool exists = AZ::IO::SystemFile::Exists(workingTempPathBuffer); - if (exists) - { - Sleep(1); - maxAttempts--; - continue; - } + azstrncpy(AZStd::data(m_tempDirectory), AZStd::size(m_tempDirectory), + testPath.c_str(), testPath.Native().size()); break; } - - AZ_Error("AzTest", maxAttempts > 0, "Unable to determine a temp directory"); - - if (maxAttempts > 0) - { - // Create the temp directory and track it for deletion - bool tempDirectoryCreated = AZ::IO::SystemFile::CreateDir(workingTempPathBuffer); - if (tempDirectoryCreated) - { - azstrncpy(m_tempDirectory, AZ::IO::MaxPathLength, workingTempPathBuffer, AZ::IO::MaxPathLength); - } - else - { - AZ_Error("AzTest", false, "Unable to create temp directory %s", workingTempPathBuffer); - } - } } - } // Test -} // AZ + + AZ_Error("AzTest", m_tempDirectory[0] != '\0', "Unable to create temp path within directory %s after %d attempts", + tempDirectoryRoot.c_str(), MaxAttempts); + } +} // AZ::Test diff --git a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp index 8c2c0e80b6..c51728a5c3 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp @@ -17,6 +17,7 @@ #include #include +#include #include namespace Terrain @@ -43,11 +44,25 @@ namespace Terrain AZ::Edit::UIHandlers::ComboBox, &TerrainPhysicsSurfaceMaterialMapping::m_surfaceTag, "Surface Tag", "Surface type to map to a physics material.") ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainPhysicsSurfaceMaterialMapping::m_materialId, "Material ID", "") + ->ElementAttribute(Physics::Attributes::MaterialLibraryAssetId, &TerrainPhysicsSurfaceMaterialMapping::GetMaterialLibraryId) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true); } } } + + AZ::Data::AssetId TerrainPhysicsSurfaceMaterialMapping::GetMaterialLibraryId() + { + if (const auto* physicsSystem = AZ::Interface::Get()) + { + if (const auto* physicsConfiguration = physicsSystem->GetConfiguration()) + { + return physicsConfiguration->m_materialLibraryAsset.GetId(); + } + } + return {}; + } + void TerrainPhysicsColliderConfig::Reflect(AZ::ReflectContext* context) { TerrainPhysicsSurfaceMaterialMapping::Reflect(context); diff --git a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h index 1a7fbf9c72..8a70f282d0 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h @@ -36,6 +36,9 @@ namespace Terrain SurfaceData::SurfaceTag m_surfaceTag; Physics::MaterialId m_materialId; + + private: + static AZ::Data::AssetId GetMaterialLibraryId(); }; class TerrainPhysicsColliderConfig