From c2a6addf1ff60dfed88c19d3ceb5f00937bcc904 Mon Sep 17 00:00:00 2001 From: alexpete Date: Tue, 20 Apr 2021 10:42:39 -0700 Subject: [PATCH 001/233] Integrating latest from github/TIF/Runtime --- .../Console/Code/Source/TestImpactConsole.cpp | 20 +- .../Runtime/Code/CMakeLists.txt | 64 +- .../TestImpactFramework/TestImpactBitwise.h | 35 + .../TestImpactFramework/TestImpactCallback.h | 23 + .../TestImpactFramework/TestImpactException.h | 51 + .../TestImpactFrameworkPath.h | 41 + .../Artifact/Dynamic/TestImpactChangeList.h | 27 + .../Artifact/Dynamic/TestImpactCoverage.h | 41 + .../Dynamic/TestImpactTestEnumerationSuite.h | 21 + .../Artifact/Dynamic/TestImpactTestRunSuite.h | 51 + .../Artifact/Dynamic/TestImpactTestSuite.h | 35 + ...TestImpactBuildTargetDescriptorFactory.cpp | 172 ++ .../TestImpactBuildTargetDescriptorFactory.h | 33 + .../Factory/TestImpactChangeListFactory.cpp | 175 ++ .../Factory/TestImpactChangeListFactory.h | 29 + .../TestImpactModuleCoverageFactory.cpp | 154 ++ .../Factory/TestImpactModuleCoverageFactory.h | 26 + .../TestImpactTestEnumerationSuiteFactory.cpp | 94 + .../TestImpactTestEnumerationSuiteFactory.h | 26 + .../Factory/TestImpactTestRunSuiteFactory.cpp | 143 ++ .../Factory/TestImpactTestRunSuiteFactory.h | 26 + .../TestImpactTestTargetMetaArtifactFactory.h | 25 + .../TestImpactTestTargetMetaMapFactory.cpp | 89 + .../TestImpactTestTargetMetaMapFactory.h | 25 + .../TestImpactBuildTargetDescriptor.cpp | 22 + .../Static/TestImpactBuildTargetDescriptor.h | 55 + .../TestImpactProductionTargetDescriptor.cpp | 21 + .../TestImpactProductionTargetDescriptor.h | 25 + .../TestImpactTargetDescriptorCompiler.cpp | 45 + .../TestImpactTargetDescriptorCompiler.h | 31 + .../Static/TestImpactTestTargetDescriptor.cpp | 22 + .../Static/TestImpactTestTargetDescriptor.h | 28 + .../Static/TestImpactTestTargetMeta.h | 36 + .../Artifact/TestImpactArtifactException.h | 26 + .../Runtime/Code/Source/Dummy.cpp | 11 - .../Clang/testimpactframework_clang.cmake | 12 + .../MSVC/testimpactframework_msvc.cmake | 12 + .../Source/Platform/Windows/Dummy_Windows.cpp | 11 - .../Windows/Process/TestImpactWin32_Handle.h | 92 + .../Windows/Process/TestImpactWin32_Pipe.cpp | 67 + .../Windows/Process/TestImpactWin32_Pipe.h | 52 + .../Process/TestImpactWin32_Process.cpp | 259 +++ .../Windows/Process/TestImpactWin32_Process.h | 101 + .../TestImpactWin32_ProcessLauncher.cpp | 24 + .../Windows/platform_windows_files.cmake | 7 +- .../Process/JobRunner/TestImpactProcessJob.h | 139 ++ .../JobRunner/TestImpactProcessJobInfo.h | 73 + .../JobRunner/TestImpactProcessJobRunner.h | 190 ++ .../Scheduler/TestImpactProcessScheduler.cpp | 270 +++ .../Scheduler/TestImpactProcessScheduler.h | 110 + .../Code/Source/Process/TestImpactProcess.cpp | 32 + .../Code/Source/Process/TestImpactProcess.h | 61 + .../Process/TestImpactProcessException.h | 26 + .../Source/Process/TestImpactProcessInfo.cpp | 67 + .../Source/Process/TestImpactProcessInfo.h | 93 + .../Process/TestImpactProcessLauncher.h | 27 + .../Source/Target/TestImpactBuildTarget.cpp | 48 + .../Source/Target/TestImpactBuildTarget.h | 55 + .../Source/Target/TestImpactBuildTargetList.h | 125 ++ .../Target/TestImpactProductionTarget.cpp | 21 + .../Target/TestImpactProductionTarget.h | 31 + .../Target/TestImpactProductionTargetList.h | 22 + .../Source/Target/TestImpactTargetException.h | 25 + .../Source/Target/TestImpactTestTarget.cpp | 32 + .../Code/Source/Target/TestImpactTestTarget.h | 41 + .../Source/Target/TestImpactTestTargetList.h | 22 + .../Enumeration/TestImpactTestEnumeration.h | 22 + .../TestImpactTestEnumerationException.h | 26 + .../TestImpactTestEnumerationSerializer.cpp | 100 + .../TestImpactTestEnumerationSerializer.h | 26 + .../Enumeration/TestImpactTestEnumerator.cpp | 190 ++ .../Enumeration/TestImpactTestEnumerator.h | 94 + .../Source/Test/Job/TestImpactTestJobCommon.h | 36 + .../Test/Job/TestImpactTestJobException.h | 26 + .../Source/Test/Job/TestImpactTestJobRunner.h | 141 ++ .../Run/TestImpactInstrumentedTestRunner.cpp | 89 + .../Run/TestImpactInstrumentedTestRunner.h | 71 + .../Test/Run/TestImpactTestCoverage.cpp | 68 + .../Source/Test/Run/TestImpactTestCoverage.h | 54 + .../Source/Test/Run/TestImpactTestRun.cpp | 70 + .../Code/Source/Test/Run/TestImpactTestRun.h | 51 + .../Test/Run/TestImpactTestRunException.h | 25 + .../Test/Run/TestImpactTestRunJobData.cpp | 26 + .../Test/Run/TestImpactTestRunJobData.h | 31 + .../Test/Run/TestImpactTestRunSerializer.cpp | 186 ++ .../Test/Run/TestImpactTestRunSerializer.h | 26 + .../Source/Test/Run/TestImpactTestRunner.cpp | 58 + .../Source/Test/Run/TestImpactTestRunner.h | 46 + .../Test/TestImpactTestSuiteContainer.h | 101 + .../Code/Source/TestImpactException.cpp | 31 + .../Code/Source/TestImpactFrameworkPath.cpp | 38 + ...ImpactBuildTargetDescriptorFactoryTest.cpp | 370 ++++ .../TestImpactChangeListFactoryTest.cpp | 288 +++ .../TestImpactModuleCoverageFactoryTest.cpp | 522 +++++ ...TestImpactTargetDescriptorCompilerTest.cpp | 150 ++ ...tImpactTestEnumerationSuiteFactoryTest.cpp | 589 ++++++ .../TestImpactTestRunSuiteFactoryTest.cpp | 592 ++++++ ...TestImpactTestTargetMetaMapFactoryTest.cpp | 239 +++ .../TestImpactProcessSchedulerTest.cpp | 718 +++++++ .../Tests/Process/TestImpactProcessTest.cpp | 491 +++++ .../Target/TestImpactBuildTargetTest.cpp | 352 ++++ .../TestImpactInstrumentedTestRunnerTest.cpp | 822 ++++++++ .../Tests/Test/TestImpactTestCoverageTest.cpp | 208 ++ .../Test/TestImpactTestEnumeratorTest.cpp | 890 ++++++++ ...TestImpactTestEumerationSerializerTest.cpp | 99 + .../Test/TestImpactTestRunSerializerTest.cpp | 99 + .../Tests/Test/TestImpactTestRunnerTest.cpp | 516 +++++ .../Code/Tests/TestImpactExceptionTest.cpp | 150 ++ .../Tests/TestImpactFrameworkPathTest.cpp | 137 ++ .../Tests/TestImpactTestJobRunnerCommon.h | 159 ++ .../Runtime/Code/Tests/TestImpactTestMain.cpp | 33 + .../Code/Tests/TestImpactTestUtils.cpp | 1843 +++++++++++++++++ .../Runtime/Code/Tests/TestImpactTestUtils.h | 220 ++ .../Code/Tests/TestProcess/CMakeLists.txt | 12 + .../Tests/TestProcess/Code/CMakeLists.txt | 24 + .../Code/Source/TestImpactTestProcess.cpp | 93 + .../Code/Source/TestImpactTestProcess.h | 38 + .../Source/TestImpactTestProcessLargeText.cpp | 531 +++++ .../Source/TestImpactTestProcessLargeText.h | 19 + .../Code/Source/TestImpactTestProcessMain.cpp | 19 + ...estimpactframework_testprocess_files.cmake | 18 + .../Code/Tests/TestTargetA/CMakeLists.txt | 12 + .../Tests/TestTargetA/Code/CMakeLists.txt | 28 + .../Code/Tests/TestImpactTestTargetA.cpp | 73 + ...actframework_testtargeta_tests_files.cmake | 14 + .../Code/Tests/TestTargetB/CMakeLists.txt | 12 + .../Tests/TestTargetB/Code/CMakeLists.txt | 29 + .../Code/Tests/TestImpactTestTargetB.cpp | 78 + ...actframework_testtargetb_tests_files.cmake | 14 + .../Code/Tests/TestTargetC/CMakeLists.txt | 12 + .../Tests/TestTargetC/Code/CMakeLists.txt | 29 + .../Code/Tests/TestImpactTestTargetC.cpp | 63 + ...actframework_testtargetc_tests_files.cmake | 14 + .../Code/Tests/TestTargetD/CMakeLists.txt | 12 + .../Tests/TestTargetD/Code/CMakeLists.txt | 29 + .../Code/Tests/TestImpactTestTargetD.cpp | 188 ++ ...actframework_testtargetd_tests_files.cmake | 14 + .../testimpactframework_runtime_files.cmake | 17 +- ...timpactframework_runtime_tests_files.cmake | 22 + 139 files changed, 16391 insertions(+), 37 deletions(-) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactBitwise.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactCallback.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactException.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFrameworkPath.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactChangeList.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactCoverage.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactTestEnumerationSuite.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactTestRunSuite.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactTestSuite.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaArtifactFactory.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactProductionTargetDescriptor.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactProductionTargetDescriptor.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTargetDescriptorCompiler.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTargetDescriptorCompiler.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetDescriptor.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetDescriptor.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetMeta.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/TestImpactArtifactException.h delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dummy.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Common/Clang/testimpactframework_clang.cmake create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Common/MSVC/testimpactframework_msvc.cmake delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Dummy_Windows.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Handle.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Pipe.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Pipe.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_ProcessLauncher.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobInfo.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcess.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcess.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessException.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessInfo.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessInfo.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessLauncher.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactProductionTarget.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactProductionTarget.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactProductionTargetList.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTargetException.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTargetList.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumeration.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationException.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobException.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobRunner.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunException.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/TestImpactTestSuiteContainer.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactException.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactFrameworkPath.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactBuildTargetDescriptorFactoryTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactChangeListFactoryTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactModuleCoverageFactoryTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTargetDescriptorCompilerTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestEnumerationSuiteFactoryTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestRunSuiteFactoryTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestTargetMetaMapFactoryTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessSchedulerTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactInstrumentedTestRunnerTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestCoverageTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEnumeratorTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEumerationSerializerTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunSerializerTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunnerTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactExceptionTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactFrameworkPathTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestJobRunnerCommon.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestMain.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessMain.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/testimpactframework_testprocess_files.cmake create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/Tests/TestImpactTestTargetA.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/testimpactframework_testtargeta_tests_files.cmake create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/Tests/TestImpactTestTargetB.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/testimpactframework_testtargetb_tests_files.cmake create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/Tests/TestImpactTestTargetC.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/testimpactframework_testtargetc_tests_files.cmake create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/Tests/TestImpactTestTargetD.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/testimpactframework_testtargetd_tests_files.cmake create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsole.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsole.cpp index c20b3c60fe..12743f4b38 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsole.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsole.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/CMakeLists.txt index d48acd73c5..4c021a12b4 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/CMakeLists.txt +++ b/Code/Tools/TestImpactFramework/Runtime/Code/CMakeLists.txt @@ -9,8 +9,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # - ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME}) +ly_get_list_relative_pal_filename(common_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/Common) + +add_subdirectory(Tests/TestProcess) +add_subdirectory(Tests/TestTargetA) +add_subdirectory(Tests/TestTargetB) +add_subdirectory(Tests/TestTargetC) +add_subdirectory(Tests/TestTargetD) ly_add_target( NAME TestImpact.Runtime.Static STATIC @@ -18,12 +24,64 @@ ly_add_target( FILES_CMAKE testimpactframework_runtime_files.cmake ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + PLATFORM_INCLUDE_FILES + ${common_dir}/${PAL_TRAIT_COMPILER_ID}/testimpactframework_${PAL_TRAIT_COMPILER_ID_LOWERCASE}.cmake INCLUDE_DIRECTORIES PRIVATE Source PUBLIC Include BUILD_DEPENDENCIES - Public + PUBLIC AZ::AzCore -) \ No newline at end of file +) + +################################################################################ +# Tests +################################################################################ + +ly_add_target( + NAME TestImpact.Runtime.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE AZ + FILES_CMAKE + testimpactframework_runtime_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Include + Source + Tests + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTestShared + AZ::AzTest + AZ::TestImpact.Runtime.Static + RUNTIME_DEPENDENCIES + AZ::AzTestRunner + AZ::TestImpact.TestProcess.Console + AZ::TestImpact.TestTargetA.Tests + AZ::TestImpact.TestTargetB.Tests + AZ::TestImpact.TestTargetC.Tests + AZ::TestImpact.TestTargetD.Tests + COMPILE_DEFINITIONS + PRIVATE + LY_TEST_IMPACT_AZ_TESTRUNNER_BIN="$" + LY_TEST_IMPACT_TEST_PROCESS_BIN="$" + LY_TEST_IMPACT_TEST_TARGET_A_BIN="$" + LY_TEST_IMPACT_TEST_TARGET_B_BIN="$" + LY_TEST_IMPACT_TEST_TARGET_C_BIN="$" + LY_TEST_IMPACT_TEST_TARGET_D_BIN="$" + LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME="$" + LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME="$" + LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME="$" + LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME="$" + LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR="${GTEST_XML_OUTPUT_DIR}/TestImpact/Temp/Exclusive/Enum" + LY_TEST_IMPACT_TEST_TARGET_RESULTS_DIR="${GTEST_XML_OUTPUT_DIR}/TestImpact/Temp/Exclusive/Result" + LY_TEST_IMPACT_TEST_TARGET_COVERAGE_DIR="${GTEST_XML_OUTPUT_DIR}/TestImpact/Temp/Exclusive/Coverage" + LY_TEST_IMPACT_INSTRUMENTATION_BIN="${LY_TEST_IMPACT_INSTRUMENTATION_BIN}" + LY_TEST_IMPACT_MODULES_DIR="${CMAKE_BINARY_DIR}" + LY_TEST_IMPACT_COVERAGE_SOURCES_DIR="${CMAKE_CURRENT_SOURCE_DIR}" +) + +ly_add_googletest( + NAME AZ::TestImpact.Runtime.Tests +) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactBitwise.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactBitwise.h new file mode 100644 index 0000000000..73278f6be6 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactBitwise.h @@ -0,0 +1,35 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +namespace TestImpact +{ + //! Convinience namespace for allowing bitwise operations on enum classes. + //! @note Any types declared in this namespace will have the bitwise operator overloads declared within. + namespace Bitwise + { + template + Flags operator|(Flags lhs, Flags rhs) + { + return static_cast( + static_cast::type>(lhs) | static_cast::type>(rhs)); + } + + template + bool IsFlagSet(Flags flags, Flags flag) + { + return static_cast( + static_cast::type>(flags) & static_cast::type>(flag)); + } + } // namespace Bitwise +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactCallback.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactCallback.h new file mode 100644 index 0000000000..a8c9542b47 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactCallback.h @@ -0,0 +1,23 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +namespace TestImpact +{ + //! Generic callback result used by test impact systems. + enum class CallbackResult : bool + { + Continue, + Abort + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactException.h new file mode 100644 index 0000000000..83ba48aa46 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactException.h @@ -0,0 +1,51 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +//! Evaluates the specified condition and throws the specified exception with the specified +// !message upon failure. +#define AZ_TestImpact_Eval(CONDITION, EXCEPTION_TYPE, MSG) \ + do \ + { \ + static_assert( \ + AZStd::is_base_of_v, \ + "TestImpact Eval macro must only be used with TestImpact exceptions"); \ + if(!(CONDITION)) \ + { \ + throw(EXCEPTION_TYPE(MSG)); \ + } \ + } \ + while (0) + +namespace TestImpact +{ + //! Base class for test impact framework exceptions. + //! @note The message passed in to the constructor is copied and thus safe with dynamic strings. + class Exception + : public std::exception + { + public: + explicit Exception() = default; + explicit Exception(const AZStd::string& msg); + explicit Exception(const char* msg); + const char* what() const noexcept override; + + private: + //! Error message detailing the reason for the exception. + AZStd::string m_msg; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFrameworkPath.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFrameworkPath.h new file mode 100644 index 0000000000..affab6daa7 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFrameworkPath.h @@ -0,0 +1,41 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Wrapper for OS paths relative to a specified parent path. + //! @note Mimics path semantics only, makes no guarantees about validity. + class FrameworkPath + { + public: + FrameworkPath() = default; + //! Creates a path with no parent. + explicit FrameworkPath(const AZ::IO::Path& absolutePath); + //! Creates a path with an absolute path and path relative to the specified parent path. + explicit FrameworkPath(const AZ::IO::Path& absolutePath, const FrameworkPath& relativeTo); + + //! Retrieves the absolute path. + const AZ::IO::Path& Absolute() const; + //! Retrieves the path relative to the specified parent path. + const AZ::IO::Path& Relative() const; + + private: + //! The absolute path value. + AZ::IO::Path m_absolutePath; + //! The path value relative to the specified parent path. + AZ::IO::Path m_relativePath; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactChangeList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactChangeList.h new file mode 100644 index 0000000000..6d966385ac --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactChangeList.h @@ -0,0 +1,27 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Artifact produced by the unified diff parsing process representing the file CRUD operations of a given diff. + struct ChangeList + { + AZStd::vector m_createdFiles; + AZStd::vector m_updatedFiles; + AZStd::vector m_deletedFiles; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactCoverage.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactCoverage.h new file mode 100644 index 0000000000..3c5b5353a7 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactCoverage.h @@ -0,0 +1,41 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include + +namespace TestImpact +{ + //! Coverage information about a particular line. + struct LineCoverage + { + size_t m_lineNumber = 0; //!< The source line number this covers. + size_t m_hitCount = 0; //!< Number of times this line was covered (zero if not covered). + }; + + //! Coverage information about a particular source file. + struct SourceCoverage + { + AZ::IO::Path m_path; //!< Source file path. + AZStd::optional> m_coverage; //!< Source file line coverage (empty if source level coverage only). + }; + + //! Coverage information about a particular module (executable, shared library). + struct ModuleCoverage + { + AZ::IO::Path m_path; //!< Module path. + AZStd::vector m_sources; //!< Sources of this module that are covered. + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactTestEnumerationSuite.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactTestEnumerationSuite.h new file mode 100644 index 0000000000..e4c22a8787 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactTestEnumerationSuite.h @@ -0,0 +1,21 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + using TestEnumerationCase = TestCase; //!< Test case for test enumeration artifacts. + using TestEnumerationSuite = TestSuite; //!< Test suite for test enumeration artifacts. +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactTestRunSuite.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactTestRunSuite.h new file mode 100644 index 0000000000..22254db38e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactTestRunSuite.h @@ -0,0 +1,51 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include +#include + +namespace TestImpact +{ + //! Result of a test that was ran. + enum class TestRunResult : bool + { + Failed, //! The given test failed. + Passed //! The given test passed. + }; + + //! Status of test as to whether or not it was ran. + enum class TestRunStatus : bool + { + NotRun, //!< The test was not run (typically because the test run was aborted by the client or runner before the test could run). + Run //!< The test was run (see TestRunResult for the result of this test). + }; + + //! Test case for test run artifacts. + struct TestRunCase + : public TestCase + { + AZStd::optional m_result; + AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{0}; //! Duration this test took to run. + TestRunStatus m_status = TestRunStatus::NotRun; + }; + + //! Test suite for test run artifacts. + struct TestRunSuite + : public TestSuite + { + AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{0}; //!< Duration this test suite took to run all of its tests. + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactTestSuite.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactTestSuite.h new file mode 100644 index 0000000000..0646dd384a --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactTestSuite.h @@ -0,0 +1,35 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Artifact describing basic information about a test case. + struct TestCase + { + AZStd::string m_name; + bool m_enabled = false; + }; + + //! Artifact describing basic information about a test suite. + template + struct TestSuite + { + AZStd::string m_name; + bool m_enabled = false; + AZStd::vector m_tests; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp new file mode 100644 index 0000000000..9e82170dff --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp @@ -0,0 +1,172 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include + +namespace TestImpact +{ + namespace + { + // Keys for pertinent JSON node and attribute names + constexpr const char* Keys[] = + { + "target", + "name", + "output_name", + "path", + "sources", + "static", + "input", + "output" + }; + + enum + { + TargetKey, + NameKey, + OutputNameKey, + PathKey, + SourcesKey, + StaticKey, + InputKey, + OutputKey + }; + + } // namespace + + AutogenSources PairAutogenSources( + const AZStd::vector& inputSources, + const AZStd::vector& outputSources, + const AZStd::string& autogenMatcher) + { + AutogenSources autogenSources; + const auto matcherPattern = AZStd::regex(autogenMatcher); + AZStd::smatch inputMatches, outputMatches; + + // This has the potential to be optimized to O(n(n-1)/2) time complexity but to be perfectly honest it's not a serious + // bottleneck right now and easier gains would be achieved by constructing build target artifacts in parallel rather than + // trying to squeeze any more juice here as each build target is independent of one and other with no shared memory + for (const auto& input : inputSources) + { + AutogenPairs autogenPairs; + autogenPairs.m_input = input.String(); + const AZStd::string inputString = input.Stem().Native(); + if (AZStd::regex_search(inputString, inputMatches, matcherPattern)) + { + for (const auto& output : outputSources) + { + const AZStd::string outputString = output.Stem().Native(); + if (AZStd::regex_search(outputString, outputMatches, matcherPattern)) + { + // Note: [0] contains the whole match, [1] contains the first capture group + const auto& inputMatch = inputMatches[1]; + const auto& outputMatch = outputMatches[1]; + if (inputMatch == outputMatch) + { + autogenPairs.m_outputs.emplace_back(output); + } + } + } + } + + if (!autogenPairs.m_outputs.empty()) + { + autogenSources.emplace_back(AZStd::move(autogenPairs)); + } + } + + return autogenSources; + } + + BuildTargetDescriptor BuildTargetDescriptorFactory( + const AZStd::string& buildTargetData, + const AZStd::vector& staticSourceExtensionExcludes, + const AZStd::vector& autogenInputExtensionExcludes, + const AZStd::string& autogenMatcher) + { + AZ_TestImpact_Eval(!autogenMatcher.empty(), ArtifactException, "Autogen matcher cannot be empty"); + + BuildTargetDescriptor buildTargetDescriptor; + rapidjson::Document buildTarget; + + if (buildTarget.Parse(buildTargetData.c_str()).HasParseError()) + { + throw TestImpact::ArtifactException("Could not parse build target data"); + } + + const auto& target = buildTarget[Keys[TargetKey]]; + buildTargetDescriptor.m_buildMetaData.m_name = target[Keys[NameKey]].GetString(); + buildTargetDescriptor.m_buildMetaData.m_outputName = target[Keys[OutputNameKey]].GetString(); + buildTargetDescriptor.m_buildMetaData.m_path = target["path"].GetString(); + + AZ_TestImpact_Eval(!buildTargetDescriptor.m_buildMetaData.m_name.empty(), ArtifactException, "Target name cannot be empty"); + AZ_TestImpact_Eval( + !buildTargetDescriptor.m_buildMetaData.m_outputName.empty(), ArtifactException, "Target output name cannot be empty"); + AZ_TestImpact_Eval(!buildTargetDescriptor.m_buildMetaData.m_path.empty(), ArtifactException, "Target path cannot be empty"); + + const auto& sources = buildTarget[Keys[SourcesKey]]; + const auto& staticSources = sources[Keys[StaticKey]].GetArray(); + if (!staticSources.Empty()) + { + buildTargetDescriptor.m_sources.m_staticSources = AZStd::vector(); + + for (const auto& source : staticSources) + { + const AZ::IO::Path sourcePath = AZ::IO::Path(source.GetString()); + if (AZStd::find( + staticSourceExtensionExcludes.begin(), staticSourceExtensionExcludes.end(), sourcePath.Extension().Native()) == + staticSourceExtensionExcludes.end()) + { + buildTargetDescriptor.m_sources.m_staticSources.emplace_back(AZStd::move(sourcePath)); + } + } + } + + const auto& inputSources = buildTarget[Keys[SourcesKey]][Keys[InputKey]].GetArray(); + const auto& outputSources = buildTarget[Keys[SourcesKey]][Keys[OutputKey]].GetArray(); + if (!inputSources.Empty() || !outputSources.Empty()) + { + AZ_TestImpact_Eval( + !inputSources.Empty() && !outputSources.Empty(), ArtifactException, "Autogen malformed, input or output sources are empty"); + + AZStd::vector inputPaths; + AZStd::vector outputPaths; + inputPaths.reserve(inputSources.Size()); + outputPaths.reserve(outputSources.Size()); + + for (const auto& source : inputSources) + { + const AZ::IO::Path sourcePath = AZ::IO::Path(source.GetString()); + if (AZStd::find( + autogenInputExtensionExcludes.begin(), autogenInputExtensionExcludes.end(), sourcePath.Extension().Native()) == + autogenInputExtensionExcludes.end()) + { + inputPaths.emplace_back(AZStd::move(sourcePath)); + } + } + + for (const auto& source : outputSources) + { + outputPaths.emplace_back(AZStd::move(AZ::IO::Path(source.GetString()))); + } + + buildTargetDescriptor.m_sources.m_autogenSources = PairAutogenSources(inputPaths, outputPaths, autogenMatcher); + } + + return buildTargetDescriptor; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h new file mode 100644 index 0000000000..b23e00478a --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h @@ -0,0 +1,33 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include +#include + +namespace TestImpact +{ + //! Constructs a build target artifact from the specified build target data. + //! @param buildTargetData The raw build target data in JSON format. + //! @param staticSourceExcludes The list of file extensions to exclude for static sources. + //! @param autogenInputExtentsionExcludes The list of file extensions to exclude for autogen input sources. + //! @param autogenMatcher The regex pattern used to match autogen input filenames with output filenames. + //! @return The constructed build target artifact. + BuildTargetDescriptor BuildTargetDescriptorFactory( + const AZStd::string& buildTargetData, + const AZStd::vector& staticSourceExtentsionExcludes, + const AZStd::vector& autogenInputExtentsionExcludes, + const AZStd::string& autogenMatcher); +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.cpp new file mode 100644 index 0000000000..4b2a33355b --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.cpp @@ -0,0 +1,175 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include + +namespace TestImpact +{ + namespace Utils + { + template + auto split(C&& str, const AZStd::string& delimiter) + { + AZStd::vector strings; + + for (auto p = str.data(), end = p + str.length(); p != end; p += ((p == end) ? 0 : delimiter.length())) + { + const auto pre = p; + p = AZStd::search(pre, end, delimiter.cbegin(), delimiter.cend()); + + if (p != pre) + { + strings.emplace_back(pre, p - pre); + } + } + + return strings; + } + } // namespace Utils + + namespace UnifiedDiff + { + class UnifiedDiffParser + { + public: + ChangeList Parse(const AZStd::string& unifiedDiff); + + private: + AZStd::optional GetTargetFile(const AZStd::string_view& targetFile); + ChangeList GenerateChangelist( + const AZStd::vector>& src, + const AZStd::vector>& dst); + + const AZStd::string m_srcFilePrefix = "--- "; + const AZStd::string m_dstFilePrefix = "+++ "; + const AZStd::string m_gitTargetPrefix = "b/"; + const AZStd::string m_perforceTargetPrefix = "/b/"; + const AZStd::string m_renameFromPrefix = "rename from "; + const AZStd::string m_renameToPrefix = "rename to "; + const AZStd::string m_nullFile = "/dev/null"; + bool m_hasGitHeader = false; + }; + + AZStd::optional UnifiedDiffParser::GetTargetFile(const AZStd::string_view& targetFile) + { + size_t startIndex = 0; + + if (targetFile.starts_with(m_renameFromPrefix)) + { + startIndex = m_renameFromPrefix.length(); + } + else if (targetFile.starts_with(m_renameToPrefix)) + { + startIndex = m_renameToPrefix.length(); + } + else if (targetFile.find(m_nullFile) != AZStd::string::npos) + { + return AZStd::nullopt; + } + else + { + startIndex = m_hasGitHeader ? m_gitTargetPrefix.size() + m_dstFilePrefix.size() + : m_perforceTargetPrefix.size() + m_dstFilePrefix.size(); + } + + const auto endIndex = targetFile.find('\t'); + + if (endIndex != AZStd::string::npos) + { + return targetFile.substr(startIndex, endIndex - startIndex); + } + + return targetFile.substr(startIndex); + } + + ChangeList UnifiedDiffParser::GenerateChangelist( + const AZStd::vector>& src, const AZStd::vector>& dst) + { + AZ_TestImpact_Eval(src.size() == dst.size(), ArtifactException, "Change list source and destination file count mismatch"); + ChangeList changelist; + + for (size_t i = 0; i < src.size(); i++) + { + if (!src[i].has_value()) + { + changelist.m_createdFiles.emplace_back(dst[i].value()); + } + else if (!dst[i].has_value()) + { + changelist.m_deletedFiles.emplace_back(src[i].value()); + } + else if (src[i] != dst[i]) + { + changelist.m_deletedFiles.emplace_back(src[i].value()); + changelist.m_createdFiles.emplace_back(dst[i].value()); + } + else + { + changelist.m_updatedFiles.emplace_back(src[i].value()); + } + } + + return changelist; + } + + ChangeList UnifiedDiffParser::Parse(const AZStd::string& unifiedDiff) + { + const AZStd::string GitHeader = "diff --git"; + const auto lines = Utils::split(unifiedDiff, "\n"); + + AZStd::vector> src; + AZStd::vector> dst; + + for (const auto& line : lines) + { + if (line.starts_with(GitHeader)) + { + m_hasGitHeader = true; + } + else if (line.starts_with(m_srcFilePrefix)) + { + src.emplace_back(GetTargetFile(line)); + } + else if (line.starts_with(m_dstFilePrefix)) + { + dst.emplace_back(GetTargetFile(line)); + } + else if (line.starts_with(m_renameFromPrefix)) + { + src.emplace_back(GetTargetFile(line)); + } + else if (line.starts_with(m_renameToPrefix)) + { + dst.emplace_back(GetTargetFile(line)); + } + } + + return GenerateChangelist(src, dst); + } + + ChangeList ChangeListFactory(const AZStd::string& unifiedDiff) + { + AZ_TestImpact_Eval(!unifiedDiff.empty(), ArtifactException, "Unified diff is empty"); + UnifiedDiffParser diff; + ChangeList changeList = diff.Parse(unifiedDiff); + AZ_TestImpact_Eval( + !changeList.m_createdFiles.empty() || + !changeList.m_updatedFiles.empty() || + !changeList.m_deletedFiles.empty(), + ArtifactException, "The unified diff contained no changes"); + return changeList; + } + } // namespace UnifiedDiff +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.h new file mode 100644 index 0000000000..82074a66ef --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.h @@ -0,0 +1,29 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include +#include + +namespace TestImpact +{ + namespace UnifiedDiff + { + //! Constructs a change list artifact from the specified unified diff data. + //! @param unifiedDiffData The raw change list data in unified diff format. + //! @return The constructed change list artifact. + ChangeList ChangeListFactory(const AZStd::string& unifiedDiffData); + } // namespace UnifiedDiff +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp new file mode 100644 index 0000000000..54ba65c762 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp @@ -0,0 +1,154 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include + +namespace TestImpact +{ + namespace + { + // Keys for pertinent XML node and attribute names + constexpr const char* Keys[] = + { + "packages", + "name", + "filename", + "coverage", + "classes", + "lines", + "line", + "number", + "hits", + "sources", + "source" + }; + + enum + { + PackagesKey, + NameKey, + FileNameKey, + CoverageKey, + ClassesKey, + LinesKey, + LineKey, + NumberKey, + HitsKey, + SourcesKey, + SourceKey + }; + } // namespace + + namespace Cobertura + { + // Note: OpenCppCoverage appears to have a very liberal interpretation of the Cobertura coverage file format so consider + // this implementation to be provisional and coupled to the Windows platform and OpenCppCoverage tool + AZStd::vector ModuleCoveragesFactory(const AZStd::string& coverageData) + { + AZ_TestImpact_Eval(!coverageData.empty(), ArtifactException, "Cannot parse coverage, string is empty"); + AZStd::vector modules; + AZStd::vector rawData(coverageData.begin(), coverageData.end()); + + try + { + AZ::rapidxml::xml_document<> doc; + // Parse the XML doc with default flags + doc.parse<0>(rawData.data()); + + // Coverage + const auto coverage_node = doc.first_node(Keys[CoverageKey]); + AZ_TestImpact_Eval(coverage_node, ArtifactException, "Could not parse coverage node"); + + // Sources + const auto sources_node = coverage_node->first_node(Keys[SourcesKey]); + if (!sources_node) + { + return {}; + } + + // Source + const auto source_node = sources_node->first_node(Keys[SourceKey]); + if (!source_node) + { + return {}; + } + + // Root drive (this seems to be an unconventional use of the sources section by OpenCppCoverage) + const AZStd::string pathRoot = AZStd::string(source_node->value(), source_node->value() + source_node->value_size()) + "\\"; + + const auto packages_node = coverage_node->first_node(Keys[PackagesKey]); + if (packages_node) + { + // Modules + for (auto package_node = packages_node->first_node(); package_node; package_node = package_node->next_sibling()) + { + // Module + ModuleCoverage moduleCoverage; + moduleCoverage.m_path = AZ::IO::Path(package_node->first_attribute(Keys[NameKey])->value()); + + const auto classes_node = package_node->first_node(Keys[ClassesKey]); + if (classes_node) + { + // Sources + for (auto class_node = classes_node->first_node(); class_node; class_node = class_node->next_sibling()) + { + // Source + SourceCoverage sourceCoverage; + sourceCoverage.m_path = AZ::IO::Path(pathRoot + class_node->first_attribute(Keys[FileNameKey])->value()); + + const auto lines_node = class_node->first_node(Keys[LinesKey]); + if (lines_node) + { + AZStd::vector lineCoverage; + + // Lines + for (auto line_node = lines_node->first_node(); line_node; line_node = line_node->next_sibling()) + { + // Line + const size_t number = + AZStd::stol(AZStd::string(line_node->first_attribute(Keys[NumberKey])->value())); + const size_t hits = AZStd::stol(AZStd::string(line_node->first_attribute(Keys[HitsKey])->value())); + lineCoverage.emplace_back(LineCoverage{number, hits}); + } + + if (!lineCoverage.empty()) + { + sourceCoverage.m_coverage.emplace(AZStd::move(lineCoverage)); + } + } + + moduleCoverage.m_sources.emplace_back(AZStd::move(sourceCoverage)); + } + } + + modules.emplace_back(AZStd::move(moduleCoverage)); + } + } + } + catch (const std::exception& e) + { + AZ_Error("ModuleCoveragesFactory", false, e.what()); + throw ArtifactException(e.what()); + } + catch (...) + { + throw ArtifactException("An unknown error occurred parsing the XML data"); + } + + return modules; + } + } // namespace Cobertura +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.h new file mode 100644 index 0000000000..c238901017 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + namespace Cobertura + { + //! Constructs a list of module coverage artifacts from the specified coverage data. + //! @param coverageData The raw coverage data in XML format. + //! @return The constructed list of module coverage artifacts. + AZStd::vector ModuleCoveragesFactory(const AZStd::string& coverageData); + } // namespace Cobertura +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.cpp new file mode 100644 index 0000000000..bf8a19c812 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.cpp @@ -0,0 +1,94 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include + +namespace TestImpact +{ + namespace + { + // Keys for pertinent XML node and attribute names + constexpr const char* Keys[] = + { + "testsuites", + "testsuite", + "name", + "testcase" + }; + + enum + { + TestSuitesKey, + TestSuiteKey, + NameKey, + TestCaseKey + }; + } // namespace + + namespace GTest + { + AZStd::vector TestEnumerationSuitesFactory(const AZStd::string& testEnumerationData) + { + AZ_TestImpact_Eval(!testEnumerationData.empty(), ArtifactException, "Cannot parse enumeration, string is empty"); + AZStd::vector testSuites; + AZStd::vector rawData(testEnumerationData.begin(), testEnumerationData.end()); + + try + { + AZ::rapidxml::xml_document<> doc; + // Parse the XML doc with default flags + doc.parse<0>(rawData.data()); + + const auto testsuites_node = doc.first_node(Keys[TestSuitesKey]); + AZ_TestImpact_Eval(testsuites_node, ArtifactException, "Could not parse enumeration, XML is invalid"); + for (auto testsuite_node = testsuites_node->first_node(Keys[TestSuiteKey]); testsuite_node; + testsuite_node = testsuite_node->next_sibling()) + { + const auto isEnabled = [](const AZStd::string& name) + { + return !name.starts_with("DISABLED_") && name.find("/DISABLED_") == AZStd::string::npos; + }; + + TestEnumerationSuite testSuite; + testSuite.m_name = testsuite_node->first_attribute(Keys[NameKey])->value(); + testSuite.m_enabled = isEnabled(testSuite.m_name); + + for (auto testcase_node = testsuite_node->first_node(Keys[TestCaseKey]); testcase_node; + testcase_node = testcase_node->next_sibling()) + { + TestEnumerationCase testCase; + testCase.m_name = testcase_node->first_attribute(Keys[NameKey])->value(); + testCase.m_enabled = isEnabled(testCase.m_name); + testSuite.m_tests.emplace_back(AZStd::move(testCase)); + } + + testSuites.emplace_back(AZStd::move(testSuite)); + } + } + catch (const std::exception& e) + { + AZ_Error("TestEnumerationSuitesFactory", false, e.what()); + throw ArtifactException(e.what()); + } + catch (...) + { + throw ArtifactException("An unknown error occured parsing the XML data"); + } + + return testSuites; + } + } // namespace GTest +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.h new file mode 100644 index 0000000000..4be58b2116 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + namespace GTest + { + //! Constructs a list of test enumeration suite artifacts from the specified test enumeraion data. + //! @param testEnumerationData The raw test enumeration data in XML format. + //! @return The constructed list of test enumeration suite artifacts. + AZStd::vector TestEnumerationSuitesFactory(const AZStd::string& testEnumerationData); + } // namespace GTest +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp new file mode 100644 index 0000000000..506e058824 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp @@ -0,0 +1,143 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include + +namespace TestImpact +{ + namespace + { + // Keys for pertinent XML node and attribute names + constexpr const char* Keys[] = + { + "testsuites", + "testsuite", + "name", + "testcase", + "status", + "run", + "notrun", + "time" + }; + + enum + { + TestSuitesKey, + TestSuiteKey, + NameKey, + TestCaseKey, + StatusKey, + RunKey, + NotRunKey, + DurationKey + }; + } // namespace + + namespace GTest + { + AZStd::vector TestRunSuitesFactory(const AZStd::string& testEnumerationData) + { + AZ_TestImpact_Eval(!testEnumerationData.empty(), ArtifactException, "Cannot parse test run, string is empty"); + AZStd::vector testSuites; + AZStd::vector rawData(testEnumerationData.begin(), testEnumerationData.end()); + + try + { + AZ::rapidxml::xml_document<> doc; + // Parse the XML doc with default flags + doc.parse<0>(rawData.data()); + + const auto testsuites_node = doc.first_node(Keys[TestSuitesKey]); + AZ_TestImpact_Eval(testsuites_node, ArtifactException, "Could not parse enumeration, XML is invalid"); + for (auto testsuite_node = testsuites_node->first_node(Keys[TestSuiteKey]); testsuite_node; + testsuite_node = testsuite_node->next_sibling()) + { + const auto isEnabled = [](const AZStd::string& name) + { + return !name.starts_with("DISABLED_") && name.find("/DISABLED_") == AZStd::string::npos; + }; + + const auto getDuration = [](const AZ::rapidxml::xml_node<>* node) + { + const AZStd::string duration = node->first_attribute(Keys[DurationKey])->value(); + return AZStd::chrono::milliseconds(AZStd::stof(duration) * 1000.f); + }; + + TestRunSuite testSuite; + testSuite.m_name = testsuite_node->first_attribute(Keys[NameKey])->value(); + testSuite.m_enabled = isEnabled(testSuite.m_name); + testSuite.m_duration = getDuration(testsuite_node); + + for (auto testcase_node = testsuite_node->first_node(Keys[TestCaseKey]); testcase_node; + testcase_node = testcase_node->next_sibling()) + { + const auto getStatus = [](const AZ::rapidxml::xml_node<>* node) + { + const AZStd::string status = node->first_attribute(Keys[StatusKey])->value(); + if (status == Keys[RunKey]) + { + return TestRunStatus::Run; + } + else if (status == Keys[NotRunKey]) + { + return TestRunStatus::NotRun; + } + + throw ArtifactException(AZStd::string::format("Unexpected run status: %s", status.c_str())); + }; + + const auto getResult = [](const AZ::rapidxml::xml_node<>* node) + { + for (auto child_node = node->first_node("failure"); child_node; child_node = child_node->next_sibling()) + { + return TestRunResult::Failed; + } + + return TestRunResult::Passed; + }; + + TestRunCase testCase; + testCase.m_name = testcase_node->first_attribute(Keys[NameKey])->value(); + testCase.m_enabled = isEnabled(testCase.m_name); + testCase.m_duration = getDuration(testcase_node); + testCase.m_status = getStatus(testcase_node); + + if (testCase.m_status == TestRunStatus::Run) + { + testCase.m_result = getResult(testcase_node); + } + + testSuite.m_tests.emplace_back(AZStd::move(testCase)); + } + + testSuites.emplace_back(AZStd::move(testSuite)); + } + } + catch (const std::exception& e) + { + AZ_Error("TestRunSuitesFactory", false, e.what()); + throw ArtifactException(e.what()); + } + catch (...) + { + throw ArtifactException("An unknown error occurred parsing the XML data"); + } + + return testSuites; + } + } // namespace GTest +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.h new file mode 100644 index 0000000000..fb16c837b2 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + namespace GTest + { + //! Constructs a list of test run suite artifacts from the specified test run data. + //! @param testRunData The raw test run data in XML format. + //! @return The constructed list of test run suite artifacts. + AZStd::vector TestRunSuitesFactory(const AZStd::string& testRunData); + } // namespace GTest +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaArtifactFactory.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaArtifactFactory.h new file mode 100644 index 0000000000..f25af960b9 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaArtifactFactory.h @@ -0,0 +1,25 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +namespace TestImpact +{ + //! Constructs a list of test target meta-data artifacts from the specified master test list data. + //! @param masterTestListData The raw master test list data in JSON format. + //! @return The constructed list of test target meta-data artifacts. + TestTargetMetas TestTargetMetaMapFactory(const AZStd::string& masterTestListData); +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp new file mode 100644 index 0000000000..9135a3c683 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp @@ -0,0 +1,89 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include + +#include + +namespace TestImpact +{ + namespace + { + // Keys for pertinent JSON node and attribute names + constexpr const char* Keys[] = + { + "google", + "test", + "tests", + "suite", + "launch_method", + "test_runner", + "stand_alone", + "name" + }; + + enum + { + GoogleKey, + TestKey, + TestsKey, + SuiteKey, + LaunchMethodKey, + TestRunnerKey, + StandAloneKey, + NameKey + }; + } // namespace + + TestTargetMetaMap TestTargetMetaMapFactory(const AZStd::string& masterTestListData) + { + TestTargetMetaMap testMetas; + rapidjson::Document masterTestList; + + if (masterTestList.Parse(masterTestListData.c_str()).HasParseError()) + { + throw TestImpact::ArtifactException("Could not parse test meta-data file"); + } + + const auto tests = masterTestList[Keys[GoogleKey]][Keys[TestKey]][Keys[TestsKey]].GetArray(); + for (const auto& test : tests) + { + TestTargetMeta testMeta; + testMeta.m_suite = test[Keys[SuiteKey]].GetString(); + + if (const auto buildTypeString = test[Keys[LaunchMethodKey]].GetString(); strcmp(buildTypeString, Keys[TestRunnerKey]) == 0) + { + testMeta.m_launchMethod = LaunchMethod::TestRunner; + } + else if (strcmp(buildTypeString, Keys[StandAloneKey]) == 0) + { + testMeta.m_launchMethod = LaunchMethod::StandAlone; + } + else + { + throw(ArtifactException("Unexpected test build type")); + } + + AZStd::string name = test[Keys[NameKey]].GetString(); + AZ_TestImpact_Eval(!name.empty(), ArtifactException, "Test name field cannot be empty"); + testMetas.emplace(AZStd::move(name), AZStd::move(testMeta)); + } + + // If there's no tests in the repo then something is seriously wrong + AZ_TestImpact_Eval(!testMetas.empty(), ArtifactException, "No tests were found in the repository"); + + return testMetas; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.h new file mode 100644 index 0000000000..039a9e89e2 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.h @@ -0,0 +1,25 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +namespace TestImpact +{ + //! Constructs a list of test target meta-data artifacts from the specified master test list data. + //! @param masterTestListData The raw master test list data in JSON format. + //! @return The constructed list of test target meta-data artifacts. + TestTargetMetaMap TestTargetMetaMapFactory(const AZStd::string& masterTestListData); +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.cpp new file mode 100644 index 0000000000..935ae14452 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.cpp @@ -0,0 +1,22 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + BuildTargetDescriptor::BuildTargetDescriptor(BuildMetaData&& buildMetaData, TargetSources&& sources) + : m_buildMetaData(AZStd::move(buildMetaData)) + , m_sources(AZStd::move(sources)) + { + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.h new file mode 100644 index 0000000000..e247b663f2 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.h @@ -0,0 +1,55 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace TestImpact +{ + //! Pairing between a given autogen input source and the generated output source(s). + struct AutogenPairs + { + AZStd::string m_input; + AZStd::vector m_outputs; + }; + + using AutogenSources = AZStd::vector; + + //! Representation of a given built target's source list. + struct TargetSources + { + AZStd::vector m_staticSources; //!< Source files used to build this target (if any). + AutogenSources m_autogenSources; //!< Autogen source files (if any). + }; + + //! Representation of a given build target's basic build infotmation. + struct BuildMetaData + { + AZStd::string m_name; //!< Build target name. + AZStd::string m_outputName; //!< Output name (sans extension) of build target binary. + AZ::IO::Path m_path; //!< Path to build target location in source tree (relative to repository root). + }; + + //! Artifact produced by the build system for each build target. Contains source and output information about said targets. + struct BuildTargetDescriptor + { + BuildTargetDescriptor() = default; + BuildTargetDescriptor(BuildMetaData&& buildMetaData, TargetSources&& sources); + + BuildMetaData m_buildMetaData; + TargetSources m_sources; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactProductionTargetDescriptor.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactProductionTargetDescriptor.cpp new file mode 100644 index 0000000000..1b9cb70dab --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactProductionTargetDescriptor.cpp @@ -0,0 +1,21 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + ProductionTargetDescriptor::ProductionTargetDescriptor(BuildTargetDescriptor&& buildTargetDescriptor) + : BuildTargetDescriptor(AZStd::move(buildTargetDescriptor)) + { + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactProductionTargetDescriptor.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactProductionTargetDescriptor.h new file mode 100644 index 0000000000..e9fccbc51a --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactProductionTargetDescriptor.h @@ -0,0 +1,25 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Artifact produced by the target artifact compiler that represents a production build target in the repository. + struct ProductionTargetDescriptor + : public BuildTargetDescriptor + { + ProductionTargetDescriptor(BuildTargetDescriptor&& buildTarget); + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTargetDescriptorCompiler.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTargetDescriptorCompiler.cpp new file mode 100644 index 0000000000..e13b2083ae --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTargetDescriptorCompiler.cpp @@ -0,0 +1,45 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include + +namespace TestImpact +{ + AZStd::tuple, AZStd::vector> CompileTargetDescriptors( + AZStd::vector&& buildTargets, TestTargetMetaMap&& testTargetMetaMap) + { + AZ_TestImpact_Eval(!buildTargets.empty(), ArtifactException, "Build target descriptor list cannot be null"); + AZ_TestImpact_Eval(!testTargetMetaMap.empty(), ArtifactException, "Test target meta map cannot be null"); + + AZStd::tuple, AZStd::vector> outputTargets; + auto& [productionTargets, testTargets] = outputTargets; + + for (auto&& buildTarget : buildTargets) + { + // If this build target has an associated test artifact then it is a test target, otherwise it is a production target + if (auto&& testTargetMeta = testTargetMetaMap.find(buildTarget.m_buildMetaData.m_name); + testTargetMeta != testTargetMetaMap.end()) + { + testTargets.emplace_back(TestTargetDescriptor(AZStd::move(buildTarget), AZStd::move(testTargetMeta->second))); + } + else + { + productionTargets.emplace_back(ProductionTargetDescriptor(AZStd::move(buildTarget))); + } + } + + return outputTargets; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTargetDescriptorCompiler.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTargetDescriptorCompiler.h new file mode 100644 index 0000000000..608fa9d072 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTargetDescriptorCompiler.h @@ -0,0 +1,31 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace TestImpact +{ + //! Compiles the production target artifacts and test target artifactss from the supplied build target artifacts and test target meta + //! map artifact. + //! @param buildTargets The list of build target artifacts to be sorted into production and test artifact types. + //! @param testTargetMetaMap The map of test target meta artifacts containing the additional meta-data about each test target. + //! @return A tuple containing the production artifacts and test artifacts. + AZStd::tuple, AZStd::vector> CompileTargetDescriptors( + AZStd::vector&& buildTargets, TestTargetMetaMap&& testTargetMetaMap); +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetDescriptor.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetDescriptor.cpp new file mode 100644 index 0000000000..f45e61c554 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetDescriptor.cpp @@ -0,0 +1,22 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + TestTargetDescriptor::TestTargetDescriptor(BuildTargetDescriptor&& buildTarget, TestTargetMeta&& testTargetMeta) + : BuildTargetDescriptor(AZStd::move(buildTarget)) + , m_testMetaData(AZStd::move(testTargetMeta)) + { + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetDescriptor.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetDescriptor.h new file mode 100644 index 0000000000..7044c963e8 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetDescriptor.h @@ -0,0 +1,28 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Artifact produced by the target artifact compiler that represents a test build target in the repository. + struct TestTargetDescriptor + : public BuildTargetDescriptor + { + TestTargetDescriptor(BuildTargetDescriptor&& buildTarget, TestTargetMeta&& testTargetMeta); + + TestTargetMeta m_testMetaData; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetMeta.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetMeta.h new file mode 100644 index 0000000000..57ae2fe894 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetMeta.h @@ -0,0 +1,36 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Method used to launch the test target. + enum class LaunchMethod : bool + { + TestRunner, //!< Target is launched through a separate test runner binary. + StandAlone //!< Target is launched directly by itself. + }; + + //! Artifact produced by the build system for each test target containing the additional meta-data about the test. + struct TestTargetMeta + { + AZStd::string m_suite; + LaunchMethod m_launchMethod = LaunchMethod::TestRunner; + }; + + //! Map between test target name and test target meta-data. + using TestTargetMetaMap = AZStd::unordered_map; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/TestImpactArtifactException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/TestImpactArtifactException.h new file mode 100644 index 0000000000..5d2a4fabd2 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/TestImpactArtifactException.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Exception for artifacts and artifact parsing operations. + class ArtifactException + : public Exception + { + public: + using Exception::Exception; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dummy.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dummy.cpp deleted file mode 100644 index 1ac4c2487f..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dummy.cpp +++ /dev/null @@ -1,11 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Common/Clang/testimpactframework_clang.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Common/Clang/testimpactframework_clang.cmake new file mode 100644 index 0000000000..af1177e359 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Common/Clang/testimpactframework_clang.cmake @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(LY_COMPILE_OPTIONS PUBLIC -fexceptions) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Common/MSVC/testimpactframework_msvc.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Common/MSVC/testimpactframework_msvc.cmake new file mode 100644 index 0000000000..79d4b190a2 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Common/MSVC/testimpactframework_msvc.cmake @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(LY_COMPILE_OPTIONS PUBLIC /EHsc) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Dummy_Windows.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Dummy_Windows.cpp deleted file mode 100644 index 1ac4c2487f..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Dummy_Windows.cpp +++ /dev/null @@ -1,11 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Handle.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Handle.h new file mode 100644 index 0000000000..3930732bd0 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Handle.h @@ -0,0 +1,92 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! OS function to cleanup handle + using CleanupFunc = BOOL (*)(HANDLE); + + //! RAII wrapper around OS handles. + template + class Handle + { + public: + Handle() = default; + explicit Handle(HANDLE handle); + ~Handle(); + + operator HANDLE&(); + PHANDLE operator&(); + HANDLE& operator=(HANDLE handle); + void Close(); + + private: + HANDLE m_handle = INVALID_HANDLE_VALUE; + }; + + template + Handle::Handle(HANDLE handle) + : m_handle(handle) + { + } + + template + Handle::~Handle() + { + Close(); + } + + template + Handle::operator HANDLE&() + { + return m_handle; + } + + template + PHANDLE Handle::operator&() + { + return &m_handle; + } + + template + HANDLE& Handle::operator=(HANDLE handle) + { + // Setting the handle to INVALID_HANDLE_VALUE will close the handle + if (handle == INVALID_HANDLE_VALUE && m_handle != INVALID_HANDLE_VALUE) + { + Close(); + } + else + { + m_handle = handle; + } + + return m_handle; + } + + template + void Handle::Close() + { + if (m_handle != INVALID_HANDLE_VALUE) + { + CleanupFuncT(m_handle); + m_handle = INVALID_HANDLE_VALUE; + } + } + + using ObjectHandle = Handle; + using WaitHandle = Handle; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Pipe.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Pipe.cpp new file mode 100644 index 0000000000..b872487942 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Pipe.cpp @@ -0,0 +1,67 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "TestImpactWin32_Pipe.h" + +#include + +#include +#include + +namespace TestImpact +{ + Pipe::Pipe(SECURITY_ATTRIBUTES& sa, HANDLE& stdChannel) + { + if (!CreatePipe(&m_parent, &m_child, &sa, 0)) + { + throw ProcessException("Couldn't create pipe"); + } + + SetHandleInformation(m_parent, HANDLE_FLAG_INHERIT, 0); + stdChannel = m_child; + } + + void Pipe::ReleaseChild() + { + m_child.Close(); + } + + void Pipe::EmptyPipe() + { + DWORD bytesAvailable = 0; + while (PeekNamedPipe(m_parent, NULL, 0, NULL, &bytesAvailable, NULL) && bytesAvailable > 0) + { + // Grow the buffer by the number of bytes available in the pipe and append the new data + DWORD bytesRead; + const size_t currentSize = m_buffer.size(); + m_buffer.resize(m_buffer.size() + bytesAvailable); + if (!ReadFile(m_parent, m_buffer.data() + currentSize, bytesAvailable, &bytesRead, NULL) || bytesRead == 0) + { + throw ProcessException("Couldn't read child output from pipe"); + } + } + } + + AZStd::string Pipe::GetContentsAndClearInternalBuffer() + { + EmptyPipe(); + AZStd::string contents; + + if (m_buffer.size() > 0) + { + contents = AZStd::string(m_buffer.begin(), m_buffer.end()); + m_buffer.clear(); + } + + return contents; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Pipe.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Pipe.h new file mode 100644 index 0000000000..d7b0d616f7 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Pipe.h @@ -0,0 +1,52 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include "TestImpactWin32_Handle.h" + +#include +#include +#include + +namespace TestImpact +{ + //! RAII wrapper around OS pipes. + //! Used to connect the standard output and standard error of the child process to a sink accessible to the + //! parent process to allow the parent process to read the output(s) of the child process. + class Pipe + { + public: + Pipe(SECURITY_ATTRIBUTES& sa, HANDLE& stdChannel); + Pipe(Pipe&& other) = delete; + Pipe(Pipe& other) = delete; + Pipe& operator=(Pipe& other) = delete; + Pipe& operator=(Pipe&& other) = delete; + + //! Releases the child end of the pipe (not needed once parent has their end). + void ReleaseChild(); + + //! Empties the contents of the pipe into the internal buffer. + void EmptyPipe(); + + //! Empties the contents of the pipe into a string, clearing the internal buffer. + AZStd::string GetContentsAndClearInternalBuffer(); + + private: + // Parent and child process ends of pipe + ObjectHandle m_parent; + ObjectHandle m_child; + + // Buffer for emptying pipe upon child processes exit + AZStd::vector m_buffer; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.cpp new file mode 100644 index 0000000000..0a8a8643f3 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.cpp @@ -0,0 +1,259 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "TestImpactWin32_Process.h" + +#include + +#include + +namespace TestImpact +{ + // Note: this is called from an OS thread + VOID ProcessWin32::ProcessExitCallback(PVOID processPtr, [[maybe_unused]] BOOLEAN EventSignalled) + { + // Lock the process destructor from being entered from the client thread + AZStd::lock_guard lifeLock(m_lifeCycleMutex); + + ProcessId id = reinterpret_cast(processPtr); + auto process = m_masterProcessList[id]; + + // Check that the process hasn't already been destructed from the client thread + if (process && process->m_isRunning) + { + // Lock state access and/or mutation from the client thread + AZStd::lock_guard stateLock(process->m_stateMutex); + process->RetrieveOSReturnCodeAndCleanUpProcess(); + } + } + + ProcessWin32::ProcessWin32(const ProcessInfo& processInfo) + : Process(processInfo) + { + AZStd::string args(m_processInfo.GetProcessPath().String()); + + if (m_processInfo.GetStartupArgs().length()) + { + args = AZStd::string::format("%s %s", args.c_str(), m_processInfo.GetStartupArgs().c_str()); + } + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = nullptr; + sa.bInheritHandle = IsPiping(); + + STARTUPINFO si; + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + + CreatePipes(sa, si); + + if (!CreateProcess( + NULL, + &args[0], + NULL, + NULL, + IsPiping(), + CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW, + NULL, NULL, + &si, &pi)) + { + throw ProcessException("Couldn't create process"); + } + + ReleaseChildPipes(); + + m_process = pi.hProcess; + m_thread = pi.hThread; + m_isRunning = true; + + { + // Lock reading of the master process list from the OS thread + AZStd::lock_guard lock(m_lifeCycleMutex); + + // Register this process with a unique id in the master process list + m_uniqueId = m_uniqueIdCounter++; + m_masterProcessList[m_uniqueId] = this; + } + + // Register the process exit signal callback + if (!RegisterWaitForSingleObject( + &m_waitCallback, + pi.hProcess, + ProcessExitCallback, + reinterpret_cast(m_uniqueId), + INFINITE, + WT_EXECUTEONLYONCE)) + { + throw ProcessException("Couldn't register wait object for process exit event"); + } + } + + bool ProcessWin32::IsPiping() const + { + return m_processInfo.ParentHasStdOutput() || m_processInfo.ParentHasStdError(); + } + + void ProcessWin32::CreatePipes(SECURITY_ATTRIBUTES& sa, STARTUPINFO& si) + { + if (IsPiping()) + { + si.dwFlags = STARTF_USESTDHANDLES; + + if (m_processInfo.ParentHasStdOutput()) + { + m_stdOutPipe.emplace(sa, si.hStdOutput); + } + + if (m_processInfo.ParentHasStdError()) + { + m_stdErrPipe.emplace(sa, si.hStdError); + } + } + } + + void ProcessWin32::ReleaseChildPipes() + { + if (m_stdOutPipe) + { + m_stdOutPipe->ReleaseChild(); + } + + if (m_stdErrPipe) + { + m_stdErrPipe->ReleaseChild(); + } + } + + void ProcessWin32::EmptyPipes() + { + if (m_stdOutPipe) + { + m_stdOutPipe->EmptyPipe(); + } + + if (m_stdErrPipe) + { + m_stdErrPipe->EmptyPipe(); + } + } + + AZStd::optional ProcessWin32::ConsumeStdOut() + { + if (m_stdOutPipe) + { + AZStd::string contents = m_stdOutPipe->GetContentsAndClearInternalBuffer(); + if (!contents.empty()) + { + return contents; + } + } + + return AZStd::nullopt; + } + + AZStd::optional ProcessWin32::ConsumeStdErr() + { + if (m_stdErrPipe) + { + AZStd::string contents = m_stdErrPipe->GetContentsAndClearInternalBuffer(); + if (!contents.empty()) + { + return contents; + } + } + + return AZStd::nullopt; + } + + void ProcessWin32::Terminate(ReturnCode returnCode) + { + // Lock process cleanup from the OS thread + AZStd::lock_guard lock(m_stateMutex); + + if (m_isRunning) + { + // Cancel the callback so we can wait for the signal ourselves + // Note: we keep the state mutex locked as closing the callback is not guaranteed to be instantaneous + m_waitCallback.Close(); + + // Terminate the process and set the error code to the terminate code + TerminateProcess(m_process, returnCode); + SetReturnCodeAndCleanUpProcesses(returnCode); + } + } + + bool ProcessWin32::IsRunning() const + { + return m_isRunning; + } + + void ProcessWin32::BlockUntilExit() + { + // Lock process cleanup from the OS thread + AZStd::lock_guard lock(m_stateMutex); + + if (m_isRunning) + { + // Cancel the callback so we can wait for the signal ourselves + // Note: we keep the state mutex locked as closing the callback is not guaranteed to be instantaneous + m_waitCallback.Close(); + + if (IsPiping()) + { + // This process will be blocked from exiting if pipe not emptied so will deadlock if we wait + // indefintely whilst there is still output in the pipes so instead keep waiting and checking + // if the pipes need emptying until the process exits + while (WAIT_OBJECT_0 != WaitForSingleObject(m_process, 1)) + { + EmptyPipes(); + } + } + else + { + // No possibility of pipe deadlocking, safe to wait indefinitely for process exit + WaitForSingleObject(m_process, INFINITE); + } + + // Now that the this process has definately exited we are safe to clean up + RetrieveOSReturnCodeAndCleanUpProcess(); + } + } + + void ProcessWin32::RetrieveOSReturnCodeAndCleanUpProcess() + { + DWORD returnCode; + GetExitCodeProcess(m_process, &returnCode); + SetReturnCodeAndCleanUpProcesses(returnCode); + } + + void ProcessWin32::SetReturnCodeAndCleanUpProcesses(ReturnCode returnCode) + { + m_returnCode = returnCode; + m_process.Close(); + m_thread.Close(); + m_waitCallback.Close(); + m_isRunning = false; + } + + ProcessWin32::~ProcessWin32() + { + // Lock the process exit signal callback from being entered OS thread + AZStd::lock_guard lock(m_lifeCycleMutex); + + // Remove this process from the master list so the process exit signal doesn't attempt to cleanup + // this process if it is deleted client side + m_masterProcessList[m_uniqueId] = nullptr; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.h new file mode 100644 index 0000000000..a29fecada1 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.h @@ -0,0 +1,101 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include "TestImpactWin32_Handle.h" +#include "TestImpactWin32_Pipe.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace TestImpact +{ + //! Platform-specific implementation of Process. + class ProcessWin32 + : public Process + { + public: + explicit ProcessWin32(const ProcessInfo& processInfo); + ProcessWin32(ProcessWin32&& other) = delete; + ProcessWin32(ProcessWin32& other) = delete; + ProcessWin32& operator=(ProcessWin32& other) = delete; + ProcessWin32& operator=(ProcessWin32&& other) = delete; + ~ProcessWin32(); + + // Process overrides... + void Terminate(ReturnCode returnCode) override; + void BlockUntilExit() override; + bool IsRunning() const override; + AZStd::optional ConsumeStdOut() override; + AZStd::optional ConsumeStdErr() override; + + private: + //! Callback for process exit signal. + static VOID ProcessExitCallback(PVOID processPtr, BOOLEAN EventSignalled); + + //! Retrieves the return code and cleans up the OS handles. + void RetrieveOSReturnCodeAndCleanUpProcess(); + + //! Sets the return code and cleans up the OS handles + void SetReturnCodeAndCleanUpProcesses(ReturnCode returnCode); + + //! Returns true if either stdout or stderr is beign redirected. + bool IsPiping() const; + + //! Creates the parent and child pipes for stdout and/or stderr. + void CreatePipes(SECURITY_ATTRIBUTES& sa, STARTUPINFO& si); + + //! Empties all pipes so the process can exit without deadlocking. + void EmptyPipes(); + + //! Releases the child end of the stdout and/or stderr pipes/ + void ReleaseChildPipes(); + + // Flag to determine whether or not the process is in flight + AZStd::atomic_bool m_isRunning = false; + + // Unique id assigned to this process (not the same as the id assigned by the client in the ProcessInfo class) + // as used in the master process list + size_t m_uniqueId = 0; + + // Handles to OS process + ObjectHandle m_process; + ObjectHandle m_thread; + + // Handle to process exit signal callback + WaitHandle m_waitCallback; + + // Process to parent standard output piping + AZStd::optional m_stdOutPipe; + AZStd::optional m_stdErrPipe; + + // Mutex protecting process state access/mutation from the OS thread and client thread + mutable AZStd::mutex m_stateMutex; + + // Mutex keeping the process life cycles in sync between the OS thread and client thread + inline static AZStd::mutex m_lifeCycleMutex; + + // Unique counter to give each launched process a unique id + inline static size_t m_uniqueIdCounter = 1; + + // Master process list used to ensure consistency of process lifecycles between OS thread and client thread + inline static std::unordered_map m_masterProcessList; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_ProcessLauncher.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_ProcessLauncher.cpp new file mode 100644 index 0000000000..d798e4ff7b --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_ProcessLauncher.cpp @@ -0,0 +1,24 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "TestImpactWin32_Process.h" + +#include +#include + +namespace TestImpact +{ + AZStd::unique_ptr LaunchProcess(const ProcessInfo& processInfo) + { + return AZStd::make_unique(processInfo); + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/platform_windows_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/platform_windows_files.cmake index 9b83a02475..32f996cdf8 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/platform_windows_files.cmake +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -10,5 +10,10 @@ # set(FILES - Dummy_Windows.cpp + Process/TestImpactWin32_ProcessLauncher.cpp + Process/TestImpactWin32_Process.cpp + Process/TestImpactWin32_Process.h + Process/TestImpactWin32_Handle.h + Process/TestImpactWin32_Pipe.cpp + Process/TestImpactWin32_Pipe.h ) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h new file mode 100644 index 0000000000..d9dd4870dd --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h @@ -0,0 +1,139 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +#include +#include + +namespace TestImpact +{ + //! Result of a job that was run. + enum class JobResult + { + NotExecuted, //!< The job was not executed (e.g. the job runner terminated before the job could be executed). + FailedToExecute, //!< The job failed to execute (e.g. due to the arguments used to execute the job being invalid). + Terminated, //!< The job was terminated by the job runner (e.g. job or runner timeout exceeded while job was in-flight). + ExecutedWithFailure, //!< The job was executed but exited in an erroneous state (the underlying process returned non-zero). + ExecutedWithSuccess //!< The job was executed and exited in a successful state (the underlying processes returned zero). + }; + + //! The meta-data for a given job. + struct JobMeta + { + JobResult m_result = JobResult::NotExecuted; + AZStd::optional + m_startTime; //!< The time, relative to the job runner start, that this job started. + AZStd::optional m_duration; //!< The duration that this job took to complete. + AZStd::optional m_returnCode; //!< The return code of the underlying processes of this job. + }; + + //! Representation of a unit of work to be performed by a process. + //! @tparam JobInfoT The JobInfo structure containing the information required to run this job. + //! @tparam JobPayloadT The resulting output of the processed artifact produced by this job. + template + class Job + { + public: + using Info = JobInfoT; + using Payload = JobPayloadT; + + //! Constructor with r-values for the specific use case of the job runner. + Job(Info jobInfo, JobMeta&& jobMeta, AZStd::optional&& payload); + + //! Returns the job info associated with this job. + const Info& GetJobInfo() const; + + //! Returns the result of this job. + JobResult GetResult() const; + + //! Returns the start time, relative to the job runner start, that this job started. + AZStd::chrono::high_resolution_clock::time_point GetStartTime() const; + + //! Returns the end time, relative to the job runner start, that this job ended. + AZStd::chrono::high_resolution_clock::time_point GetEndTime() const; + + //! Returns the duration that this job took to complete. + AZStd::chrono::milliseconds GetDuration() const; + + //! Returns the return code of the underlying processes of this job. + AZStd::optional GetReturnCode() const; + + //! Returns the payload produced by this job. + const AZStd::optional& GetPayload() const; + + private: + Info m_jobInfo; + JobMeta m_meta; + AZStd::optional m_payload; + }; + + template + Job::Job(Info jobInfo, JobMeta&& jobMeta, AZStd::optional&& payload) + : m_jobInfo(jobInfo) + , m_meta(AZStd::move(jobMeta)) + , m_payload(AZStd::move(payload)) + { + } + + template + const JobInfoT& Job::GetJobInfo() const + { + return m_jobInfo; + } + + template + JobResult Job::GetResult() const + { + return m_meta.m_result; + } + + template + AZStd::optional Job::GetReturnCode() const + { + return m_meta.m_returnCode; + } + + template + AZStd::chrono::high_resolution_clock::time_point Job::GetStartTime() const + { + return m_meta.m_startTime.value_or(AZStd::chrono::high_resolution_clock::time_point()); + } + + template + AZStd::chrono::high_resolution_clock::time_point Job::GetEndTime() const + { + if (m_meta.m_startTime.has_value() && m_meta.m_duration.has_value()) + { + return m_meta.m_startTime.value() + m_meta.m_duration.value(); + } + else + { + return AZStd::chrono::high_resolution_clock::time_point(); + } + } + + template + AZStd::chrono::milliseconds Job::GetDuration() const + { + return m_meta.m_duration.value_or(AZStd::chrono::milliseconds{0}); + } + + template + const AZStd::optional& Job::GetPayload() const + { + return m_payload; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobInfo.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobInfo.h new file mode 100644 index 0000000000..fd9d76652a --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobInfo.h @@ -0,0 +1,73 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Per-job information to configure and run jobs and process the resulting artifacts. + //! @tparam AdditionalInfo Additional information to be provided to each job to be consumed by client. + template + class JobInfo + : public AdditionalInfo + { + public: + using IdType = size_t; + + //! Client-provided identifier to distinguish between different jobs. + //! @note Ids of different job types are not interchangeable. + struct Id + { + IdType m_value; + }; + + //! Constructs the job information with any additional information required by the job. + //! @param jobId The client-provided unique identifier for the job. + //! @param args The arguments used to launch the process running the job. + //! @param additionalInfo The arguments to be provided to the additional information data structure. + template + JobInfo(Id jobId, const AZStd::string& args, AdditionalInfoArgs&&... additionalInfo); + + //! Returns the id of this job. + Id GetId() const; + + //! Returns the command arguments used to execute this job. + const AZStd::string& GetArgs() const; + + private: + Id m_id; + AZStd::string m_args; + }; + + template + template + JobInfo::JobInfo(Id jobId, const AZStd::string& args, AdditionalInfoArgs&&... additionalInfo) + : AdditionalInfo{std::forward(additionalInfo)...} + , m_id(jobId) + , m_args(args) + { + } + + template + typename JobInfo::Id JobInfo::GetId() const + { + return m_id; + } + + template + const AZStd::string& JobInfo::GetArgs() const + { + return m_args; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h new file mode 100644 index 0000000000..8b9e4fa69e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h @@ -0,0 +1,190 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace TestImpact +{ + //! Callback for job completion/failure. + //! @param jobInfo The job information associated with this job. + //! @param meta The meta-data about the job run. + //! @param std The standard output and standard error of the process running the job. + template + using JobCallback = AZStd::function; + + //! The payloads produced by the job-specific payload producer in the form of a map associating each job id with the job's payload. + template + using PayloadMap = AZStd::unordered_map>; + + //! The map used by the client to associate the job information and meta-data with the job ids. + template + using JobDataMap = AZStd::unordered_map>; + + //! The callback for producing the payloads for the jobs after all jobs have finished executing. + //! @param jobInfos The information for each job run. + //! @param jobDataMap The job data (in the form of job info and meta-data) for each job run. + template + using PayloadMapProducer = AZStd::function(const JobDataMap& jobDataMap)>; + + //! Generic job runner that launches a process for each job, records metrics about each job run and hands the payload artifacts + //! produced by each job to the client before compositing the metrics and payload artifacts for each job into a single interface + //! to be consumed by the client. + template + class JobRunner + { + public: + //! Constructs the job runner with the specified parameters to constrain job runs. + //! @param stdOutRouting The standard output routing to be specified for all jobs. + //! @param stdErrRouting The standard error routing to be specified for all jobs. + //! @param maxConcurrentProcesses he maximum number of concurrent jobs in-flight. + //! @param processTimeout The maximum duration a job may be in-flight before being forcefully terminated (nullopt if no timeout). + //! @param scheduleTimeout The maximum duration the scheduler may run before forcefully terminating all in-flight jobs (nullopt if + //! no timeout). + JobRunner( + StdOutputRouting stdOutRouting, + StdErrorRouting stdErrRouting, + size_t maxConcurrentProcesses, + AZStd::optional processTimeout, + AZStd::optional scheduleTimeout); + + //! Executes the specified jobs and returns the products of their labor. + //! @note: the job and payload callbacks are specified here rather than in the constructor to allow clients to use capturing lambdas + //! should they desire to. + //! @param jobs The arguments (and other pertinent information) required for each job to be run. + //! @param jobCallback The client callback to be called when each job changes state. + //! @param payloadMapProducer The client callback to be called when all jobs have finished to transform the work produced by each + //! job into the desired output. + AZStd::vector Execute( + const AZStd::vector& jobs, JobCallback jobCallback, + PayloadMapProducer payloadMapProducer); + + private: + size_t m_maxConcurrentProcesses = 0; //!< Maximum number of concurrent jobs being executed at a given time. + StdOutputRouting m_stdOutRouting; //!< Standard output routing from each job process to job runner. + StdErrorRouting m_stdErrRouting; //!< Standard error routing from each job process to job runner + AZStd::optional m_jobTimeout; //!< Maximum time a job can run for before being forcefully terminated. + AZStd::optional m_runnerTimeout; //!< Maximum time the job runner can run before forcefully terminating all in-flight jobs and shutting down. + }; + + template + JobRunner::JobRunner( + StdOutputRouting stdOutRouting, + StdErrorRouting stdErrRouting, + size_t maxConcurrentProcesses, + AZStd::optional jobTimeout, + AZStd::optional runnerTimeout) + : m_maxConcurrentProcesses(maxConcurrentProcesses) + , m_stdOutRouting(stdOutRouting) + , m_stdErrRouting(stdErrRouting) + , m_jobTimeout(jobTimeout) + , m_runnerTimeout(runnerTimeout) + { + } + + template + AZStd::vector JobRunner::Execute( + const AZStd::vector& jobInfos, + JobCallback jobCallback, + PayloadMapProducer payloadMapProducer) + { + AZStd::vector processes; + AZStd::unordered_map> metas; + AZStd::vector jobs; + jobs.reserve(jobInfos.size()); + processes.reserve(jobInfos.size()); + + // Transform the job infos into the underlying process infos required for each job + for (size_t jobIndex = 0; jobIndex < jobInfos.size(); jobIndex++) + { + const auto* jobInfo = &jobInfos[jobIndex]; + const auto jobId = jobInfo->GetId().m_value; + metas.emplace(jobId, AZStd::pair{JobMeta{}, jobInfo}); + processes.emplace_back(jobId, m_stdOutRouting, m_stdErrRouting, jobInfo->GetArgs()); + } + + // Wrapper around low-level process launch callback to gather job meta-data and present a simplified callback interface to the client + const auto processLaunchCallback = [&jobCallback, &jobInfos, &metas]( + TestImpact::ProcessId pid, + TestImpact::LaunchResult launchResult, + AZStd::chrono::high_resolution_clock::time_point createTime) + { + auto& [meta, jobInfo] = metas.at(pid); + if (launchResult == LaunchResult::Failure) + { + meta.m_result = JobResult::FailedToExecute; + return jobCallback(*jobInfo, meta, {}); + } + else + { + meta.m_startTime = createTime; + return CallbackResult::Continue; + } + }; + + // Wrapper around low-level process exit callback to gather job meta-data and present a simplified callback interface to the client + const auto processExitCallback = [&jobCallback, &jobInfos, &metas]( + TestImpact::ProcessId pid, + TestImpact::ExitCondition exitCondition, + TestImpact::ReturnCode returnCode, + TestImpact::StdContent&& std, + AZStd::chrono::high_resolution_clock::time_point exitTime) + { + auto& [meta, jobInfo] = metas.at(pid); + meta.m_returnCode = returnCode; + meta.m_duration = AZStd::chrono::duration_cast(exitTime - *meta.m_startTime); + if (exitCondition == ExitCondition::Gracefull && returnCode == 0) + { + meta.m_result = JobResult::ExecutedWithSuccess; + } + else if (exitCondition == ExitCondition::Terminated || exitCondition == ExitCondition::Timeout) + { + meta.m_result = JobResult::Terminated; + } + else + { + meta.m_result = JobResult::ExecutedWithFailure; + } + + return jobCallback(*jobInfo, meta, AZStd::move(std)); + }; + + // Schedule all jobs for execution + ProcessScheduler scheduler( + processes, + processLaunchCallback, + processExitCallback, + m_maxConcurrentProcesses, + m_jobTimeout, + m_runnerTimeout + ); + + // Hand off the jobs to the client for payload generation + auto payloadMap = payloadMapProducer(metas); + + // Unpack the payload map produced by the client into a vector of jobs containing the job data and payload for each job + for (const auto& jobInfo : jobInfos) + { + const auto jobId = jobInfo.GetId().m_value; + jobs.emplace_back(JobT(jobInfo, AZStd::move(metas.at(jobId).first), AZStd::move(payloadMap[jobId]))); + } + + return jobs; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp new file mode 100644 index 0000000000..15230b1a46 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp @@ -0,0 +1,270 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include +#include + +namespace TestImpact +{ + struct ProcessScheduler::ProcessInFlight + { + AZStd::unique_ptr m_process; + AZStd::optional m_startTime; + AZStd::string m_stdOutput; + AZStd::string m_stdError; + }; + + ProcessScheduler::ProcessScheduler( + const AZStd::vector& processes, + const ProcessLaunchCallback& processLaunchCallback, + const ProcessExitCallback& processExitCallback, + size_t maxConcurrentProcesses, + AZStd::optional processTimeout, + AZStd::optional scheduleTimeout) + : m_processCreateCallback(processLaunchCallback) + , m_processExitCallback(processExitCallback) + , m_processTimeout(processTimeout) + , m_scheduleTimeout(scheduleTimeout) + , m_startTime(AZStd::chrono::high_resolution_clock::now()) + { + AZ_TestImpact_Eval(maxConcurrentProcesses != 0, ProcessException, "Max Number of concurrent processes in flight cannot be 0"); + AZ_TestImpact_Eval(!processes.empty(), ProcessException, "Number of processes to launch cannot be 0"); + AZ_TestImpact_Eval( + !m_processTimeout.has_value() || m_processTimeout->count() > 0, ProcessException, + "Process timeout must be empty or non-zero value"); + AZ_TestImpact_Eval( + !m_scheduleTimeout.has_value() || m_scheduleTimeout->count() > 0, ProcessException, + "Scheduler timeout must be empty or non-zero value"); + + const size_t numConcurrentProcesses = AZStd::min(processes.size(), maxConcurrentProcesses); + m_processPool.resize(numConcurrentProcesses); + + for (const auto& process : processes) + { + m_processQueue.emplace(process); + } + + for (auto& process : m_processPool) + { + if (PopAndLaunch(process) == CallbackResult::Abort) + { + TerminateAllProcesses(ExitCondition::Terminated); + return; + } + } + + MonitorProcesses(); + } + + ProcessScheduler::~ProcessScheduler() + { + TerminateAllProcesses(ExitCondition::Terminated); + } + + void ProcessScheduler::MonitorProcesses() + { + while (true) + { + // Check to see whether or not the scheduling has exceeded its specified runtime + if (m_scheduleTimeout.has_value()) + { + const auto shedulerRunTime = AZStd::chrono::milliseconds(AZStd::chrono::high_resolution_clock::now() - m_startTime); + + if (shedulerRunTime > m_scheduleTimeout) + { + // Runtime exceeded, terminate all proccesses and schedule no further + TerminateAllProcesses(ExitCondition::Timeout); + return; + } + } + + // Flag to determine whether or not there are currently any processes in-flight + bool processesInFlight = false; + + // Loop round the process pool and visit round robin queued up processes for launch + for (auto& processInFlight : m_processPool) + { + if (processInFlight.m_process) + { + // Process is alive (note: not necessarilly currently running) + AccumulateProcessStdContent(processInFlight); + const ProcessId processId = processInFlight.m_process->GetProcessInfo().GetId(); + + if (!processInFlight.m_process->IsRunning()) + { + // Process has exited of its own accord + const ReturnCode returnCode = processInFlight.m_process->GetReturnCode().value(); + processInFlight.m_process.reset(); + const auto exitTime = AZStd::chrono::high_resolution_clock::now(); + + // Inform the client that the processes has exited + if (CallbackResult::Abort == m_processExitCallback( + processId, + ExitCondition::Gracefull, + returnCode, + ConsumeProcessStdContent(processInFlight), + exitTime)) + { + // Client chose to abort the scheduler + TerminateAllProcesses(ExitCondition::Terminated); + return; + } + else if (!m_processQueue.empty()) + { + // This slot in the pool is free so launch one of the processes waiting in the queue + if (PopAndLaunch(processInFlight) == CallbackResult::Abort) + { + // Client chose to abort the scheduler + TerminateAllProcesses(ExitCondition::Terminated); + return; + } + else + { + // We know from the above PopAndLaunch there is at least one process in-flight this iteration + processesInFlight = true; + } + } + } + else + { + // Process is still in-flight + const auto exitTime = AZStd::chrono::high_resolution_clock::now(); + const auto runTime = AZStd::chrono::milliseconds(exitTime - processInFlight.m_startTime.value()); + + // Check to see whether or not the processes has exceeded its specified flight time + if (m_processTimeout.has_value() && runTime > m_processTimeout) + { + processInFlight.m_process->Terminate(ProcessTimeoutErrorCode); + const ReturnCode returnCode = processInFlight.m_process->GetReturnCode().value(); + processInFlight.m_process.reset(); + + if (CallbackResult::Abort == m_processExitCallback( + processId, + ExitCondition::Timeout, + returnCode, + ConsumeProcessStdContent(processInFlight), + exitTime)) + { + // Flight time exceeded, terminate this process + TerminateAllProcesses(ExitCondition::Terminated); + return; + } + } + + // We know that at least this process is in-flight this iteration + processesInFlight = true; + } + } + else + { + // Queue is empty, no more processes to launch + if (!m_processQueue.empty()) + { + if (PopAndLaunch(processInFlight) == CallbackResult::Abort) + { + // Client chose to abort the scheduler + TerminateAllProcesses(ExitCondition::Terminated); + return; + } + else + { + // We know from the above PopAndLaunch there is at least one process in-flight this iteration + processesInFlight = true; + } + } + } + } + + if (!processesInFlight) + { + break; + } + } + } + + CallbackResult ProcessScheduler::PopAndLaunch(ProcessInFlight& processInFlight) + { + auto processInfo = m_processQueue.front(); + m_processQueue.pop(); + const auto createTime = AZStd::chrono::high_resolution_clock::now(); + LaunchResult createResult = LaunchResult::Success; + + try + { + processInFlight.m_process = LaunchProcess(AZStd::move(processInfo)); + processInFlight.m_startTime = createTime; + } + catch (ProcessException& e) + { + AZ_Warning("ProcessScheduler", false, e.what()); + createResult = LaunchResult::Failure; + } + + return m_processCreateCallback(processInfo.GetId(), createResult, createTime); + } + + void ProcessScheduler::AccumulateProcessStdContent(ProcessInFlight& processInFlight) + { + // Accumulate the stdout/stderr so we don't deadlock with the process waiting for the pipe to empty before finishing + processInFlight.m_stdOutput += processInFlight.m_process->ConsumeStdOut().value_or(""); + processInFlight.m_stdError += processInFlight.m_process->ConsumeStdErr().value_or(""); + } + + StdContent ProcessScheduler::ConsumeProcessStdContent(ProcessInFlight& processInFlight) + { + return + { + !processInFlight.m_stdOutput.empty() + ? AZStd::optional{AZStd::move(processInFlight.m_stdOutput)} + : AZStd::nullopt, + !processInFlight.m_stdError.empty() + ? AZStd::optional{AZStd::move(processInFlight.m_stdError)} + : AZStd::nullopt + }; + } + + void ProcessScheduler::TerminateAllProcesses(ExitCondition exitStatus) + { + bool isCallingBackToClient = true; + const ReturnCode returnCode = static_cast(exitStatus); + + for (auto& processInFlight : m_processPool) + { + if (processInFlight.m_process) + { + processInFlight.m_process->Terminate(ProcessTerminateErrorCode); + AccumulateProcessStdContent(processInFlight); + const ProcessId processId = processInFlight.m_process->GetProcessInfo().GetId(); + + if (isCallingBackToClient) + { + const auto exitTime = AZStd::chrono::high_resolution_clock::now(); + if (CallbackResult::Abort == m_processExitCallback( + processInFlight.m_process->GetProcessInfo().GetId(), + exitStatus, + returnCode, + ConsumeProcessStdContent(processInFlight), + exitTime)) + { + // Client chose to abort the scheduler, do not make any further callbacks + isCallingBackToClient = false; + } + } + + processInFlight.m_process.reset(); + } + } + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h new file mode 100644 index 0000000000..5531c78397 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h @@ -0,0 +1,110 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace TestImpact +{ + //! Result of the attempt to launch a process. + enum class LaunchResult : bool + { + Failure, + Success + }; + + //! The condition under which the processes exited. + //! @note For convinience, the terminate and timeout condition values are set to the corresponding return value sent to the + //! process. + enum class ExitCondition : ReturnCode + { + Gracefull, //!< Process has exited of its own accord. + Terminated = ProcessTerminateErrorCode, //!< The process was terminated by the client/scheduler. + Timeout = ProcessTimeoutErrorCode //!< The process was terminated by the scheduler due to exceeding runtime limit. + }; + + //! Callback for process launch attempt. + //! @param processId The id of the process that attempted to launch. + //! @param launchResult The result of the process launch attempt. + //! @param createTime The timestamp of the process launch attempt. + using ProcessLaunchCallback = + AZStd::function; + + //! Callback for process exit of successfully launched process. + //! @param processId The id of the process that attempted to launch. + //! @param exitStatus The circumstances upon which the processes exited. + //! @param returnCode The return code of the exited process. + //! @param std The standard output and standard error of the process. + //! @param createTime The timestamp of the process exit. + using ProcessExitCallback = + AZStd::function; + + //! Schedules a batch of processes for launch using a round robin approach to distribute the in-flight processes over + //! the specified number of concurrent process slots. + class ProcessScheduler + { + public: + //! Constructs the scheduler with the specified batch of processes. + //! @param processes The batch of processes to schedule. + //! @param processLaunchCallback The process launch callback function. + //! @param processExitCallback The process exit callback function. + //! @param maxConcurrentProcesses The maximum number of concurrent processes in-flight. + //! @param processTimeout The maximum duration a process may be in-flight for before being forcefully terminated. + //! @param scheduleTimeout The maximum duration the scheduler may run before forcefully terminating all in-flight processes. + //! processes and abandoning any queued processes. + ProcessScheduler( + const AZStd::vector& processes, + const ProcessLaunchCallback& processLaunchCallback, + const ProcessExitCallback& processExitCallback, + size_t maxConcurrentProcesses, + AZStd::optional processTimeout, + AZStd::optional scheduleTimeout); + + ~ProcessScheduler(); + + private: + struct ProcessInFlight; + + void MonitorProcesses(); + CallbackResult PopAndLaunch(ProcessInFlight& processInFlight); + void TerminateAllProcesses(ExitCondition exitStatus); + StdContent ConsumeProcessStdContent(ProcessInFlight& processInFlight); + void AccumulateProcessStdContent(ProcessInFlight& processInFlight); + + const ProcessLaunchCallback m_processCreateCallback; + const ProcessExitCallback m_processExitCallback; + const AZStd::optional m_processTimeout; + const AZStd::optional m_scheduleTimeout; + const AZStd::chrono::high_resolution_clock::time_point m_startTime; + AZStd::vector m_processPool; + AZStd::queue m_processQueue; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcess.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcess.cpp new file mode 100644 index 0000000000..3edc5af28f --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcess.cpp @@ -0,0 +1,32 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +namespace TestImpact +{ + Process::Process(const ProcessInfo& processInfo) + : m_processInfo(processInfo) + { + } + + const ProcessInfo& Process::GetProcessInfo() const + { + return m_processInfo; + } + + AZStd::optional Process::GetReturnCode() const + { + return m_returnCode; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcess.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcess.h new file mode 100644 index 0000000000..763290d787 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcess.h @@ -0,0 +1,61 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +namespace TestImpact +{ + //! Abstraction of platform-specific process. + class Process + { + public: + explicit Process(const ProcessInfo& processInfo); + virtual ~Process() = default; + + //! Terminates the process with the specified return code. + virtual void Terminate(ReturnCode returnCode) = 0; + + //! Block the calling thread until the process exits. + virtual void BlockUntilExit() = 0; + + //! Returns whether or not the process is still running. + virtual bool IsRunning() const = 0; + + //! Returns the process info associated with this process. + const ProcessInfo& GetProcessInfo() const; + + //! Returns the return code of the exited process. + //! Will be empty if the process is still running or was not successfully launched. + AZStd::optional GetReturnCode() const; + + //! Flushes the internal buffer and returns the process's buffered standard output. + //! Subsequent calls will keep returning data so long as the process is producing output. + //! Will return nullopt if no output routing or no output produced. + virtual AZStd::optional ConsumeStdOut() = 0; + + //! Flushes the internal buffer and returns the process's buffered standard error. + //! Subsequent calls will keep returning data so long as the process is producing errors. + //! Will return nullopt if no error routing or no errors produced. + virtual AZStd::optional ConsumeStdErr() = 0; + + protected: + //! The information used to launch the process. + ProcessInfo m_processInfo; + + //! The return code of a successfully launched process (otherwise is empty) + AZStd::optional m_returnCode; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessException.h new file mode 100644 index 0000000000..0c53800287 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessException.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Exception for processes and process-related operations. + class ProcessException + : public Exception + { + public: + using Exception::Exception; + }; +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessInfo.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessInfo.cpp new file mode 100644 index 0000000000..8dd81e4a21 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessInfo.cpp @@ -0,0 +1,67 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +namespace TestImpact +{ + ProcessInfo::ProcessInfo(ProcessId id, const AZ::IO::Path& processPath, const AZStd::string& startupArgs) + : m_id(id) + , m_parentHasStdOutput(false) + , m_parentHasStdErr(false) + , m_processPath(processPath) + , m_startupArgs(startupArgs) + { + AZ_TestImpact_Eval(processPath.String().length() > 0, ProcessException, "Process path cannot be empty"); + } + + ProcessInfo::ProcessInfo( + ProcessId id, + StdOutputRouting stdOut, + StdErrorRouting stdErr, + const AZ::IO::Path& processPath, + const AZStd::string& startupArgs) + : m_id(id) + , m_processPath(processPath) + , m_startupArgs(startupArgs) + , m_parentHasStdOutput(stdOut == StdOutputRouting::ToParent ? true : false) + , m_parentHasStdErr(stdErr == StdErrorRouting::ToParent ? true : false) + { + AZ_TestImpact_Eval(processPath.String().length() > 0, ProcessException, "Process path cannot be empty"); + } + + ProcessId ProcessInfo::GetId() const + { + return m_id; + } + + const AZ::IO::Path& ProcessInfo::GetProcessPath() const + { + return m_processPath; + } + + const AZStd::string& ProcessInfo::GetStartupArgs() const + { + return m_startupArgs; + } + + bool ProcessInfo::ParentHasStdOutput() const + { + return m_parentHasStdOutput; + } + + bool ProcessInfo::ParentHasStdError() const + { + return m_parentHasStdErr; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessInfo.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessInfo.h new file mode 100644 index 0000000000..b73613db0d --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessInfo.h @@ -0,0 +1,93 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include + +namespace TestImpact +{ + //! Identifier to distinguish between processes. + using ProcessId = size_t; + + //! Return code of successfully launched process. + using ReturnCode = int; + + //! Error code for processes that are forcefully terminated whilst in-flight by the client. + inline constexpr const ReturnCode ProcessTerminateErrorCode = 0xF10BAD; + + //! Error code for processes that are forcefully terminated whilst in-flight by the scheduler due to timing out. + inline constexpr const ReturnCode ProcessTimeoutErrorCode = 0xBADF10; + + //! Specifier for how the process's standard out willt be routed + enum class StdOutputRouting + { + ToParent, + None + }; + + enum class StdErrorRouting + { + ToParent, + None + }; + + //! Container for process standard output and standard error. + struct StdContent + { + AZStd::optional m_out; + AZStd::optional m_err; + }; + + //! Information about a process the arguments used to launch it. + class ProcessInfo + { + public: + //! Provides the information required to launch a process. + //! @param processId Client-supplied id to diffrentiate between processes. + //! @param stdOut Routing of process standard output. + //! @param stdErr Routing of process standard error. + //! @param processPath Path to executable binary to launch. + //! @param startupArgs Arguments to launch the process with. + ProcessInfo( + ProcessId processId, + StdOutputRouting stdOut, + StdErrorRouting stdErr, + const AZ::IO::Path& processPath, + const AZStd::string& startupArgs = ""); + ProcessInfo(ProcessId processId, const AZ::IO::Path& processPath, const AZStd::string& startupArgs = ""); + + //! Returns the identifier of this process. + ProcessId GetId() const; + + //! Returns whether or not stdoutput is routed to the parent process. + bool ParentHasStdOutput() const; + + //! Returns whether or not stderror is routed to the parent process. + bool ParentHasStdError() const; + + // Returns the path to the process binary. + const AZ::IO::Path& GetProcessPath() const; + + //! Returns the command line arguments used to launch the process. + const AZStd::string& GetStartupArgs() const; + + private: + const ProcessId m_id; + const bool m_parentHasStdOutput; + const bool m_parentHasStdErr; + const AZ::IO::Path m_processPath; + const AZStd::string m_startupArgs; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessLauncher.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessLauncher.h new file mode 100644 index 0000000000..cefd16f003 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/TestImpactProcessLauncher.h @@ -0,0 +1,27 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +namespace TestImpact +{ + class Process; + class ProcessInfo; + + //! Attempts to launch a process with the provided command line arguments. + //! @param processInfo The path and command line arguments to launch the process with. + AZStd::unique_ptr LaunchProcess(const ProcessInfo& processInfo); +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.cpp new file mode 100644 index 0000000000..bce6fa25d6 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.cpp @@ -0,0 +1,48 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "TestImpactBuildTarget.h" + +namespace TestImpact +{ + BuildTarget::BuildTarget(BuildTargetDescriptor&& descriptor, TargetType type) + : m_buildMetaData(AZStd::move(descriptor.m_buildMetaData)) + , m_sources(AZStd::move(descriptor.m_sources)) + , m_type(type) + { + } + + const AZStd::string& BuildTarget::GetName() const + { + return m_buildMetaData.m_name; + } + + const AZStd::string& BuildTarget::GetOutputName() const + { + return m_buildMetaData.m_outputName; + } + + const AZ::IO::Path& BuildTarget::GetPath() const + { + return m_buildMetaData.m_path; + } + + const TargetSources& BuildTarget::GetSources() const + { + return m_sources; + } + + TargetType BuildTarget::GetType() const + { + return m_type; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h new file mode 100644 index 0000000000..d4346c15ef --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h @@ -0,0 +1,55 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +namespace TestImpact +{ + //! Type id for querying specialized derived target types from base pointer/reference. + enum class TargetType : bool + { + Production, //!< Production build target. + Test //!< Test build target. + }; + + //! Representation of a generic build target in the repository. + class BuildTarget + { + public: + BuildTarget(BuildTargetDescriptor&& descriptor, TargetType type); + virtual ~BuildTarget() = default; + + //! Returns the build target name. + const AZStd::string& GetName() const; + + //! Returns the build target's compiled binary name. + const AZStd::string& GetOutputName() const; + + //! Returns the path in the source tree to the build target location. + const AZ::IO::Path& GetPath() const; + + //! Returns the build target's sources. + const TargetSources& GetSources() const; + + //! Returns the build target type. + TargetType GetType() const; + + private: + BuildMetaData m_buildMetaData; + TargetSources m_sources; + TargetType m_type; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h new file mode 100644 index 0000000000..dfd102b55f --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h @@ -0,0 +1,125 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include +#include +#include +#include + +#include + +namespace TestImpact +{ + //! Container for unique set of sorted build target types. + //! @tparam Target The specialized build target type. + template + class BuildTargetList + { + public: + using TargetType = Target; + + BuildTargetList(AZStd::vector&& descriptors); + + //! Returns the targets in the collection. + const AZStd::vector& GetTargets() const; + + //! Returns the target with the specified name. + const Target* GetTarget(const AZStd::string& name) const; + + //! Returns the target with the specified name or throws if target not found. + const Target* GetTargetOrThrow(const AZStd::string& name) const; + + // Returns the number of targets in the list. + size_t GetNumTargets() const; + + private: + AZStd::vector m_targets; + }; + + template + BuildTargetList::BuildTargetList(AZStd::vector&& descriptors) + { + AZ_TestImpact_Eval(!descriptors.empty(), TargetException, "Target list is empty"); + + AZStd::sort( + descriptors.begin(), descriptors.end(), [](const typename Target::Descriptor& lhs, const typename Target::Descriptor& rhs) + { + return lhs.m_buildMetaData.m_name < rhs.m_buildMetaData.m_name; + }); + + const auto duplicateElement = AZStd::adjacent_find( + descriptors.begin(), descriptors.end(), [](const typename Target::Descriptor& lhs, const typename Target::Descriptor& rhs) + { + return lhs.m_buildMetaData.m_name == rhs.m_buildMetaData.m_name; + }); + + AZ_TestImpact_Eval(duplicateElement == descriptors.end(), TargetException, "Target list contains duplicate targets"); + + m_targets.reserve(descriptors.size()); + for (auto&& descriptor : descriptors) + { + m_targets.emplace_back(Target(AZStd::move(descriptor))); + } + } + + template + const AZStd::vector& BuildTargetList::GetTargets() const + { + return m_targets; + } + + template + size_t BuildTargetList::GetNumTargets() const + { + return m_targets.size(); + } + + template + const Target* BuildTargetList::GetTarget(const AZStd::string& name) const + { + struct TargetComparator + { + bool operator()(const Target& target, const AZStd::string& name) const + { + return target.GetName() < name; + } + + bool operator()(const AZStd::string& name, const Target& target) const + { + return name < target.GetName(); + } + }; + + const auto targetRange = std::equal_range(m_targets.begin(), m_targets.end(), name, TargetComparator{}); + + if (targetRange.first != targetRange.second) + { + return targetRange.first; + } + else + { + return nullptr; + } + } + + template + const Target* BuildTargetList::GetTargetOrThrow(const AZStd::string& name) const + { + const Target* target = GetTarget(name); + AZ_TestImpact_Eval(target, TargetException, AZStd::string::format("Couldn't find target %s", name.c_str()).c_str()); + return target; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactProductionTarget.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactProductionTarget.cpp new file mode 100644 index 0000000000..88c3cf932a --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactProductionTarget.cpp @@ -0,0 +1,21 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "TestImpactProductionTarget.h" + +namespace TestImpact +{ + ProductionTarget::ProductionTarget(Descriptor&& descriptor) + : BuildTarget(AZStd::move(descriptor), TargetType::Production) + { + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactProductionTarget.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactProductionTarget.h new file mode 100644 index 0000000000..d4ddb6dc50 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactProductionTarget.h @@ -0,0 +1,31 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Build target specialization for production targets (build targets containing production code and no test code). + class ProductionTarget + : public BuildTarget + { + public: + using Descriptor = ProductionTargetDescriptor; + ProductionTarget(Descriptor&& descriptor); + }; + + template + inline constexpr bool IsProductionTarget = AZStd::is_same_v>>>; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactProductionTargetList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactProductionTargetList.h new file mode 100644 index 0000000000..d2f23abe2e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactProductionTargetList.h @@ -0,0 +1,22 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Container for set of sorted production targets containing no duplicates. + using ProductionTargetList = BuildTargetList; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTargetException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTargetException.h new file mode 100644 index 0000000000..d3f2ec25ee --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTargetException.h @@ -0,0 +1,25 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Exception for target and target-related operations. + class TargetException : public Exception + { + public: + using Exception::Exception; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.cpp new file mode 100644 index 0000000000..0189bc9e78 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.cpp @@ -0,0 +1,32 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "TestImpactTestTarget.h" + +namespace TestImpact +{ + TestTarget::TestTarget(Descriptor&& descriptor) + : BuildTarget(AZStd::move(descriptor), TargetType::Test) + , m_testMetaData(AZStd::move(descriptor.m_testMetaData)) + { + } + + const AZStd::string& TestTarget::GetSuite() const + { + return m_testMetaData.m_suite; + } + + LaunchMethod TestTarget::GetLaunchMethod() const + { + return m_testMetaData.m_launchMethod; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.h new file mode 100644 index 0000000000..b6f44e7cc1 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.h @@ -0,0 +1,41 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Build target specialization for test targets (build targets containing test code and no production code). + class TestTarget + : public BuildTarget + { + public: + using Descriptor = TestTargetDescriptor; + + TestTarget(Descriptor&& descriptor); + + //! Returns the test target suite. + const AZStd::string& GetSuite() const; + + //! Returns the test target launch method. + LaunchMethod GetLaunchMethod() const; + + private: + const TestTargetMeta m_testMetaData; + }; + + template + inline constexpr bool IsTestTarget = AZStd::is_same_v>>>; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTargetList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTargetList.h new file mode 100644 index 0000000000..f8cafcb735 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTargetList.h @@ -0,0 +1,22 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Container for set of sorted test targets containing no duplicates. + using TestTargetList = BuildTargetList; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumeration.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumeration.h new file mode 100644 index 0000000000..c12abec006 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumeration.h @@ -0,0 +1,22 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Representation of a given test target's enumerated tests. + using TestEnumeration = TestSuiteContainer; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationException.h new file mode 100644 index 0000000000..0c9d27df7a --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationException.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Exception for test enumerations and test enumeration related operations. + class TestEnumerationException + : public Exception + { + public: + using Exception::Exception; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp new file mode 100644 index 0000000000..0b171ba404 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp @@ -0,0 +1,100 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include +#include + +namespace TestImpact +{ + namespace + { + // Keys for pertinent JSON node and attribute names + constexpr const char* Keys[] = + { + "suites", + "name", + "enabled", + "tests" + }; + + enum + { + SuitesKey, + NameKey, + EnabledKey, + TestsKey + }; + } // namespace + + AZStd::string SerializeTestEnumeration(const TestEnumeration& testEnum) + { + rapidjson::StringBuffer stringBuffer; + rapidjson::PrettyWriter writer(stringBuffer); + + writer.StartObject(); + writer.Key(Keys[SuitesKey]); + writer.StartArray(); + for (const auto& suite : testEnum.GetTestSuites()) + { + writer.StartObject(); + writer.Key(Keys[NameKey]); + writer.String(suite.m_name.c_str()); + writer.Key(Keys[EnabledKey]); + writer.Bool(suite.m_enabled); + writer.Key(Keys[TestsKey]); + writer.StartArray(); + for (const auto& test : suite.m_tests) + { + writer.StartObject(); + writer.Key(Keys[NameKey]); + writer.String(test.m_name.c_str()); + writer.Key(Keys[EnabledKey]); + writer.Bool(test.m_enabled); + writer.EndObject(); + } + writer.EndArray(); + writer.EndObject(); + } + writer.EndArray(); + writer.EndObject(); + + return stringBuffer.GetString(); + } + + TestEnumeration DeserializeTestEnumeration(const AZStd::string& testEnumString) + { + AZStd::vector testSuites; + rapidjson::Document doc; + + if (doc.Parse<0>(testEnumString.c_str()).HasParseError()) + { + throw TestEnumerationException("Could not parse enumeration data"); + } + + for (const auto& suite : doc[Keys[SuitesKey]].GetArray()) + { + testSuites.emplace_back(TestEnumerationSuite{suite[Keys[NameKey]].GetString(), suite[Keys[EnabledKey]].GetBool(), {}}); + for (const auto& test : suite[Keys[TestsKey]].GetArray()) + { + testSuites.back().m_tests.emplace_back( + TestEnumerationCase{test[Keys[NameKey]].GetString(), test[Keys[EnabledKey]].GetBool()}); + } + } + + return TestEnumeration(std::move(testSuites)); + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.h new file mode 100644 index 0000000000..422308d862 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +namespace TestImpact +{ + //! Serializes the specified test enumeration to JSON format. + AZStd::string SerializeTestEnumeration(const TestEnumeration& testEnumeration); + + //! Deserializes a test enumeration from the specified test enumeration data in JSON format. + TestEnumeration DeserializeTestEnumeration(const AZStd::string& testEnumerationString); +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.cpp new file mode 100644 index 0000000000..1964f8876e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.cpp @@ -0,0 +1,190 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include +#include + +#include + +namespace TestImpact +{ + namespace + { + void WriteCacheFile( + const TestEnumeration& enumeration, const AZ::IO::Path& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy) + { + const AZStd::string cacheJSON = SerializeTestEnumeration(enumeration); + const AZStd::vector cacheBytes(cacheJSON.begin(), cacheJSON.end()); + AZ::IO::SystemFile cacheFile; + + if (!cacheFile.Open( + path.c_str(), + AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY)) + { + AZ_TestImpact_Eval( + !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheWriteFailure), TestEnumerationException, + "Couldn't open cache file for writing"); + return; + } + + if (cacheFile.Write(cacheBytes.data(), cacheBytes.size()) == 0) + { + AZ_TestImpact_Eval( + !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheWriteFailure), TestEnumerationException, + "Couldn't write cache file data"); + return; + } + } + + AZStd::optional ReadCacheFile(const AZ::IO::Path& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy) + { + AZ::IO::SystemFile cacheFile; + AZStd::string cacheJSON; + if (!cacheFile.Open(path.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY)) + { + AZ_TestImpact_Eval( + !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheNotExist), TestEnumerationException, + "Couldn't locate cache file"); + return AZStd::nullopt; + } + + const AZ::IO::SystemFile::SizeType length = cacheFile.Length(); + if (length == 0) + { + AZ_TestImpact_Eval( + !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheReadFailure), TestEnumerationException, + "Cache file is empty"); + return AZStd::nullopt; + } + + cacheFile.Seek(0, AZ::IO::SystemFile::SF_SEEK_BEGIN); + cacheJSON.resize(length); + if (cacheFile.Read(length, cacheJSON.data()) != length) + { + AZ_TestImpact_Eval( + !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheReadFailure), TestEnumerationException, + "Couldn't read cache file"); + return AZStd::nullopt; + } + + return DeserializeTestEnumeration(cacheJSON); + } + } // namespace + + TestEnumeration ParseTestEnumerationFile(const AZ::IO::Path& enumerationFile) + { + return TestEnumeration(GTest::TestEnumerationSuitesFactory(ReadFileContents(enumerationFile))); + } + + TestEnumerationJobData::TestEnumerationJobData(const AZ::IO::Path& enumerationArtifact, AZStd::optional&& cache) + : m_enumerationArtifact(enumerationArtifact) + , m_cache(AZStd::move(cache)) + { + } + + const AZ::IO::Path& TestEnumerationJobData::GetEnumerationArtifactPath() const + { + return m_enumerationArtifact; + } + + const AZStd::optional& TestEnumerationJobData::GetCache() const + { + return m_cache; + } + + TestEnumerator::TestEnumerator( + AZStd::optional clientCallback, + size_t maxConcurrentEnumerations, + AZStd::optional enumerationTimeout, + AZStd::optional enumeratorTimeout) + : JobRunner(clientCallback, AZStd::nullopt, StdOutputRouting::None, StdErrorRouting::None, maxConcurrentEnumerations, enumerationTimeout, enumeratorTimeout) + { + } + + AZStd::vector TestEnumerator::Enumerate( + const AZStd::vector& jobInfos, + CacheExceptionPolicy cacheExceptionPolicy, + JobExceptionPolicy jobExceptionPolicy) + { + AZStd::vector cachedJobs; + AZStd::vector jobQueue; + + for (const auto& jobInfo : jobInfos) + { + // If this job has a cache read policy attempt to read the cache + if (jobInfo.GetCache().has_value() && jobInfo.GetCache()->m_policy == JobData::CachePolicy::Read) + { + JobMeta meta; + auto enumeration = ReadCacheFile(jobInfo.GetCache()->m_file, cacheExceptionPolicy); + + // Even though cached jobs don't get executed we still give the client the opportunity to handle the job state + // change in order to make the caching process transparent to the client + if (enumeration.has_value()) + { + if (m_clientJobCallback.has_value()) + { + (*m_clientJobCallback)(jobInfo, meta); + } + + // Cache read successfully, this job will not be placed in the job queue + cachedJobs.emplace_back(Job(jobInfo, AZStd::move(meta), AZStd::move(enumeration))); + } + else + { + // The cache read failed and exception policy for cache read failures is not to throw so instead place this job in the + // job queue + jobQueue.emplace_back(jobInfo); + } + } + else + { + // This job has no cache read policy so place in job queue + jobQueue.emplace_back(jobInfo); + } + } + + const auto payloadGenerator = [this, cacheExceptionPolicy](const JobDataMap& jobDataMap) + { + PayloadMap enumerations; + for (const auto& [jobId, jobData] : jobDataMap) + { + const auto& [meta, jobInfo] = jobData; + if (meta.m_result == JobResult::ExecutedWithSuccess) + { + const auto& enumeration = enumerations[jobId] = ParseTestEnumerationFile(jobInfo->GetEnumerationArtifactPath()); + + // Write out the enumeration to a cache file if we have a cache write policy for this job + if (jobInfo->GetCache().has_value() && jobInfo->GetCache()->m_policy == JobData::CachePolicy::Write) + { + WriteCacheFile(enumeration.value(), jobInfo->GetCache()->m_file, cacheExceptionPolicy); + } + } + } + + return enumerations; + }; + + // Generate the enumeration results for the jobs that weren't cached + auto jobs = ExecuteJobs(jobQueue, payloadGenerator, jobExceptionPolicy); + + // We need to add the cached jobs to the completed job list even though they technically weren't executed + for (auto&& job : cachedJobs) + { + jobs.emplace_back(AZStd::move(job)); + } + + return jobs; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.h new file mode 100644 index 0000000000..a005a32b0d --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.h @@ -0,0 +1,94 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include + +#include + +namespace TestImpact +{ + //! Per-job data for test enumerations. + class TestEnumerationJobData + { + public: + //! Policy for how a test enumeration will be written/read from the a previous cache instead of enumerated from the test target. + enum class CachePolicy + { + Read, //!< Do read from a cache file but do not overwrite any existing cache file. + Write //!< Do not read from a cache file but instead overwrite any existing cache file. + }; + + //! Cache configuration for a given test enumeration command. + struct Cache + { + CachePolicy m_policy; + AZ::IO::Path m_file; + }; + + TestEnumerationJobData(const AZ::IO::Path& enumerationArtifact, AZStd::optional&& cache); + + //! Returns the path to the enumeration artifact produced by the test target. + const AZ::IO::Path& GetEnumerationArtifactPath() const; + + //! Returns the cache details for this job. + const AZStd::optional& GetCache() const; + + private: + AZ::IO::Path m_enumerationArtifact; //!< Path to enumeration artifact to be processed. + AZStd::optional m_cache = AZStd::nullopt; //!< No caching takes place if cache is empty. + }; + + namespace Bitwise + { + //! Exception policy for test enumeration cache reads/writes. + enum class CacheExceptionPolicy + { + Never = 0, //! Never throw. + OnCacheNotExist = 1, //! Throw when a cache read policy was in place but a cache file for this job doesn't exist. + OnCacheReadFailure = 1 << 1, //! Throw when a cache read policy is in place but the cache file could not be read. + OnCacheWriteFailure = 1 << 2 //! Throw when a cache write policy is in place but the cache file could not be written. + }; + } // namespace Bitwise + + //! Enumerate a batch of test targets to determine the test suites and fixtures they contain, caching the results where applicable. + class TestEnumerator + : public TestJobRunner + { + using JobRunner = TestJobRunner; + + public: + using CacheExceptionPolicy = Bitwise::CacheExceptionPolicy; + + //! Constructs a test enumerator with the specified parameters common to all enumeration job runs of this enumerator. + //! @param clientCallback The optional client callback to be called whenever an enumeration job changes state. + //! @param maxConcurrentEnumerations The maximum number of enumerations to be in flight at any given time. + //! @param enumerationTimeout The maximum duration an enumeration may be in-flight for before being forcefully terminated. + //! @param enumeratorTimeout The maximum duration the enumerator may run before forcefully terminating all in-flight enumerations. + TestEnumerator( + AZStd::optional clientCallback, + size_t maxConcurrentEnumerations, + AZStd::optional enumerationTimeout, + AZStd::optional enumeratorTimeout); + + //! Executes the specified test enumeration jobs according to the specified cache and job exception policies. + //! @param jobInfos The enumeration jobs to execute. + //! @param cacheExceptionPolicy The cache exception policy to be used for this run. + //! @param jobExceptionPolicy The enumeration job exception policy to be used for this run. + //! @return the test enumeration jobs with their associated test enumeration payloads. + AZStd::vector Enumerate( + const AZStd::vector& jobInfos, CacheExceptionPolicy cacheExceptionPolicy, JobExceptionPolicy jobExceptionPolicy); + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h new file mode 100644 index 0000000000..ee245f1ff3 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h @@ -0,0 +1,36 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include +#include +#include + +namespace TestImpact +{ + template + AZStd::string ReadFileContents(const AZ::IO::Path& file) + { + static_assert(AZStd::is_base_of::value, "Exception must be a TestImpact exception or derived type"); + + const auto fileSize = AZ::IO::SystemFile::Length(file.c_str()); + AZ_TestImpact_Eval(fileSize > 0, ExceptionType, AZStd::string::format("File %s does not exist", file.c_str())); + + AZStd::vector buffer(fileSize + 1); + buffer[fileSize] = '\0'; + AZ_TestImpact_Eval(AZ::IO::SystemFile::Read(file.c_str(), buffer.data()), ExceptionType, "Could not read file contents"); + + return AZStd::string(buffer.begin(), buffer.end()); + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobException.h new file mode 100644 index 0000000000..263d70322e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobException.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Exception for test job related operations. + class TestJobException + : public Exception + { + public: + using Exception::Exception; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobRunner.h new file mode 100644 index 0000000000..a4a0b19845 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobRunner.h @@ -0,0 +1,141 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include + +namespace TestImpact +{ + namespace Bitwise + { + //! Exception policy for test jobs (and derived jobs). + enum class TestJobExceptionPolicy + { + Never = 0, //!< Never throw. + OnFailedToExecute = 1, //!< Throw when a job fails to execute. + OnExecutedWithFailure = 1 << 1 //!< Throw when a job returns with an error code. + }; + } // namespace Bitwise + + //! Base class for test related job runners. + //! @tparam AdditionalInfo The data structure containing the information additional to the command arguments necessary to execute and + //! complete a job. + //! @tparam Payload The output produced by a job. + template + class TestJobRunner + { + public: + using JobData = AdditionalInfo; + using JobInfo = JobInfo; + using JobPayload = Payload; + using Job = Job; + using ClientJobCallback = AZStd::function; + using DerivedJobCallback = JobCallback; + using JobExceptionPolicy = Bitwise::TestJobExceptionPolicy; + using JobDataMap = JobDataMap; + + //! Constructs the job runner with the specified parameters common to all job runs of this runner. + //! @param clientCallback The optional callback function provided by the client to be called upon job state change. + //! @param clientCallback The optional callback function provided by the derived job runner to be called upon job state change. + //! @param stdOutRouting The standard output routing from the underlying job processes to the derived runner. + //! @param stdErrorRouting The standard error routing from the underlying job processes to the derived runner. + //! @param maxConcurrentJobs The maximum number of jobs to be in flight at any given time. + //! @param jobTimeout The maximum duration a job may be in-flight for before being forcefully terminated (nullopt if no timeout). + //! @param runnerTimeout The maximum duration the runner may run before forcefully terminating all in-flight jobs (nullopt if no + //! timeout). + TestJobRunner( + AZStd::optional clientCallback, + AZStd::optional derivedJobCallback, + StdOutputRouting stdOutRouting, + StdErrorRouting stdErrRouting, + size_t maxConcurrentJobs, + AZStd::optional jobTimeout, + AZStd::optional runnerTimeout); + + protected: + //! Runs the specified jobs and returns the completed payloads produced by each job. + //! @param jobInfos The batch of jobs to execute. + //! @param payloadMapProducer The client callback for producing the payload map based on the completed job data. + //! @param jobExceptionPolicy The job execution policy for this job run. + AZStd::vector ExecuteJobs( + const AZStd::vector& jobInfos, + PayloadMapProducer payloadMapProducer, + JobExceptionPolicy jobExceptionPolicy); + + const AZStd::optional m_clientJobCallback; + + private: + JobRunner m_jobRunner; + const AZStd::optional m_derivedJobCallback; + }; + + template + TestJobRunner::TestJobRunner( + AZStd::optional clientCallback, + AZStd::optional derivedJobCallback, + StdOutputRouting stdOutRouting, + StdErrorRouting stdErrRouting, + size_t maxConcurrentJobs, + AZStd::optional jobTimeout, + AZStd::optional runnerTimeout) + : m_jobRunner(stdOutRouting, stdErrRouting, maxConcurrentJobs, jobTimeout, runnerTimeout) + , m_clientJobCallback(clientCallback) + , m_derivedJobCallback(derivedJobCallback) + { + } + + template + AZStd::vector::Job> TestJobRunner::ExecuteJobs( + const AZStd::vector& jobInfos, + PayloadMapProducer payloadMapProducer, + JobExceptionPolicy jobExceptionPolicy) + { + // Callback to handle job exception policies and client/derived callbacks + const auto jobCallback = [this, &jobExceptionPolicy](const JobInfo& jobInfo, const JobMeta& meta, StdContent&& std) + { + if (meta.m_result == JobResult::FailedToExecute && IsFlagSet(jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)) + { + throw TestJobException("Job failed to execute"); + } + else if (meta.m_result == JobResult::ExecutedWithFailure && IsFlagSet(jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)) + { + throw TestJobException("Job executed with failure"); + } + + if (m_derivedJobCallback.has_value()) + { + if (const auto result = (*m_derivedJobCallback)(jobInfo, meta, AZStd::move(std)); result != CallbackResult::Continue) + { + return result; + } + } + + if (m_clientJobCallback.has_value()) + { + (*m_clientJobCallback)(jobInfo, meta); + } + + return CallbackResult::Continue; + }; + + return m_jobRunner.Execute(jobInfos, jobCallback, payloadMapProducer); + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.cpp new file mode 100644 index 0000000000..3b7a088fb4 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.cpp @@ -0,0 +1,89 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +namespace TestImpact +{ + InstrumentedTestRunJobData::InstrumentedTestRunJobData(const AZ::IO::Path& resultsArtifact, const AZ::IO::Path& coverageArtifact) + : TestRunJobData(resultsArtifact) + , m_coverageArtifact(coverageArtifact) + { + } + + const AZ::IO::Path& InstrumentedTestRunJobData::GetCoverageArtifactPath() const + { + return m_coverageArtifact; + } + + InstrumentedTestRunner::JobPayload ParseTestRunAndCoverageFiles( + const AZ::IO::Path& runFile, + const AZ::IO::Path& coverageFile, + AZStd::chrono::milliseconds duration, + InstrumentedTestRunner::CoverageExceptionPolicy coverageExceptionPolicy) + { + TestRun run(GTest::TestRunSuitesFactory(ReadFileContents(runFile)), duration); + AZStd::vector moduleCoverages = Cobertura::ModuleCoveragesFactory(ReadFileContents(coverageFile)); + if (moduleCoverages.empty()) + { + AZ_TestImpact_Eval( + !IsFlagSet(coverageExceptionPolicy, Bitwise::CoverageExceptionPolicy::OnEmptyCoverage), TestRunException, + "No coverage data generated"); + } + + TestCoverage coverage(AZStd::move(moduleCoverages)); + return {AZStd::move(run), AZStd::move(coverage)}; + } + + InstrumentedTestRunner::InstrumentedTestRunner( + AZStd::optional clientCallback, + size_t maxConcurrentRuns, + AZStd::optional runTimeout, + AZStd::optional runnerTimeout) + : JobRunner(clientCallback, AZStd::nullopt, StdOutputRouting::None, StdErrorRouting::None, maxConcurrentRuns, runTimeout, runnerTimeout) + { + } + + AZStd::vector InstrumentedTestRunner::RunInstrumentedTests( + const AZStd::vector& jobInfos, + CoverageExceptionPolicy coverageExceptionPolicy, + JobExceptionPolicy jobExceptionPolicy) + { + const auto payloadGenerator = [this, coverageExceptionPolicy](const JobDataMap& jobDataMap) + { + PayloadMap runs; + for (const auto& [jobId, jobData] : jobDataMap) + { + const auto& [meta, jobInfo] = jobData; + if (meta.m_result == JobResult::ExecutedWithSuccess || meta.m_result == JobResult::ExecutedWithFailure) + { + runs[jobId] = ParseTestRunAndCoverageFiles( + jobInfo->GetRunArtifactPath(), + jobInfo->GetCoverageArtifactPath(), + meta.m_duration.value(), + coverageExceptionPolicy); + } + } + + return runs; + }; + + return ExecuteJobs(jobInfos, payloadGenerator, jobExceptionPolicy); + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.h new file mode 100644 index 0000000000..e6f059b5d3 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.h @@ -0,0 +1,71 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace TestImpact +{ + //! Per-job data for instrumented test runs. + class InstrumentedTestRunJobData : public TestRunJobData + { + public: + InstrumentedTestRunJobData(const AZ::IO::Path& resultsArtifact, const AZ::IO::Path& coverageArtifact); + + //! Returns the path to the coverage artifact produced by the test target. + const AZ::IO::Path& GetCoverageArtifactPath() const; + + private: + AZ::IO::Path m_coverageArtifact; //!< Path to coverage data. + }; + + namespace Bitwise + { + //! Exception policy for test coverage artifacts. + enum class CoverageExceptionPolicy + { + Never = 0, //! Never throw. + OnEmptyCoverage = 1 //! Throw when no coverage data was produced. + }; + } // namespace Bitwise + + //! Runs a batch of test targets to determine the test coverage and passes/failures. + class InstrumentedTestRunner : public TestJobRunner> + { + using JobRunner = TestJobRunner>; + + public: + using CoverageExceptionPolicy = Bitwise::CoverageExceptionPolicy; + + //! Constructs an instrumented test runner with the specified parameters common to all job runs of this runner. + //! @param clientCallback The optional client callback to be called whenever a run job changes state. + //! @param maxConcurrentRuns The maximum number of runs to be in flight at any given time. + //! @param runTimeout The maximum duration a run may be in-flight for before being forcefully terminated. + //! @param runnerTimeout The maximum duration the runner may run before forcefully terminating all in-flight runs. + InstrumentedTestRunner( + AZStd::optional clientCallback, size_t maxConcurrentRuns, + AZStd::optional runTimeout, AZStd::optional runnerTimeout); + + //! Executes the specified instrumented test run jobs according to the specified job exception policies. + //! @param jobInfos The test run jobs to execute. + //! @param CoverageExceptionPolicy The coverage exception policy to be used for this run. + //! @param jobExceptionPolicy The test run job exception policy to be used for this run (use + //! TestJobExceptionPolicy::OnFailedToExecute to throw on test failures). + //! @return the instrumented test run jobs with their associated test run and test coverage payloads. + AZStd::vector RunInstrumentedTests( + const AZStd::vector& jobInfos, CoverageExceptionPolicy coverageExceptionPolicy, JobExceptionPolicy jobExceptionPolicy); + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.cpp new file mode 100644 index 0000000000..3a6f5d0b79 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.cpp @@ -0,0 +1,68 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +namespace TestImpact +{ + TestCoverage::TestCoverage(AZStd::vector&& moduleCoverages) + : m_modules(AZStd::move(moduleCoverages)) + { + for (const auto& moduleCovered : m_modules) + { + for (const auto& sourceCovered : moduleCovered.m_sources) + { + m_sourcesCovered.emplace_back(sourceCovered.m_path); + if (sourceCovered.m_coverage.has_value()) + { + m_coverageLevel = CoverageLevel::Line; + } + } + } + + AZStd::sort(m_sourcesCovered.begin(), m_sourcesCovered.end()); + m_sourcesCovered.erase(AZStd::unique(m_sourcesCovered.begin(), m_sourcesCovered.end()), m_sourcesCovered.end()); + + if (!m_coverageLevel.has_value() && !m_sourcesCovered.empty()) + { + m_coverageLevel = CoverageLevel::Source; + } + } + + size_t TestCoverage::GetNumSourcesCovered() const + { + return m_sourcesCovered.size(); + } + + size_t TestCoverage::GetNumModulesCovered() const + { + return m_modules.size(); + } + + const AZStd::vector& TestCoverage::GetSourcesCovered() const + { + return m_sourcesCovered; + } + + const AZStd::vector& TestCoverage::GetModuleCoverages() const + { + return m_modules; + } + + AZStd::optional TestCoverage::GetCoverageLevel() const + { + return m_coverageLevel; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.h new file mode 100644 index 0000000000..ab92b1bd80 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.h @@ -0,0 +1,54 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +namespace TestImpact +{ + //! Scope of coverage data. + enum class CoverageLevel : bool + { + Source, //!< Line-level coverage data. + Line //!< Source-level coverage data. + }; + + //! Representation of a given test target's test coverage results. + class TestCoverage + { + public: + TestCoverage(AZStd::vector&& moduleCoverages); + + //! Returns the number of unique sources covered. + size_t GetNumSourcesCovered() const; + + //! Returns the number of modules (dynamic libraries, child processes, etc.) covered. + size_t GetNumModulesCovered() const; + + //! Returns the sorted set of unique sources covered (empty if no coverage). + const AZStd::vector& GetSourcesCovered() const; + + //! Returns the modules covered (empty if no coverage). + const AZStd::vector& GetModuleCoverages() const; + + //! Returns the coverage level (empty if no coverage). + AZStd::optional GetCoverageLevel() const; + + private: + AZStd::vector m_modules; + AZStd::vector m_sourcesCovered; + AZStd::optional m_coverageLevel; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.cpp new file mode 100644 index 0000000000..1ba2a3e849 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.cpp @@ -0,0 +1,70 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "TestImpactTestRun.h" + +namespace TestImpact +{ + TestRun::TestRun(AZStd::vector&& testSuites, AZStd::chrono::milliseconds duration) + : TestSuiteContainer(AZStd::move(testSuites)) + , m_duration(duration) + { + for (const auto& suite : m_testSuites) + { + for (const auto& test : suite.m_tests) + { + if (test.m_status == TestRunStatus::Run) + { + m_numRuns++; + + if (test.m_result.value() == TestRunResult::Passed) + { + m_numPasses++; + } + else + { + m_numFailures++; + } + } + else + { + m_numNotRuns++; + } + } + } + } + + size_t TestRun::GetNumRuns() const + { + return m_numRuns; + } + + size_t TestRun::GetNumNotRuns() const + { + return m_numNotRuns; + } + + size_t TestRun::GetNumPasses() const + { + return m_numPasses; + } + + size_t TestRun::GetNumFailures() const + { + return m_numFailures; + } + + AZStd::chrono::milliseconds TestRun::GetDuration() const + { + return m_duration; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.h new file mode 100644 index 0000000000..75d2140a68 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.h @@ -0,0 +1,51 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Representation of a given test target's test run results. + class TestRun + : public TestSuiteContainer + { + using TestSuiteContainer = TestSuiteContainer; + + public: + TestRun(AZStd::vector&& testSuites, AZStd::chrono::milliseconds duration); + + //! Returns the total number of tests that were run. + size_t GetNumRuns() const; + + //! Returns the total number of tests that were not run. + size_t GetNumNotRuns() const; + + //! Returns the total number of tests that were run and passed. + size_t GetNumPasses() const; + + //! Returns the total number of tests that were run and failed. + size_t GetNumFailures() const; + + //! Returns the duration of the job that was executed to yield this run data. + AZStd::chrono::milliseconds GetDuration() const; + + private: + size_t m_numRuns = 0; + size_t m_numNotRuns = 0; + size_t m_numPasses = 0; + size_t m_numFailures = 0; + AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{0}; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunException.h new file mode 100644 index 0000000000..ec13a17f57 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunException.h @@ -0,0 +1,25 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Exception for test runs and test run related operations. + class TestRunException : public Exception + { + public: + using Exception::Exception; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.cpp new file mode 100644 index 0000000000..fa40b30263 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.cpp @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + TestRunJobData::TestRunJobData(const AZ::IO::Path& resultsArtifact) + : m_runArtifact(resultsArtifact) + { + } + + const AZ::IO::Path& TestRunJobData::GetRunArtifactPath() const + { + return m_runArtifact; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.h new file mode 100644 index 0000000000..802aa0d30f --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.h @@ -0,0 +1,31 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Per-job data for test runs. + class TestRunJobData + { + public: + TestRunJobData(const AZ::IO::Path& resultsArtifact); + + //! Returns the path to the test run artifact produced by the test target. + const AZ::IO::Path& GetRunArtifactPath() const; + + private: + AZ::IO::Path m_runArtifact; //!< Path to results data. + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.cpp new file mode 100644 index 0000000000..15a135ad25 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.cpp @@ -0,0 +1,186 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include +#include + +namespace TestImpact +{ + namespace + { + // Keys for pertinent JSON node and attribute names + constexpr const char* Keys[] = + { + "suites", + "name", + "enabled", + "tests", + "duration", + "status", + "result" + }; + + enum + { + SuitesKey, + NameKey, + EnabledKey, + TestsKey, + DurationKey, + StatusKey, + ResultKey + }; + } // namespace + + AZStd::string SerializeTestRun(const TestRun& testRun) + { + rapidjson::StringBuffer stringBuffer; + rapidjson::PrettyWriter writer(stringBuffer); + + // Run + writer.StartObject(); + + // Run duration + writer.Key(Keys[DurationKey]); + writer.Uint(testRun.GetDuration().count()); + + // Suites + writer.Key(Keys[SuitesKey]); + writer.StartArray(); + + for (const auto& suite : testRun.GetTestSuites()) + { + // Suite + writer.StartObject(); + + // Suite name + writer.Key(Keys[NameKey]); + writer.String(suite.m_name.c_str()); + + // Suite duration + writer.Key(Keys[DurationKey]); + writer.Uint(suite.m_duration.count()); + + // Suite enabled + writer.Key(Keys[EnabledKey]); + writer.Bool(suite.m_enabled); + + // Suite tests + writer.Key(Keys[TestsKey]); + writer.StartArray(); + for (const auto& test : suite.m_tests) + { + // Test + writer.StartObject(); + + // Test name + writer.Key(Keys[NameKey]); + writer.String(test.m_name.c_str()); + + // Test enabled + writer.Key(Keys[EnabledKey]); + writer.Bool(test.m_enabled); + + // Test duration + writer.Key(Keys[DurationKey]); + writer.Uint(test.m_duration.count()); + + // Test status + writer.Key(Keys[StatusKey]); + writer.Bool(static_cast(test.m_status)); + + // Test result + if (test.m_status == TestRunStatus::Run) + { + writer.Key(Keys[ResultKey]); + writer.Bool(static_cast(test.m_result.value())); + } + else + { + writer.Key(Keys[ResultKey]); + writer.Null(); + } + + // End test + writer.EndObject(); + } + + // End tests + writer.EndArray(); + + // End suite + writer.EndObject(); + } + + // End suites + writer.EndArray(); + + // End run + writer.EndObject(); + + return stringBuffer.GetString(); + } + + TestRun DeserializeTestRun(const AZStd::string& testEnumString) + { + AZStd::vector testSuites; + rapidjson::Document doc; + + if (doc.Parse<0>(testEnumString.c_str()).HasParseError()) + { + throw TestRunException("Could not parse enumeration data"); + } + + // Run duration + const AZStd::chrono::milliseconds runDuration = AZStd::chrono::milliseconds{doc[Keys[DurationKey]].GetUint()}; + + // Suites + for (const auto& suite : doc[Keys[SuitesKey]].GetArray()) + { + // Suite name + const AZStd::string name = suite[Keys[NameKey]].GetString(); + + // Suite duration + const AZStd::chrono::milliseconds suiteDuration = AZStd::chrono::milliseconds{suite[Keys[DurationKey]].GetUint()}; + + // Suite enabled + const bool enabled = suite[Keys[EnabledKey]].GetBool(); + + testSuites.emplace_back(TestRunSuite{ + suite[Keys[NameKey]].GetString(), + suite[Keys[EnabledKey]].GetBool(), + {}, + AZStd::chrono::milliseconds{suite[Keys[DurationKey]].GetUint()}}); + + // Suite tests + for (const auto& test : suite[Keys[TestsKey]].GetArray()) + { + AZStd::optional result; + TestRunStatus status = static_cast(test[Keys[StatusKey]].GetBool()); + if (status == TestRunStatus::Run) + { + result = static_cast(test[Keys[ResultKey]].GetBool()); + } + const AZStd::chrono::milliseconds testDuration = AZStd::chrono::milliseconds{test[Keys[DurationKey]].GetUint()}; + testSuites.back().m_tests.emplace_back( + TestRunCase{test[Keys[NameKey]].GetString(), test[Keys[EnabledKey]].GetBool(), result, testDuration, status}); + } + } + + return TestRun(std::move(testSuites), runDuration); + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.h new file mode 100644 index 0000000000..5978b27105 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +namespace TestImpact +{ + //! Serializes the specified test run to JSON format. + AZStd::string SerializeTestRun(const TestRun& testRun); + + //! Deserializes a test run from the specified test run data in JSON format. + TestRun DeserializeTestRun(const AZStd::string& testRunString); +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.cpp new file mode 100644 index 0000000000..dde7dd7d47 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.cpp @@ -0,0 +1,58 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include +#include + +#include + +namespace TestImpact +{ + TestRun ParseTestRunFile(const AZ::IO::Path& runFile, AZStd::chrono::milliseconds duration) + { + return TestRun(GTest::TestRunSuitesFactory(ReadFileContents(runFile)), duration); + } + + TestRunner::TestRunner( + AZStd::optional clientCallback, + size_t maxConcurrentRuns, + AZStd::optional runTimeout, + AZStd::optional runnerTimeout) + : JobRunner(clientCallback, AZStd::nullopt, StdOutputRouting::None, StdErrorRouting::None, maxConcurrentRuns, runTimeout, runnerTimeout) + { + } + + AZStd::vector TestRunner::RunTests( + const AZStd::vector& jobInfos, + JobExceptionPolicy jobExceptionPolicy) + { + const auto payloadGenerator = [this](const JobDataMap& jobDataMap) + { + PayloadMap runs; + for (const auto& [jobId, jobData] : jobDataMap) + { + const auto& [meta, jobInfo] = jobData; + if (meta.m_result == JobResult::ExecutedWithSuccess || meta.m_result == JobResult::ExecutedWithFailure) + { + runs[jobId] = ParseTestRunFile(jobInfo->GetRunArtifactPath(), meta.m_duration.value()); + } + } + + return runs; + }; + + return ExecuteJobs(jobInfos, payloadGenerator, jobExceptionPolicy); + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.h new file mode 100644 index 0000000000..0bf66601fe --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.h @@ -0,0 +1,46 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include + +namespace TestImpact +{ + //! Runs a batch of test targets to determine the test passes/failures. + class TestRunner + : public TestJobRunner + { + using JobRunner = TestJobRunner; + + public: + //! Constructs a test runner with the specified parameters common to all job runs of this runner. + //! @param clientCallback The optional client callback to be called whenever a run job changes state. + //! @param maxConcurrentRuns The maximum number of runs to be in flight at any given time. + //! @param runTimeout The maximum duration a run may be in-flight for before being forcefully terminated. + //! @param runnerTimeout The maximum duration the runner may run before forcefully terminating all in-flight runs. + TestRunner( + AZStd::optional clientCallback, + size_t maxConcurrentRuns, + AZStd::optional runTimeout, + AZStd::optional runnerTimeout); + + //! Executes the specified test run jobs according to the specified job exception policies. + //! @param jobInfos The test run jobs to execute. + //! @param jobExceptionPolicy The test run job exception policy to be used for this run (use + //! TestJobExceptionPolicy::OnFailedToExecute to throw on test failures). + //! @return the test run jobs with their associated test run payloads. + AZStd::vector RunTests(const AZStd::vector& jobInfos, JobExceptionPolicy jobExceptionPolicy); + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/TestImpactTestSuiteContainer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/TestImpactTestSuiteContainer.h new file mode 100644 index 0000000000..c41d76bd04 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/TestImpactTestSuiteContainer.h @@ -0,0 +1,101 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Encapsulation of test suites into a class with meta-data about each the suites. + //! @tparam TestSuite The test suite data structure to encapsulate. + template + class TestSuiteContainer + { + public: + TestSuiteContainer(AZStd::vector&& testSuites); + + //! Returns the test suites in this container. + const AZStd::vector& GetTestSuites() const; + + //! Returns the number of test suites in this container. + size_t GetNumTestSuites() const; + + //! Returns the total number of tests across all test suites. + size_t GetNumTests() const; + + //! Returns the total number of enabled tests across all test suites. + size_t GetNumEnabledTests() const; + + //! Returns the total number of disabled tests across all test suites. + size_t GetNumDisabledTests() const; + + protected: + AZStd::vector m_testSuites; + size_t m_numDisabledTests = 0; + size_t m_numEnabledTests = 0; + }; + + template + TestSuiteContainer::TestSuiteContainer(AZStd::vector&& testSuites) + : m_testSuites(std::move(testSuites)) + { + for (const auto& suite : m_testSuites) + { + if (suite.m_enabled) + { + const auto enabled = std::count_if(suite.m_tests.begin(), suite.m_tests.end(), [](const auto& test) + { + return test.m_enabled; + }); + + m_numEnabledTests += enabled; + m_numDisabledTests += suite.m_tests.size() - enabled; + } + else + { + // Disabled status of suites propagates down to all tests regardless of whether or not each individual test is disabled + m_numDisabledTests += suite.m_tests.size(); + } + } + } + + template + const AZStd::vector& TestSuiteContainer::GetTestSuites() const + { + return m_testSuites; + } + + template + size_t TestSuiteContainer::GetNumTests() const + { + return m_numEnabledTests + m_numDisabledTests; + } + + template + size_t TestSuiteContainer::GetNumEnabledTests() const + { + return m_numEnabledTests; + } + + template + size_t TestSuiteContainer::GetNumDisabledTests() const + { + return m_numDisabledTests; + } + + template + size_t TestSuiteContainer::GetNumTestSuites() const + { + return m_testSuites.size(); + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactException.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactException.cpp new file mode 100644 index 0000000000..804ab26e4d --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactException.cpp @@ -0,0 +1,31 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + Exception::Exception(const AZStd::string& msg) + : m_msg(msg) + { + } + + Exception::Exception(const char* msg) + : m_msg(msg) + { + } + + const char* Exception::what() const noexcept + { + return m_msg.c_str(); + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactFrameworkPath.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactFrameworkPath.cpp new file mode 100644 index 0000000000..6b5d671776 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactFrameworkPath.cpp @@ -0,0 +1,38 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + FrameworkPath::FrameworkPath(const AZ::IO::Path& absolutePath) + { + m_absolutePath = AZ::IO::Path(absolutePath).MakePreferred(); + m_relativePath = m_absolutePath.LexicallyRelative(m_absolutePath); + } + + FrameworkPath::FrameworkPath(const AZ::IO::Path& absolutePath, const FrameworkPath& relativeTo) + { + m_absolutePath = AZ::IO::Path(absolutePath).MakePreferred(); + m_relativePath = m_absolutePath.LexicallyRelative(relativeTo.Absolute()); + } + + const AZ::IO::Path& FrameworkPath::Absolute() const + { + return m_absolutePath; + } + + const AZ::IO::Path& FrameworkPath::Relative() const + { + return m_relativePath; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactBuildTargetDescriptorFactoryTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactBuildTargetDescriptorFactoryTest.cpp new file mode 100644 index 0000000000..a0a4a7309e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactBuildTargetDescriptorFactoryTest.cpp @@ -0,0 +1,370 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +#include +#include + +namespace UnitTest +{ + class BuildTargetDescriptorFactoryTestFixture + : public AllocatorsTestFixture + { + protected: + const AZStd::vector m_staticExclude = {".cmake"}; + const AZStd::vector m_inputExclude = {".jinja"}; + const AZStd::string m_autogenMatcher = {"(.*)\\..*"}; + + const AZStd::vector m_autogenInputs = + { + "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/Log.ScriptCanvasNode.xml", + "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/DrawText.ScriptCanvasNode.xml", + "Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNode_Header.jinja", + "Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNode_Source.jinja" + }; + + const AZStd::vector m_autogenOutputs = + { + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.h", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.h", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.cpp", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.cpp" + }; + + const AZStd::vector m_staticSources = + { + "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.cpp", + "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.h", + "Gems/ScriptCanvasDiagnosticLibrary/Code/scriptcanvasdiagnosticlibrary_autogen_files.cmake", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.h", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.h", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.cpp", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.cpp" + }; + + const AZStd::vector m_expectedStaticSources = + { + "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.cpp", + "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.h", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.h", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.h", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.cpp", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.cpp" + }; + + const AZStd::string m_name = "ScriptCanvasDiagnosticLibrary.Static"; + const AZStd::string m_outputName = "ScriptCanvasDiagnosticLibrary"; + const AZStd::string m_path = "Gems/ScriptCanvasDiagnosticLibrary/Code"; + + const TestImpact::AutogenSources m_expectedAutogenSources = + { + { + "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/Log.ScriptCanvasNode.xml", + { + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.h", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.cpp" + } + }, + { + "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/DrawText.ScriptCanvasNode.xml", + { + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.h", + "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.cpp" + } + } + }; + }; + + TEST_F(BuildTargetDescriptorFactoryTestFixture, NoRawData_ExpectArtifactException) + { + // Given an empty raw descriptor string + const AZStd::string rawTargetDescriptor; + + try + { + // When attempting to construct the build target descriptor + const TestImpact::BuildTargetDescriptor buildTarget = + TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(BuildTargetDescriptorFactoryTestFixture, InvalidRawData_ExpectArtifactException) + { + // Given a raw descriptor string of invalid data + const AZStd::string rawTargetDescriptor = "abcde"; + + try + { + // When attempting to construct the build target descriptor + const TestImpact::BuildTargetDescriptor buildTarget = + TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(BuildTargetDescriptorFactoryTestFixture, NoAutogenMatcher_ExpectArtifactException) + { + // Given a valid raw descriptor string + const AZStd::string rawTargetDescriptor = + GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, m_autogenInputs, m_autogenOutputs); + + try + { + // When attempting to construct the build target descriptor with an empty autogen matcher + const TestImpact::BuildTargetDescriptor buildTarget = + TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, ""); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(BuildTargetDescriptorFactoryTestFixture, EmptyName_ExpectArtifactException) + { + // Given a invalid raw descriptor string lacking build meta-data name + const AZStd::string rawTargetDescriptor = + GenerateBuildTargetDescriptorString("", m_outputName, m_path, m_staticSources, m_autogenInputs, m_autogenOutputs); + + try + { + // When attempting to construct the build target descriptor + const TestImpact::BuildTargetDescriptor buildTarget = + TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(BuildTargetDescriptorFactoryTestFixture, EmptyOutputName_ExpectArtifactException) + { + // Given a invalid raw descriptor string lacking build meta-data output name + const AZStd::string rawTargetDescriptor = + GenerateBuildTargetDescriptorString(m_name, "", m_path, m_staticSources, m_autogenInputs, m_autogenOutputs); + + try + { + // When attempting to construct the build target descriptor + const TestImpact::BuildTargetDescriptor buildTarget = + TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(BuildTargetDescriptorFactoryTestFixture, EmptyPath_ExpectArtifactException) + { + // Given a invalid raw descriptor string lacking build meta-data path + const AZStd::string rawTargetDescriptor = + GenerateBuildTargetDescriptorString(m_name, m_outputName, "", m_staticSources, m_autogenInputs, m_autogenOutputs); + + try + { + // When attempting to construct the build target descriptor + const TestImpact::BuildTargetDescriptor buildTarget = + TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(BuildTargetDescriptorFactoryTestFixture, NoStaticSources_ExpectValidDescriptor) + { + const TestImpact::BuildTargetDescriptor expectedBuiltTarget = + GenerateBuildTargetDescriptor(m_name, m_outputName, m_path, {}, m_expectedAutogenSources); + + // Given a valid raw descriptor string with no static sources + const AZStd::string rawTargetDescriptor = + GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, {}, m_autogenInputs, m_autogenOutputs); + + // When attempting to construct the build target descriptor + const TestImpact::BuildTargetDescriptor buildTarget = + TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, {}, m_inputExclude, m_autogenMatcher); + + // Expect the constructed build target descriptor to match the specified descriptor + EXPECT_TRUE(buildTarget == expectedBuiltTarget); + } + + TEST_F(BuildTargetDescriptorFactoryTestFixture, NoAutogenSources_ExpectValidDescriptor) + { + const TestImpact::BuildTargetDescriptor expectedBuiltTarget = + GenerateBuildTargetDescriptor(m_name, m_outputName, m_path, m_expectedStaticSources, {}); + + // Given a valid raw descriptor string with no autogen sources + const AZStd::string rawTargetDescriptor = + GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, {}, {}); + + // When attempting to construct the build target descriptor + const TestImpact::BuildTargetDescriptor buildTarget = + TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); + + // Expect the constructed build target descriptor to match the specified descriptor + EXPECT_TRUE(buildTarget == expectedBuiltTarget); + } + + TEST_F(BuildTargetDescriptorFactoryTestFixture, NoStaticOrAutogenSources_ExpectValidDescriptor) + { + const TestImpact::BuildTargetDescriptor expectedBuiltTarget = GenerateBuildTargetDescriptor(m_name, m_outputName, m_path, {}, {}); + + // Given a valid raw descriptor string with no static or autogen sources + const AZStd::string rawTargetDescriptor = GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, {}, {}, {}); + + // When attempting to construct the build target descriptor + const TestImpact::BuildTargetDescriptor buildTarget = + TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); + + // Expect the constructed build target descriptor to match the specified descriptor + EXPECT_TRUE(buildTarget == expectedBuiltTarget); + } + + TEST_F(BuildTargetDescriptorFactoryTestFixture, AutogenOutputSourcesButNoAutogenInputSources_ExpectArtifactException) + { + // Given a valid raw descriptor string with autogen output sources but no autogen input sources + const AZStd::string rawTargetDescriptor = + GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, {}, m_autogenOutputs); + + try + { + // When attempting to construct the build target descriptor + const TestImpact::BuildTargetDescriptor buildTarget = + TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(BuildTargetDescriptorFactoryTestFixture, AutogenInputSourcesButNoAutogenOutputSources_ExpectArtifactException) + { + // Given a valid raw descriptor string with autogen inout sources but no autogen output sources + const AZStd::string rawTargetDescriptor = + GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, m_autogenInputs, {}); + + try + { + // When attempting to construct the build target descriptor + const TestImpact::BuildTargetDescriptor buildTarget = + TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(BuildTargetDescriptorFactoryTestFixture, StaticAndAutogenSources_ExpectValidDescriptor) + { + const TestImpact::BuildTargetDescriptor expectedBuiltTarget = + GenerateBuildTargetDescriptor(m_name, m_outputName, m_path, m_expectedStaticSources, m_expectedAutogenSources); + + // Given a valid raw descriptor string with static and autogen sources + const AZStd::string rawTargetDescriptor = + GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, m_autogenInputs, m_autogenOutputs); + + // When attempting to construct the build target descriptor + const TestImpact::BuildTargetDescriptor buildTarget = + TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); + + // Expect the constructed build target descriptor to match the specified descriptor + EXPECT_TRUE(buildTarget == expectedBuiltTarget); + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactChangeListFactoryTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactChangeListFactoryTest.cpp new file mode 100644 index 0000000000..aeb8f118a4 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactChangeListFactoryTest.cpp @@ -0,0 +1,288 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include + +namespace UnitTest +{ + TEST(ChangeListFactoryTest, NoRawData_ExpectArtifactException) + { + // Given an empty unified diff string + const AZStd::string unifiedDiff; + + try + { + // When attempting to construct the change list + const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(ChangeListFactoryTest, NoChanges_ExpectArtifactException) + { + // Given a unified diff string with no changes + const AZStd::string unifiedDiff = "On this day in 1738 absolutely nothing happened"; + + try + { + // When attempting to construct the change list + const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(ChangeListFactoryTest, CreateOnly_ExpectValidChangeListWithFileCreateOperations) + { + // Given a unified diff with only one file creation and no file updates or deletions + const AZStd::string unifiedDiff = + "From f642a2f698452fc18484758b0046132415f09467 Mon Sep 17 00:00:00 2001\n" + "From: user \n" + "Date: Sat, 13 Mar 2021 22:58:07 +0000\n" + "Subject: Test\n" + "\n" + "---\n" + " New.txt | 1 +\n" + " create mode 100644 New.txt\n" + "diff --git a/New.txt b/New.txt\n" + "new file mode 100644\n" + "index 0000000..30d74d2\n" + "--- /dev/null\n" + "+++ b/New.txt\n" + "@@ -0,0 +1 @@\n" + "+test\n" + "\\ No newline at end of file\n" + "-- \n" + "2.30.0.windows.2\n" + "\n" + "\n"; + + // When attempting to construct the change list + const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff); + + // Expect the change list to contain the 1 created file + EXPECT_EQ(changeList.m_createdFiles.size(), 1); + EXPECT_TRUE( + AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "New.txt") != changeList.m_createdFiles.end()); + + // Expect the change list to contain no updated files + EXPECT_TRUE(changeList.m_updatedFiles.empty()); + + // Expect the change list to contain no deleted files + EXPECT_TRUE(changeList.m_deletedFiles.empty()); + } + + TEST(ChangeListFactoryTest, UpdateOnly_ExpectValidChangeListWithFileUpdateOperations) + { + // Given a unified diff with only one file update and no file creations or deletions + const AZStd::string unifiedDiff = + "From f642a2f698452fc18484758b0046132415f09467 Mon Sep 17 00:00:00 2001\n" + "From: user \n" + "Date: Sat, 13 Mar 2021 22:58:07 +0000\n" + "Subject: Test\n" + "\n" + "---\n" + " A.txt | 2 +-\n" + "diff --git a/A.txt b/A.txt\n" + "index 7c4a013..e132db2 100644\n" + "--- a/A.txt\n" + "+++ b/A.txt\n" + "@@ -1 +1 @@\n" + "-aaa\n" + "\\ No newline at end of file\n" + "+zzz\n" + "\\ No newline at end of file\n" + "-- \n" + "2.30.0.windows.2\n" + "\n" + "\n"; + + // When attempting to construct the change list + const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff); + + // Expect the change list to contain no created files + EXPECT_TRUE(changeList.m_createdFiles.empty()); + + // Expect the change list to contain one updated file + EXPECT_EQ(changeList.m_updatedFiles.size(), 1); + EXPECT_TRUE( + AZStd::find(changeList.m_updatedFiles.begin(), changeList.m_updatedFiles.end(), "A.txt") != changeList.m_updatedFiles.end()); + + // Expect the change list to contain no deleted files + EXPECT_TRUE(changeList.m_deletedFiles.empty()); + } + + TEST(ChangeListFactoryTest, DeleteOnly_ExpectValidChangeListWithFileDeleteOperations) + { + // Given a unified diff with only one file deletion and no file creations or updates + const AZStd::string unifiedDiff = + "From f642a2f698452fc18484758b0046132415f09467 Mon Sep 17 00:00:00 2001\n" + "From: user \n" + "Date: Sat, 13 Mar 2021 22:58:07 +0000\n" + "Subject: Test\n" + "\n" + "---\n" + " B.txt | 1 -\n" + " delete mode 100644 B.txt\n" + "diff --git a/B.txt b/B.txt\n" + "deleted file mode 100644\n" + "index 01f02e3..0000000\n" + "--- a/B.txt\n" + "+++ /dev/null\n" + "@@ -1 +0,0 @@\n" + "-bbb\n" + "\\ No newline at end of file\n" + "-- \n" + "2.30.0.windows.2\n" + "\n" + "\n"; + + // When attempting to construct the change list + const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff); + + // Expect the change list to contain no created files + EXPECT_TRUE(changeList.m_createdFiles.empty()); + + // Expect the change list to contain no updated files + EXPECT_TRUE(changeList.m_updatedFiles.empty()); + + // Expect the change list to contain one deleted file + EXPECT_EQ(changeList.m_deletedFiles.size(), 1); + EXPECT_TRUE( + AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "B.txt") != changeList.m_deletedFiles.end()); + } + + TEST(ChangeListFactoryTest, ParseUnifiedDiffWithAllPossibleOperations_ExpectChangeListMatchingOperations) + { + // Given a unified diff with created files, updated files, deleted files, renamed files and moved files + const AZStd::string unifiedDiff = + "From f642a2f698452fc18484758b0046132415f09467 Mon Sep 17 00:00:00 2001\n" + "From: user \n" + "Date: Sat, 13 Mar 2021 22:58:07 +0000\n" + "Subject: Test\n" + "\n" + "---\n" + " A.txt | 2 +-\n" + " B.txt | 1 -\n" + " D.txt => Foo/D.txt | 0\n" + " E.txt => Foo/Y.txt | 0\n" + " New.txt | 1 +\n" + " C.txt => X.txt | 0\n" + " 6 files changed, 2 insertions(+), 2 deletions(-)\n" + " delete mode 100644 B.txt\n" + " rename D.txt => Foo/D.txt (100%)\n" + " rename E.txt => Foo/Y.txt (100%)\n" + " create mode 100644 New.txt\n" + " rename C.txt => X.txt (100%)\n" + "\n" + "diff --git a/A.txt b/A.txt\n" + "index 7c4a013..e132db2 100644\n" + "--- a/A.txt\n" + "+++ b/A.txt\n" + "@@ -1 +1 @@\n" + "-aaa\n" + "\\ No newline at end of file\n" + "+zzz\n" + "\\ No newline at end of file\n" + "diff --git a/B.txt b/B.txt\n" + "deleted file mode 100644\n" + "index 01f02e3..0000000\n" + "--- a/B.txt\n" + "+++ /dev/null\n" + "@@ -1 +0,0 @@\n" + "-bbb\n" + "\\ No newline at end of file\n" + "diff --git a/D.txt b/Foo/D.txt\n" + "similarity index 100%\n" + "rename from D.txt\n" + "rename to Foo/D.txt\n" + "diff --git a/E.txt b/Foo/Y.txt\n" + "similarity index 100%\n" + "rename from E.txt\n" + "rename to Foo/Y.txt\n" + "diff --git a/New.txt b/New.txt\n" + "new file mode 100644\n" + "index 0000000..30d74d2\n" + "--- /dev/null\n" + "+++ b/New.txt\n" + "@@ -0,0 +1 @@\n" + "+test\n" + "\\ No newline at end of file\n" + "diff --git a/C.txt b/X.txt\n" + "similarity index 100%\n" + "rename from C.txt\n" + "rename to X.txt\n" + "-- \n" + "2.30.0.windows.2\n" + "\n" + "\n"; + + // When attempting to construct the change list + const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff); + + // Expect the change list to contain the 4 created files + EXPECT_EQ(changeList.m_createdFiles.size(), 4); + EXPECT_TRUE( + AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "Foo/D.txt") != + changeList.m_createdFiles.end()); + EXPECT_TRUE( + AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "Foo/Y.txt") != + changeList.m_createdFiles.end()); + EXPECT_TRUE( + AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "X.txt") != changeList.m_createdFiles.end()); + EXPECT_TRUE( + AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "New.txt") != changeList.m_createdFiles.end()); + + // Expect the change list to contain the 1 updated file + EXPECT_EQ(changeList.m_updatedFiles.size(), 1); + EXPECT_TRUE( + AZStd::find(changeList.m_updatedFiles.begin(), changeList.m_updatedFiles.end(), "A.txt") != changeList.m_updatedFiles.end()); + + // Expect the change list to contain the 4 deleted files + EXPECT_EQ(changeList.m_deletedFiles.size(), 4); + EXPECT_TRUE( + AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "B.txt") != changeList.m_deletedFiles.end()); + EXPECT_TRUE( + AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "D.txt") != changeList.m_deletedFiles.end()); + EXPECT_TRUE( + AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "E.txt") != changeList.m_deletedFiles.end()); + EXPECT_TRUE( + AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "C.txt") != changeList.m_deletedFiles.end()); + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactModuleCoverageFactoryTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactModuleCoverageFactoryTest.cpp new file mode 100644 index 0000000000..137addc360 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactModuleCoverageFactoryTest.cpp @@ -0,0 +1,522 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +#include +#include + +namespace UnitTest +{ + TEST(CoberturaModuleCoveragesFactory, ParseEmptyString_ThrowsArtifactException) + { + // Given an empty string + const AZStd::string rawCoverage; + + try + { + // When attempting to parse the empty coverage + const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); + + // Do not expect the parsing to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(CoberturaModuleCoveragesFactory, ParseInvalidString_ThrowsArtifactException) + { + // Given an invalid string + const AZStd::string rawCoverage = "!@?"; + + try + { + // When attempting to parse the invalid coverage + const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); + + // Do not expect the parsing to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(CoberturaModuleCoveragesFactory, ParseEmptyCoverage_ExpectEmptyModuleCoverages) + { + // Given an empty coverage string + const AZStd::string rawCoverage = + "" + "" + " " + " " + "" + ""; + + // When attempting to parse the empty coverage + const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); + + // Expect an empty module coverages + EXPECT_TRUE(coverage.empty()); + } + + TEST(CoberturaModuleCoveragesFactory, ParseTestTargetLineCoverageA_ReturnsValidLineCoverage) + { + const AZStd::vector expectedCoverage = GetTestTargetALineModuleCoverages(); + + // Given the raw line coverage output of TestTargetA + const AZStd::string rawCoverage = + "" + "" + " " + " C:" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + // When the raw line coverage text is parsed + const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); + + // Expect the generated suite data to match that of the raw coverage text + EXPECT_TRUE(coverage == expectedCoverage); + } + + TEST(CoberturaModuleCoveragesFactory, ParseTestTargetSourceCoverageA_ReturnsValidSourceCoverage) + { + const AZStd::vector expectedCoverage = GetTestTargetASourceModuleCoverages(); + + // Given the raw source coverage output of TestTargetA + const AZStd::string rawCoverage = + "" + "" + " " + " C:" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + // When the raw source coverage text is parsed + const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); + + // Expect the generated suite data to match that of the raw coverage text + EXPECT_TRUE(coverage == expectedCoverage); + } + + TEST(CoberturaModuleCoveragesFactory, ParseTestTargetLineCoverageB_ReturnsValidLineCoverage) + { + const AZStd::vector expectedCoverage = GetTestTargetBLineModuleCoverages(); + + // Given the raw line coverage output of TestTargetB + const AZStd::string rawCoverage = + "" + "" + " " + " C:" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + // When the raw line coverage text is parsed + const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); + + // Expect the generated suite data to match that of the raw coverage text + EXPECT_TRUE(coverage == expectedCoverage); + } + + TEST(CoberturaModuleCoveragesFactory, ParseTestTargetSourceCoverageB_ReturnsValidSourceCoverage) + { + const AZStd::vector expectedCoverage = GetTestTargetBSourceModuleCoverages(); + + // Given the raw source coverage output of TestTargetB + const AZStd::string rawCoverage = + "" + "" + " " + " C:" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + // When the raw source coverage text is parsed + const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); + + // Expect the generated suite data to match that of the raw coverage text + EXPECT_TRUE(coverage == expectedCoverage); + } + + TEST(CoberturaModuleCoveragesFactory, ParseTestTargetLineCoverageC_ReturnsValidLineCoverage) + { + const AZStd::vector expectedCoverage = GetTestTargetCLineModuleCoverages(); + + // Given the raw line coverage output of TestTargetC + const AZStd::string rawCoverage = + "" + "" + " " + " C:" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + // When the raw line coverage text is parsed + const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); + + // Expect the generated suite data to match that of the raw coverage text + EXPECT_TRUE(coverage == expectedCoverage); + } + + TEST(CoberturaModuleCoveragesFactory, ParseTestTargetSourceCoverageC_ReturnsValidSourceCoverage) + { + const AZStd::vector expectedCoverage = GetTestTargetCSourceModuleCoverages(); + + // Given the raw source coverage output of TestTargetC + const AZStd::string rawCoverage = + "" + "" + " " + " C:" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + // When the raw source coverage text is parsed + const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); + + // Expect the generated suite data to match that of the raw coverage text + EXPECT_TRUE(coverage == expectedCoverage); + } + + TEST(CoberturaModuleCoveragesFactory, ParseTestTargetLineCoverageD_ReturnsValidLineCoverage) + { + const AZStd::vector expectedCoverage = GetTestTargetDLineModuleCoverages(); + + // Given the raw line coverage output of TestTargetD + const AZStd::string rawCoverage = + "" + "" + " " + " C:" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + // When the raw line coverage text is parsed + const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); + + // Expect the generated suite data to match that of the raw coverage text + EXPECT_TRUE(coverage == expectedCoverage); + } + + TEST(CoberturaModuleCoveragesFactory, ParseTestTargetSourceCoverageD_ReturnsValidSourceCoverage) + { + const AZStd::vector expectedCoverage = GetTestTargetDSourceModuleCoverages(); + + // Given the raw source coverage output of TestTargetD + const AZStd::string rawCoverage = + "" + "" + " " + " C:" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + // When the raw source coverage text is parsed + const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); + + // Expect the generated suite data to match that of the raw coverage text + EXPECT_TRUE(coverage == expectedCoverage); + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTargetDescriptorCompilerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTargetDescriptorCompilerTest.cpp new file mode 100644 index 0000000000..1e72902b4e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTargetDescriptorCompilerTest.cpp @@ -0,0 +1,150 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +#include +#include + +namespace UnitTest +{ + class TargetDescriptorCompilerTestFixture + : public AllocatorsTestFixture + { + public: + void SetUp() override + { + AllocatorsTestFixture::SetUp(); + + m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"TestTargetA", "", ""}, {}}); + m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"TestTargetB", "", ""}, {}}); + m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"ProductionTargetA", "", ""}, {}}); + m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"ProductionTargetB", "", ""}, {}}); + m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"ProductionTargetC", "", ""}, {}}); + + m_testTargetMetaMap.emplace("TestTargetA", TestImpact::TestTargetMeta{"", TestImpact::LaunchMethod::TestRunner}); + m_testTargetMetaMap.emplace("TestTargetB", TestImpact::TestTargetMeta{"", TestImpact::LaunchMethod::StandAlone}); + } + + protected: + AZStd::vector m_buildTargetDescriptors; + TestImpact::TestTargetMetaMap m_testTargetMetaMap; + }; + + TestImpact::ProductionTargetDescriptor ConstructProductionTargetDescriptor(const AZStd::string& name) + { + return TestImpact::ProductionTargetDescriptor{TestImpact::BuildTargetDescriptor{{name, "", ""}, {{}, {}}}}; + } + + TestImpact::TestTargetDescriptor ConstructTestTargetDescriptor(const AZStd::string& name, TestImpact::LaunchMethod launchMethod) + { + return TestImpact::TestTargetDescriptor{ + TestImpact::BuildTargetDescriptor{{name, "", ""}, {{}, {}}}, TestImpact::TestTargetMeta{"", launchMethod}}; + } + + TEST_F(TargetDescriptorCompilerTestFixture, EmptyBuildTargetDescriptorList_ExpectArtifactException) + { + try + { + // Given an empty build target descriptor list but valid test target meta map + // When attempting to construct the test target + const auto& [productionTargetDescriptors, testTargetDescriptors] = + TestImpact::CompileTargetDescriptors({}, AZStd::move(m_testTargetMetaMap)); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(TargetDescriptorCompilerTestFixture, EmptyTestTargetMetaMap_ExpectArtifactException) + { + try + { + // Given a valid build target descriptor list but empty test target meta map + // When attempting to construct the test target + const auto& [productionTargetDescriptors, testTargetDescriptors] = + TestImpact::CompileTargetDescriptors(AZStd::move(m_buildTargetDescriptors), {}); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(TargetDescriptorCompilerTestFixture, TestTargetWithNoMatchingMeta_ExpectArtifactException) + { + try + { + // Given a valid build target descriptor list but a test target meta map with an orphan entry + m_testTargetMetaMap.emplace("Orphan", TestImpact::TestTargetMeta{"", TestImpact::LaunchMethod::TestRunner}); + + // When attempting to construct the test target + const auto& [productionTargetDescriptors, testTargetDescriptors] = + TestImpact::CompileTargetDescriptors(AZStd::move(m_buildTargetDescriptors), {}); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(TargetDescriptorCompilerTestFixture, ValidProductionTargetsAndTestTargetMetas_ExpectValidProductionAndTestTargets) + { + // Given a valid build target descriptor list and a valid test target meta map + // When attempting to construct the test target + const auto& [productionTargetDescriptors, testTargetDescriptors] = + TestImpact::CompileTargetDescriptors(AZStd::move(m_buildTargetDescriptors), AZStd::move(m_testTargetMetaMap)); + + // Expect the production targets to match the expected targets + EXPECT_TRUE(productionTargetDescriptors.size() == 3); + EXPECT_TRUE(productionTargetDescriptors[0] == ConstructProductionTargetDescriptor("ProductionTargetA")); + EXPECT_TRUE(productionTargetDescriptors[1] == ConstructProductionTargetDescriptor("ProductionTargetB")); + EXPECT_TRUE(productionTargetDescriptors[2] == ConstructProductionTargetDescriptor("ProductionTargetC")); + + // Expect the test targets to match the expected targets + EXPECT_TRUE(testTargetDescriptors.size() == 2); + EXPECT_TRUE(testTargetDescriptors[0] == ConstructTestTargetDescriptor("TestTargetA", TestImpact::LaunchMethod::TestRunner)); + EXPECT_TRUE(testTargetDescriptors[1] == ConstructTestTargetDescriptor("TestTargetB", TestImpact::LaunchMethod::StandAlone)); + } + +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestEnumerationSuiteFactoryTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestEnumerationSuiteFactoryTest.cpp new file mode 100644 index 0000000000..d6e1dc5920 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestEnumerationSuiteFactoryTest.cpp @@ -0,0 +1,589 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + TEST(GTestEnumerationSuiteFactory, ParseEmptyString_ThrowsArtifactException) + { + // Given an empty string + const AZStd::string rawEnum; + + try + { + // When attempting to parse the empty suite + const AZStd::vector suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum); + + // Do not expect the parsing to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(GTestEnumerationSuiteFactory, ParseInvalidString_ThrowsArtifactException) + { + // Given an invalid string + const AZStd::string rawEnum = "!@?"; + + try + { + // When attempting to parse the empty suite + const AZStd::vector suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum); + + // Do not expect the parsing to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(GTestEnumerationSuiteFactory, ParseTestTargetA_ReturnsValidSuitesAndTests) + { + const AZStd::vector expectedSuites = GetTestTargetATestEnumerationSuites(); + + // Given the raw enum output of TestTargetA + const AZStd::string rawEnum = + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + "\n"; + + // When the raw enumeration text is parsed + const AZStd::vector suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum); + + // Expect the generated suite data to match that of the raw enum text + EXPECT_TRUE(suites == expectedSuites); + } + + TEST(GTestEnumerationSuiteFactory, ParseTestTargetB_ReturnsValidSuitesAndTests) + { + const AZStd::vector expectedSuites = GetTestTargetBTestEnumerationSuites(); + + // Given the raw enum output of TestTargetB + const AZStd::string rawEnum = + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + "\n"; + + // When the raw enumeration text is parsed + const AZStd::vector suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum); + + // Expect the generated suite data to match that of the raw enum text + EXPECT_TRUE(suites == expectedSuites); + } + + TEST(GTestEnumerationSuiteFactory, ParseTestTargetC_ReturnsValidSuitesAndTests) + { + const AZStd::vector expectedSuites = GetTestTargetCTestEnumerationSuites(); + + // Given the raw enum output of TestTargetC + const AZStd::string rawEnum = + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + "\n"; + + // When the raw enumeration text is parsed + const AZStd::vector suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum); + + // Expect the generated suite data to match that of the raw enum text + EXPECT_TRUE(suites == expectedSuites); + } + + TEST(GTestEnumerationSuiteFactory, ParseTestTargetD_ReturnsValidSuitesAndTests) + { + const AZStd::vector expectedSuites = GetTestTargetDTestEnumerationSuites(); + + // Given the raw enum output of TestTargetD + const AZStd::string rawEnum = + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + "\n"; + + // When the raw enumeration text is parsed + const AZStd::vector suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum); + + // Expect the generated suite data to match that of the raw enum text + EXPECT_TRUE(suites == expectedSuites); + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestRunSuiteFactoryTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestRunSuiteFactoryTest.cpp new file mode 100644 index 0000000000..0f0a7dfdd8 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestRunSuiteFactoryTest.cpp @@ -0,0 +1,592 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + TEST(GTestRunSuiteFactory, ParseEmptyString_ThrowsArtifactException) + { + // Given an empty string + const AZStd::string rawRun; + + try + { + // When attempting to parse the empty suite + const AZStd::vector suites = TestImpact::GTest::TestRunSuitesFactory(rawRun); + + // Do not expect the parsing to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect a artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(GTestRunSuiteFactory, ParseInvalidString_ThrowsArtifactException) + { + // Given an invalid string + const AZStd::string rawRun = "!@?"; + + try + { + // When attempting to parse the invalid suite + const AZStd::vector suites = TestImpact::GTest::TestRunSuitesFactory(rawRun); + + // Do not expect the parsing to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect a artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(GTestRunSuiteFactory, ParseTestTargetA_ReturnsValidSuitesAndTests) + { + const AZStd::vector expectedSuites = GetTestTargetATestRunSuites(); + + // Given the raw run output of TestTargetA + const AZStd::string rawRun = + "" + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + // When the raw run text is parsed + const AZStd::vector suites = TestImpact::GTest::TestRunSuitesFactory(rawRun); + + // Expect the generated suite data to match that of the raw run text + EXPECT_TRUE(suites == expectedSuites); + } + + TEST(GTestRunSuiteFactory, ParseTestTargetB_ReturnsValidSuitesAndTests) + { + const AZStd::vector expectedSuites = GetTestTargetBTestRunSuites(); + + // Given the raw run output of TestTargetB + const AZStd::string rawRun = + "" + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + // When the raw run text is parsed + const AZStd::vector suites = TestImpact::GTest::TestRunSuitesFactory(rawRun); + + // Expect the generated suite data to match that of the raw run text + EXPECT_TRUE(suites == expectedSuites); + } + + TEST(GTestRunSuiteFactory, ParseTestTargetC_ReturnsValidSuitesAndTests) + { + const AZStd::vector expectedSuites = GetTestTargetCTestRunSuites(); + + // Given the raw run output of TestTargetC + const AZStd::string rawRun = + "" + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + // When the raw run text is parsed + const AZStd::vector suites = TestImpact::GTest::TestRunSuitesFactory(rawRun); + + // Expect the generated suite data to match that of the raw run text + EXPECT_TRUE(suites == expectedSuites); + } + + TEST(GTestRunSuiteFactory, ParseTestTargetD_ReturnsValidSuitesAndTests) + { + const AZStd::vector expectedSuites = GetTestTargetDTestRunSuites(); + + // Given the raw run output of TestTargetD + const AZStd::string rawRun = + "" + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + // When the raw run text is parsed + const AZStd::vector suites = TestImpact::GTest::TestRunSuitesFactory(rawRun); + + // Expect the generated suite data to match that of the raw run text + EXPECT_TRUE(suites == expectedSuites); + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestTargetMetaMapFactoryTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestTargetMetaMapFactoryTest.cpp new file mode 100644 index 0000000000..588c46d1ce --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestTargetMetaMapFactoryTest.cpp @@ -0,0 +1,239 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +#include +#include + +namespace UnitTest +{ + TEST(TestTargetMetaMapFactoryTest, NoRawData_ExpectArtifactException) + { + // Given an empty meta data string + const AZStd::string rawTestTargetMetaData; + + try + { + // When attempting to construct the test target + const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(TestTargetMetaMapFactoryTest, InvalidRawData_ExpectArtifactException) + { + // Given a raw meta data string of invalid data + const AZStd::string rawTestTargetMetaData = "abcde"; + + try + { + // When attempting to construct the test target + const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(TestTargetMetaMapFactoryTest, EmptyTestMetaArray_ExpectArtifactException) + { + // Given a raw meta data string of valid JSON that contains no tests + const AZStd::string rawTestTargetMetaData = + "{" + " \"google\": {" + " \"test\": {" + " \"tests\": [" + " ]" + " }" + " }" + "}"; + + try + { + // When attempting to construct the test target + const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(TestTargetMetaMapFactoryTest, EmptyName_ExpectArtifactException) + { + // Given a raw meta data string with a test that has no name value + const AZStd::string rawTestTargetMetaData = + "{" + " \"google\": {" + " \"test\": {" + " \"tests\": [" + " { \"name\": \"\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"test_runner\" }" + " ]" + " }" + " }" + "}"; + + try + { + // When attempting to construct the test target + const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(TestTargetMetaMapFactoryTest, EmptyBuildType_ExpectArtifactException) + { + // Given a raw meta data string with a test that has no build type + const AZStd::string rawTestTargetMetaData = + "{" + " \"google\": {" + " \"test\": {" + " \"tests\": [" + " { \"name\": \"TestName\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"\" }" + " ]" + " }" + " }" + "}"; + + try + { + // When attempting to construct the test target + const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(TestTargetMetaMapFactoryTest, InvalidBuildType_ExpectArtifactException) + { + // Given a raw meta data string with a test that has an invalid build type + const AZStd::string rawTestTargetMetaData = + "{" + " \"google\": {" + " \"test\": {" + " \"tests\": [" + " { \"name\": \"TestName\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"Unknown\" }" + " ]" + " }" + " }" + "}"; + + try + { + // When attempting to construct the test target + const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an artifact exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(TestTargetMetaMapFactoryTest, ValidMetaData_ExpectValidTestMetaData) + { + // Given a raw meta data string valid test meta-data + const AZStd::string rawTestTargetMetaData = + "{" + " \"google\": {" + " \"test\": {" + " \"tests\": [" + " { \"name\": \"TestA\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"test_runner\" }," + " { \"name\": \"TestB\", \"namespace\": \"\", \"suite\": \"main\", \"launch_method\": \"test_runner\" }," + " { \"name\": \"TestC\", \"namespace\": \"\", \"suite\": \"\", \"launch_method\": \"test_runner\" }," + " { \"name\": \"TestD\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"stand_alone\" }" + " ]" + " }" + " }" + "}"; + + // When attempting to construct the test target + const auto testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); + + // Expect the constructed test meta-data to match that of the supplied raw data + EXPECT_EQ(testTargetMetaData.size(), 4); + EXPECT_TRUE(testTargetMetaData.find("TestA") != testTargetMetaData.end()); + EXPECT_TRUE((testTargetMetaData.at("TestA") == TestImpact::TestTargetMeta{"main", TestImpact::LaunchMethod::TestRunner})); + EXPECT_TRUE(testTargetMetaData.find("TestB") != testTargetMetaData.end()); + EXPECT_TRUE((testTargetMetaData.at("TestB") == TestImpact::TestTargetMeta{"main", TestImpact::LaunchMethod::TestRunner})); + EXPECT_TRUE(testTargetMetaData.find("TestC") != testTargetMetaData.end()); + EXPECT_TRUE((testTargetMetaData.at("TestC") == TestImpact::TestTargetMeta{"", TestImpact::LaunchMethod::TestRunner})); + EXPECT_TRUE(testTargetMetaData.find("TestD") != testTargetMetaData.end()); + EXPECT_TRUE((testTargetMetaData.at("TestD") == TestImpact::TestTargetMeta{"main", TestImpact::LaunchMethod::StandAlone})); + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessSchedulerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessSchedulerTest.cpp new file mode 100644 index 0000000000..c64b05e431 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessSchedulerTest.cpp @@ -0,0 +1,718 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace UnitTest +{ + using SchedulerPermutation = AZStd::tuple + < + size_t, // Number of concurrent processes + size_t // Number of processes to launch + >; + + class ProcessSchedulerTestFixtureWithParams + : public AllocatorsTestFixture + , private AZ::Debug::TraceMessageBus::Handler + , public ::testing::WithParamInterface + { + private: + bool OnOutput(const char*, const char*) override; + void SetUp() override; + void TearDown() override; + + protected: + void ConfigurePermutation(); + void ExpectSuccessfulLaunch(TestImpact::ProcessId pid); + void ExpectGracefulExit(TestImpact::ProcessId pid); + void ExpectUnsuccessfulLaunch(TestImpact::ProcessId pid); + void ExpectExitCondition( + TestImpact::ProcessId pid, AZStd::optional expectedExitCondition = AZStd::nullopt); + void DoNotExpectExitCondition(TestImpact::ProcessId pid); + void ExpectTerminatedProcess(TestImpact::ProcessId pid); + void ExpectTimeoutProcess(TestImpact::ProcessId pid); + void ExpectUnlaunchedProcess(TestImpact::ProcessId pid); + void ExpectLargeStdOutput(TestImpact::ProcessId pid); + void ExpectLargeStdError(TestImpact::ProcessId pid); + void ExpectSmallStdOutput(TestImpact::ProcessId pid); + void ExpectSmallStdError(TestImpact::ProcessId pid); + void DoNotExpectStdOutput(TestImpact::ProcessId pid); + void DoNotExpectStdError(TestImpact::ProcessId pid); + + struct ProcessResult + { + AZStd::optional m_launchResult; + AZStd::optional m_exitStatus; + AZStd::optional m_createTime; + AZStd::optional m_exitTime; + AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds(0); + AZStd::optional m_returnCode; + TestImpact::StdContent m_std; + }; + + size_t m_numMaxConcurrentProcesses = 0; + size_t m_numProcessesToLaunch = 0; + + AZStd::unique_ptr m_processScheduler; + TestImpact::ProcessLaunchCallback m_processLaunchCallback; + TestImpact::ProcessExitCallback m_processExitCallback; + AZStd::vector m_processInfos; + AZStd::vector m_processResults; + }; + + // Permutation values for small process batches + const std::vector SmallNumMaxConcurrentProcesses = {1, 4, 8}; + const std::vector SmallNumProcessesToLaunch = {1, 8, 32}; + + // Permutation values for large process batches + const std::vector LargeNumMaxConcurrentProcesses = {16, 32, 64}; + const std::vector LargeNumProcessesToLaunch = {128, 256, 512}; + + void ProcessSchedulerTestFixtureWithParams::ConfigurePermutation() + { + const auto& [numMaxConcurrentProcesses, numProcessesToLaunch] = GetParam(); + m_numMaxConcurrentProcesses = numMaxConcurrentProcesses; + m_numProcessesToLaunch = numProcessesToLaunch; + } + + // Consume AZ log spam + bool ProcessSchedulerTestFixtureWithParams::OnOutput([[maybe_unused]] const char*, [[maybe_unused]] const char*) + { + return true; + } + + void ProcessSchedulerTestFixtureWithParams::SetUp() + { + UnitTest::AllocatorsTestFixture::SetUp(); + AZ::Debug::TraceMessageBus::Handler::BusConnect(); + ConfigurePermutation(); + + m_processLaunchCallback = [this]( + TestImpact::ProcessId pid, + TestImpact::LaunchResult launchResult, + AZStd::chrono::high_resolution_clock::time_point createTime) + { + m_processResults[pid].m_launchResult = launchResult; + m_processResults[pid].m_createTime.emplace(createTime); + return TestImpact::CallbackResult::Continue; + }; + + m_processExitCallback = [this]( + TestImpact::ProcessId pid, + TestImpact::ExitCondition exitStatus, + TestImpact::ReturnCode returnCode, + TestImpact::StdContent&& std, + AZStd::chrono::high_resolution_clock::time_point exitTime) + { + m_processResults[pid].m_std = std::move(std); + m_processResults[pid].m_returnCode.emplace(returnCode); + m_processResults[pid].m_exitStatus = exitStatus; + m_processResults[pid].m_exitTime = exitTime; + m_processResults[pid].m_duration = + AZStd::chrono::duration_cast(exitTime - *m_processResults[pid].m_createTime); + return TestImpact::CallbackResult::Continue; + }; + + m_processResults.resize(m_numProcessesToLaunch); + } + + void ProcessSchedulerTestFixtureWithParams::TearDown() + { + AZ::Debug::TraceMessageBus::Handler::BusDisconnect(); + UnitTest::AllocatorsTestFixture::TearDown(); + } + + // Expects the process to have exited under the specified circumstances + void ProcessSchedulerTestFixtureWithParams::ExpectExitCondition( + TestImpact::ProcessId pid, AZStd::optional expectedExitCondition) + { + const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; + + // Expect the processes to have exited + EXPECT_TRUE(exitStatus.has_value()); + + // Expect the return code to be valid + EXPECT_TRUE(returnCode.has_value()); + + if (expectedExitCondition.has_value()) + { + // Expect the process to have exited under the specified conditions + EXPECT_EQ(exitStatus, expectedExitCondition); + + // Expect the return code to be that of the process's is (for gracefull exists) or that of the error code + // associated with the abnormal exit condition + EXPECT_EQ(returnCode, + expectedExitCondition == TestImpact::ExitCondition::Gracefull + ? pid + : static_cast(*exitStatus)); + } + + // Expect the duration to be non zero and the create time and exit time to have values as the processes has been in-flight + EXPECT_GT(duration.count(), 0); + EXPECT_TRUE(createTime.has_value()); + EXPECT_TRUE(exitTime.has_value()); + + // Expect the exit time to be later than the start time + EXPECT_GT(exitTime, createTime); + } + + // Expects the process to not have exited for to lack of being in-flight + void ProcessSchedulerTestFixtureWithParams::DoNotExpectExitCondition(TestImpact::ProcessId pid) + { + const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; + + // Expect the processes to not have exited as the process has not been launched successfully + EXPECT_FALSE(exitStatus.has_value()); + + // Expect the return code to be invalid + EXPECT_FALSE(returnCode.has_value()); + + // Expect the duration to be zero as the process has not been in-flight + EXPECT_EQ(duration.count(), 0); + + // Do not expect the exit time to have a value + EXPECT_FALSE(exitTime.has_value()); + } + + // Expects the process to have been launched successfully, makes no assumptions about how it exited + void ProcessSchedulerTestFixtureWithParams::ExpectSuccessfulLaunch(TestImpact::ProcessId pid) + { + const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; + + // Expect a launch to have been attempted by this process (not still in queue) + EXPECT_TRUE(launchResult.has_value()); + + // Expect the process to have launched successfully + EXPECT_EQ(launchResult, TestImpact::LaunchResult::Success); + } + + // Expects the process to have failed to launch + void ProcessSchedulerTestFixtureWithParams::ExpectUnsuccessfulLaunch(TestImpact::ProcessId pid) + { + const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; + + // Expect a launch to have been attempted by this process (not still in queue) + EXPECT_TRUE(launchResult.has_value()); + + // Expect the process to have launched unsuccessfully + EXPECT_EQ(launchResult, TestImpact::LaunchResult::Failure); + + // Expect the create time to have a value as the process was technically created + EXPECT_TRUE(createTime.has_value()); + + DoNotExpectExitCondition(pid); + } + + // Expects the process to have exited gracefully of its own accord (i.e. not terminated for any reason by the scheduler) + void ProcessSchedulerTestFixtureWithParams::ExpectGracefulExit(TestImpact::ProcessId pid) + { + const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; + + ExpectSuccessfulLaunch(pid); + ExpectExitCondition(pid, TestImpact::ExitCondition::Gracefull); + } + + // Expects the process to have been terminated by the client or scheduler + void ProcessSchedulerTestFixtureWithParams::ExpectTerminatedProcess(TestImpact::ProcessId pid) + { + const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; + + ExpectSuccessfulLaunch(pid); + ExpectExitCondition(pid, TestImpact::ExitCondition::Terminated); + } + + // Expects the process to have been terminated by the scheduler due to the process or scheduler timing out + void ProcessSchedulerTestFixtureWithParams::ExpectTimeoutProcess(TestImpact::ProcessId pid) + { + const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; + + ExpectSuccessfulLaunch(pid); + ExpectExitCondition(pid, TestImpact::ExitCondition::Timeout); + } + + // Expects the process to have not been attempted to launch (was still in the queue) + void ProcessSchedulerTestFixtureWithParams::ExpectUnlaunchedProcess(TestImpact::ProcessId pid) + { + const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; + + // Expect a launch to not have been attempted by this process (still in queue) + EXPECT_FALSE(launchResult.has_value()); + + // Expect the create time to have a value as the process was technically created + EXPECT_FALSE(createTime.has_value()); + + DoNotExpectExitCondition(pid); + } + + // Expects the std output to have been captured from the process with a large volume of text + void ProcessSchedulerTestFixtureWithParams::ExpectLargeStdOutput(TestImpact::ProcessId pid) + { + const auto& stdOut = m_processResults[pid].m_std.m_out; + + // Expect standard output to have a value + EXPECT_TRUE(stdOut.has_value()); + + // Expect the output length to be that of the large text output from the child process + EXPECT_EQ(stdOut.value().length(), LargeTextSize); + } + + // Expects the std error to have been captured from the process with a large volume of text + void ProcessSchedulerTestFixtureWithParams::ExpectLargeStdError(TestImpact::ProcessId pid) + { + const auto& stdErr = m_processResults[pid].m_std.m_err; + + // Expect standard error to have a value + EXPECT_TRUE(stdErr.has_value()); + + // Expect the error length to be that of the large text output from the child process + EXPECT_EQ(stdErr.value().length(), LargeTextSize); + } + + // Expects the std output to have been captured from the process with a small known text string + void ProcessSchedulerTestFixtureWithParams::ExpectSmallStdOutput(TestImpact::ProcessId pid) + { + const auto& stdOut = m_processResults[pid].m_std.m_out; + + // Expect standard output to have a value + EXPECT_TRUE(stdOut.has_value()); + + // Expect the output to match the known stdout of the child + EXPECT_EQ(stdOut.value(), KnownTestProcessOutputString(pid)); + } + + // Expects the std error to have been captured from the process with a small known text string + void ProcessSchedulerTestFixtureWithParams::ExpectSmallStdError(TestImpact::ProcessId pid) + { + const auto& stdErr = m_processResults[pid].m_std.m_err; + + // Expect standard error to have a value + EXPECT_TRUE(stdErr.has_value()); + + // Expect the output to match the known stderr of the child + EXPECT_EQ(stdErr.value(), KnownTestProcessErrorString(pid)); + } + + // Expects no standard output from the child process + void ProcessSchedulerTestFixtureWithParams::DoNotExpectStdOutput(TestImpact::ProcessId pid) + { + const auto& stdOut = m_processResults[pid].m_std.m_out; + + // Do not expect standard output to have a value + EXPECT_FALSE(stdOut.has_value()); + } + + // Expects no standard error from the child process + void ProcessSchedulerTestFixtureWithParams::DoNotExpectStdError(TestImpact::ProcessId pid) + { + const auto& stdErr = m_processResults[pid].m_std.m_err; + + // Do not expect standard error to have a value + EXPECT_FALSE(stdErr.has_value()); + } + + TEST_P(ProcessSchedulerTestFixtureWithParams, ValidProcesses_SuccessfulLaunches) + { + // Given a set of processes to launch with valid arguments + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + m_processInfos.emplace_back( + pid, + ValidProcessPath, + ConstructTestProcessArgs(pid, NoSleep)); + } + + // When the process scheduler launches the processes + m_processScheduler = AZStd::make_unique( + m_processInfos, + m_processLaunchCallback, + m_processExitCallback, + m_numMaxConcurrentProcesses, + AZStd::nullopt, + AZStd::nullopt + ); + + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + ExpectGracefulExit(pid); + } + } + + TEST_P(ProcessSchedulerTestFixtureWithParams, ValidAndInvalidProcesses_LaunchSuccessesAndFailures) + { + const size_t invalidProcessGroup = 4; + + // Given a mixture of processes to launch with valid and invalid arguments + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + if (pid % invalidProcessGroup == 0) + { + m_processInfos.emplace_back( + pid, + InvalidProcessPath, + ConstructTestProcessArgs(pid, NoSleep)); + } + else + { + m_processInfos.emplace_back( + pid, + ValidProcessPath, + ConstructTestProcessArgs(pid, NoSleep)); + } + } + + // When the process scheduler launches the processes + m_processScheduler = AZStd::make_unique( + m_processInfos, + m_processLaunchCallback, + m_processExitCallback, + m_numMaxConcurrentProcesses, + AZStd::nullopt, + AZStd::nullopt + ); + + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + if (pid % invalidProcessGroup == 0) + { + ExpectUnsuccessfulLaunch(pid); + } + else + { + ExpectGracefulExit(pid); + } + + DoNotExpectStdOutput(pid); + DoNotExpectStdError(pid); + } + } + + TEST_P(ProcessSchedulerTestFixtureWithParams, ProcessTimeouts_InFlightProcessesTimeout) + { + const size_t timeoutProcessGroup = 4; + + // Given a mixture of processes to launch with some processes sleeping indefinately + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + if (pid % timeoutProcessGroup == 0) + { + m_processInfos.emplace_back(pid, ValidProcessPath, ConstructTestProcessArgs(pid, LongSleep)); + } + else + { + m_processInfos.emplace_back(pid, ValidProcessPath, ConstructTestProcessArgs(pid, NoSleep)); + } + } + + // When the process scheduler launches the processes with a process timeout value + m_processScheduler = AZStd::make_unique( + m_processInfos, + m_processLaunchCallback, + m_processExitCallback, + m_numMaxConcurrentProcesses, + AZStd::chrono::milliseconds(100), + AZStd::nullopt + ); + + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + if (pid % timeoutProcessGroup == 0) + { + ExpectTimeoutProcess(pid); + } + else + { + ExpectExitCondition(pid); + } + + DoNotExpectStdOutput(pid); + DoNotExpectStdError(pid); + } + } + + TEST_P( + ProcessSchedulerTestFixtureWithParams, ProcessLaunchCallbackAbort_InFlightProcessesTerminatedAndQueuedProcessesUnlaunched) + { + const size_t processToAbort = 8; + + // Given a set of processes to launch + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + m_processInfos.emplace_back( + pid, + ValidProcessPath, + ConstructTestProcessArgs(pid, NoSleep)); + } + + // Given a launch callback that will return the abort value for the process to abort + const auto abortingLaunchCallback = [this, processToAbort]( + TestImpact::ProcessId pid, + TestImpact::LaunchResult launchResult, + AZStd::chrono::high_resolution_clock::time_point createTime) + { + m_processLaunchCallback(pid, launchResult, createTime); + + if (pid == processToAbort) + { + return TestImpact::CallbackResult::Abort; + } + else + { + return TestImpact::CallbackResult::Continue; + } + }; + + // When the process scheduler launches the processes + m_processScheduler = AZStd::make_unique( + m_processInfos, + abortingLaunchCallback, + m_processExitCallback, + m_numMaxConcurrentProcesses, + AZStd::nullopt, + AZStd::nullopt + ); + + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + if (pid < processToAbort) + { + ExpectSuccessfulLaunch(pid); + } + else if (pid == processToAbort) + { + ExpectTerminatedProcess(pid); + } + else + { + ExpectUnlaunchedProcess(pid); + } + + DoNotExpectStdOutput(pid); + DoNotExpectStdError(pid); + } + } + + TEST_P(ProcessSchedulerTestFixtureWithParams, ProcessExitCallbackAbort_InFlightProcessesTerminated) + { + const size_t processToAbort = 4; + + // Given a set of processes to launch + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + m_processInfos.emplace_back( + pid, + ValidProcessPath, + ConstructTestProcessArgs(pid, NoSleep)); + } + + // Given an exit callback that will return the abort value for the process to abort + const auto abortingExitCallback = [this, processToAbort]( + TestImpact::ProcessId pid, + TestImpact::ExitCondition exitStatus, + TestImpact::ReturnCode returnCode, + TestImpact::StdContent&& std, + AZStd::chrono::high_resolution_clock::time_point exitTime) + { + m_processExitCallback(pid, exitStatus, returnCode, std::move(std), exitTime); + + if (pid == processToAbort) + { + return TestImpact::CallbackResult::Abort; + } + else + { + return TestImpact::CallbackResult::Continue; + } + }; + + // When the process scheduler launches the processes + m_processScheduler = AZStd::make_unique( + m_processInfos, + m_processLaunchCallback, + abortingExitCallback, + m_numMaxConcurrentProcesses, + AZStd::nullopt, + AZStd::nullopt + ); + + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + if (pid < processToAbort) + { + ExpectSuccessfulLaunch(pid); + } + else if (pid == processToAbort) + { + ExpectGracefulExit(pid); + } + else + { + if (m_processResults[pid].m_launchResult.has_value()) + { + ExpectSuccessfulLaunch(pid); + } + else + { + ExpectUnlaunchedProcess(pid); + } + } + + DoNotExpectStdOutput(pid); + DoNotExpectStdError(pid); + } + } + + TEST_P(ProcessSchedulerTestFixtureWithParams, SchedulerTimeout_QueuedAndInFlightProcessesTerminated) + { + const size_t processToTimeout = 0; + + // Given a set of processes to launch where one process will sleep indefinately + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + if (pid == processToTimeout) + { + m_processInfos.emplace_back( + pid, + ValidProcessPath, + ConstructTestProcessArgs(pid, LongSleep)); + } + else + { + m_processInfos.emplace_back( + pid, + ValidProcessPath, + ConstructTestProcessArgs(pid, NoSleep)); + } + } + + // When the process scheduler launches the processes with a scheduler timeout value + m_processScheduler = AZStd::make_unique( + m_processInfos, + m_processLaunchCallback, + m_processExitCallback, + m_numMaxConcurrentProcesses, + AZStd::nullopt, + AZStd::chrono::milliseconds(100) + ); + + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + if (pid == processToTimeout) + { + ExpectTimeoutProcess(pid); + } + else + { + if (m_processResults[pid].m_launchResult.has_value()) + { + ExpectSuccessfulLaunch(pid); + } + else + { + ExpectUnlaunchedProcess(pid); + } + } + + DoNotExpectStdOutput(pid); + DoNotExpectStdError(pid); + } + } + + TEST_P(ProcessSchedulerTestFixtureWithParams, RedirectStdOut_StdOutputIsLargeTextString) + { + // Given a set of processes to launch + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + m_processInfos.emplace_back( + pid, + TestImpact::StdOutputRouting::ToParent, + TestImpact::StdErrorRouting::None, + ValidProcessPath, + ConstructTestProcessArgsLargeText(pid, NoSleep)); + } + + // When the process scheduler launches the processes + m_processScheduler = AZStd::make_unique( + m_processInfos, + m_processLaunchCallback, + m_processExitCallback, + m_numMaxConcurrentProcesses, + AZStd::nullopt, + AZStd::nullopt + ); + + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + ExpectGracefulExit(pid); + ExpectLargeStdOutput(pid); + DoNotExpectStdError(pid); + } + } + + TEST_P(ProcessSchedulerTestFixtureWithParams, RedirectStdError_StdErrorIsLargeTextString) + { + // Given a set of processes to launch + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + m_processInfos.emplace_back( + pid, + TestImpact::StdOutputRouting::None, + TestImpact::StdErrorRouting::ToParent, + ValidProcessPath, + ConstructTestProcessArgsLargeText(pid, NoSleep)); + } + + // When the process scheduler launches the processes + m_processScheduler = AZStd::make_unique( + m_processInfos, + m_processLaunchCallback, + m_processExitCallback, + m_numMaxConcurrentProcesses, + AZStd::nullopt, + AZStd::nullopt + ); + + for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) + { + ExpectGracefulExit(pid); + DoNotExpectStdOutput(pid); + ExpectLargeStdError(pid); + } + } + + INSTANTIATE_TEST_CASE_P( + SmallConcurrentProcesses, + ProcessSchedulerTestFixtureWithParams, + ::testing::Combine( + ::testing::ValuesIn(SmallNumMaxConcurrentProcesses), + ::testing::ValuesIn(SmallNumProcessesToLaunch) + )); + + INSTANTIATE_TEST_CASE_P( + LargeConcurrentProcesses, + ProcessSchedulerTestFixtureWithParams, + ::testing::Combine( + ::testing::ValuesIn(LargeNumMaxConcurrentProcesses), + ::testing::ValuesIn(LargeNumProcessesToLaunch) + )); +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessTest.cpp new file mode 100644 index 0000000000..df4ea5a967 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessTest.cpp @@ -0,0 +1,491 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +#include +#include +#include + +namespace UnitTest +{ + class ProcessTestFixture + : public AllocatorsTestFixture + { + protected: + void SetUp() override + { + UnitTest::AllocatorsTestFixture::SetUp(); + } + + void TearDown() override + { + if (m_process && m_process->IsRunning()) + { + m_process->Terminate(0); + } + + UnitTest::AllocatorsTestFixture::TearDown(); + } + + AZStd::unique_ptr m_process; + static constexpr const TestImpact::ProcessId m_id = 1; + static constexpr const TestImpact::ReturnCode m_terminateErrorCode = 666; + }; + + TEST_F(ProcessTestFixture, LaunchValidProcess_ProcessReturnsSuccessfully) + { + // Given a process launched with a valid binary + TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process has exited + m_process->BlockUntilExit(); + + // Expect the process to no longer be running + EXPECT_FALSE(m_process->IsRunning()); + } + + TEST_F(ProcessTestFixture, LaunchInvalidProcessInfo_ThrowsProcessException) + { + try + { + // Given a process launched with an invalid binary + TestImpact::ProcessInfo processInfo(m_id, ""); + + // Do not expect this code to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ProcessException& e) + { + // Except a process exception to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(ProcessTestFixture, LaunchInvalidBinary_ThrowsProcessException) + { + try + { + // Given a process launched with an ivalid binary + TestImpact::ProcessInfo processInfo(m_id, "#!#zz:/z/z/z.exe.z@"); + + // When the process is attempted launch + m_process = TestImpact::LaunchProcess(processInfo); + + // Do not expect this code to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ProcessException& e) + { + // Except a process exception to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(ProcessTestFixture, GetProcessInfo_ReturnsProcessInfo) + { + // Given a process launched with a valid binary and args + const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); + TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process has exited + m_process->BlockUntilExit(); + + // Expect the retrieved process info to match to process info used to launch the process + EXPECT_EQ(m_process->GetProcessInfo().GetId(), m_id); + EXPECT_EQ(m_process->GetProcessInfo().GetProcessPath(), ValidProcessPath); + EXPECT_EQ(m_process->GetProcessInfo().GetStartupArgs(), args); + } + + TEST_F(ProcessTestFixture, GetReturnCodeAfterExit_ReturnsArg) + { + // Given a process launched with a valid binary and args + const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); + TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process has exited + m_process->BlockUntilExit(); + + // Expect the process to no longer be running + EXPECT_FALSE(m_process->IsRunning()); + + // Expect the return value to be the process id + EXPECT_EQ(m_process->GetReturnCode(), m_id); + } + + TEST_F(ProcessTestFixture, GetReturnCodeInFlight_ReturnsNullOpt) + { + // Given a process launched with a valid binary and args to sleep for 100 seconds + const AZStd::string args = ConstructTestProcessArgs(m_id, LongSleep); + TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args); + + // When the process is running + m_process = TestImpact::LaunchProcess(processInfo); + + // Expect the return value to be empty + EXPECT_EQ(m_process->GetReturnCode(), AZStd::nullopt); + } + + TEST_F(ProcessTestFixture, TerminateWithErrorCode_ReturnsErrorCode) + { + // Given a process launched with a valid binary and args to sleep for 100 seconds + const AZStd::string args = ConstructTestProcessArgs(m_id, LongSleep); + TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process is terminated by the client with the specified error code + m_process->Terminate(m_terminateErrorCode); + + // Expect the return value to be the error code specified in the Terminate call + EXPECT_EQ(m_process->GetReturnCode(), m_terminateErrorCode); + + // Expect the process to no longer be running + EXPECT_FALSE(m_process->IsRunning()); + } + + TEST_F(ProcessTestFixture, CheckIsRunningWhilstRunning_ProcessIsRunning) + { + // Given a process launched with a valid binary and args to sleep for 100 seconds + const AZStd::string args = ConstructTestProcessArgs(m_id, LongSleep); + TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args); + + // When the process is running + m_process = TestImpact::LaunchProcess(processInfo); + + // Expect the process to be running + EXPECT_TRUE(m_process->IsRunning()); + } + + TEST_F(ProcessTestFixture, CheckIsRunningWhilstNotRunning_ReturnsFalse) + { + // Given a process launched with a valid binary + TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process is terminated by the client + m_process->Terminate(0); + + // Expect the process to be running + EXPECT_FALSE(m_process->IsRunning()); + } + + TEST_F(ProcessTestFixture, RedirectStdOut_OutputIsKnownTestProcessOutputString) + { + // Given a process launched with a valid binary and standard output redirected to parent + const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); + TestImpact::ProcessInfo processInfo( + m_id, + TestImpact::StdOutputRouting::ToParent, + TestImpact::StdErrorRouting::None, + ValidProcessPath, + args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process exits + m_process->BlockUntilExit(); + + // Expect stdoutput to be rerouted to the parent + EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdOutput()); + + // Do not expect stdouterr to be rerouted to the parent + EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdError()); + + // Expect the output to match the known stdout of the child + EXPECT_EQ(m_process->ConsumeStdOut(), KnownTestProcessOutputString(m_id)); + } + + TEST_F(ProcessTestFixture, RedirectStdErr_OutputIsKnownTestProcessOutputString) + { + // Given a process launched with a valid binary and standard error redirected to parent + const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); + TestImpact::ProcessInfo processInfo( + m_id, + TestImpact::StdOutputRouting::None, + TestImpact::StdErrorRouting::ToParent, + ValidProcessPath, + args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process exits + m_process->BlockUntilExit(); + + // Do not expect stdoutput to be rerouted to the parent + EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdOutput()); + + // Expect stderror to be rerouted to the parent + EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdError()); + + // Expect the output to match the known stdout of the child + EXPECT_EQ(m_process->ConsumeStdErr(), KnownTestProcessErrorString(m_id)); + } + + TEST_F(ProcessTestFixture, RedirectStdOutAndTerminate_OutputIsEmpty) + { + // Given a process launched with a valid binary and standard output redirected to parent + const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); + TestImpact::ProcessInfo processInfo( + m_id, + TestImpact::StdOutputRouting::ToParent, + TestImpact::StdErrorRouting::None, + ValidProcessPath, + args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process is terminated + m_process->Terminate(0); + + // Expect stdoutput to be rerouted to the parent + EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdOutput()); + + // Do not expect stdouterr to be rerouted to the parent + EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdError()); + + // Expect the output to be empty + EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); + } + + TEST_F(ProcessTestFixture, RedirectStdErrAndTerminate_OutputIsEmpty) + { + // Given a process launched with a valid binary and standard error redirected to parent + const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); + TestImpact::ProcessInfo processInfo( + m_id, + TestImpact::StdOutputRouting::None, + TestImpact::StdErrorRouting::ToParent, + ValidProcessPath, + args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process is terminated + m_process->Terminate(0); + + // Do not expect stdoutput to be rerouted to the parent + EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdOutput()); + + // Expect stderror to be rerouted to the parent + EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdError()); + + // Expect the output to be empty + EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); + } + + TEST_F(ProcessTestFixture, RedirectStdOutAndStdErrorRouting_OutputsAreKnownTestProcessOutputStrings) + { + // Given a process launched with a valid binary and both standard output and standard error redirected to parent + const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); + TestImpact::ProcessInfo processInfo( + m_id, + TestImpact::StdOutputRouting::ToParent, + TestImpact::StdErrorRouting::ToParent, + ValidProcessPath, + args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process exits + m_process->BlockUntilExit(); + + // Expect stdoutput to be rerouted to the parent + EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdOutput()); + + // Expect stderror to be rerouted to the parent + EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdError()); + + // Expect the output to match the known stdout and stderr of the child + EXPECT_EQ(m_process->ConsumeStdOut(), KnownTestProcessOutputString(m_id)); + EXPECT_EQ(m_process->ConsumeStdErr(), KnownTestProcessErrorString(m_id)); + } + + TEST_F(ProcessTestFixture, NoStdOutOrStdErrRedirect_OutputIsEmpty) + { + // Given a process launched with a valid binary and both standard output and standard error redirected to parent + const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); + TestImpact::ProcessInfo processInfo( + m_id, + TestImpact::StdOutputRouting::None, + TestImpact::StdErrorRouting::None, + ValidProcessPath, + args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process exits + m_process->BlockUntilExit(); + + // Do not expect stdoutput to be rerouted to the parent + EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdOutput()); + + // Do not expect stdouterr to be rerouted to the parent + EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdError()); + + // Expect the output to to be empty + EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); + EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); + } + + TEST_F(ProcessTestFixture, LargePipeNoRedirects_OutputsAreEmpty) + { + // Given a process outputting large text with no standard output and no standard error redirected to parent + const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, NoSleep); + TestImpact::ProcessInfo processInfo( + m_id, + TestImpact::StdOutputRouting::None, + TestImpact::StdErrorRouting::None, + ValidProcessPath, + args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process exits + m_process->BlockUntilExit(); + + // Expect the output to to be empty + EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); + EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); + } + + TEST_F(ProcessTestFixture, LargePipeNoRedirectsAndTerminated_OutputsAreEmpty) + { + // Given a process outputting large text with no standard output and standard error redirected to parent + const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, LongSleep); + TestImpact::ProcessInfo processInfo( + m_id, + TestImpact::StdOutputRouting::None, + TestImpact::StdErrorRouting::None, + ValidProcessPath, + args); + + // When the process is running + m_process = TestImpact::LaunchProcess(processInfo); + + // Expect the output to to be empty + EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); + EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); + + // When the process is terminated + m_process->Terminate(0); + + // Expect the output to to be empty + EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); + EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); + } + + TEST_F(ProcessTestFixture, LargePipeRedirectsAndReadWhilstRunning_OutputsAreEmpty) + { + // Given a process outputting large text with no standard output and standard error redirected to parent + const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, ShortSleep); + TestImpact::ProcessInfo processInfo( + m_id, + TestImpact::StdOutputRouting::None, + TestImpact::StdErrorRouting::None, + ValidProcessPath, + args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the outputs are read as the process is running + while (m_process->IsRunning()) + { + // Expect the outputs to be empty + EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); + EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); + } + } + + TEST_F(ProcessTestFixture, LargePipeRedirectsAndTerminated_OutputsAreEmpty) + { + // Given a process outputting large text with both standard output and standard error redirected to parent + const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, LongSleep); + TestImpact::ProcessInfo processInfo( + m_id, + TestImpact::StdOutputRouting::ToParent, + TestImpact::StdErrorRouting::ToParent, + ValidProcessPath, + args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process is terminated + m_process->Terminate(0); + + // Expect the outputs to be empty + EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); + EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); + } + + TEST_F(ProcessTestFixture, LargePipeRedirectsAndBlockedUntilExit_OutputsAreLargeTextFileSize) + { + // Given a process outputting large text with both standard output and standard error redirected to parent + const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, NoSleep); + TestImpact::ProcessInfo processInfo( + m_id, + TestImpact::StdOutputRouting::ToParent, + TestImpact::StdErrorRouting::ToParent, + ValidProcessPath, + args); + m_process = TestImpact::LaunchProcess(processInfo); + + // When the process exits + m_process->BlockUntilExit(); + + // Expect the output lengths to be that of the large text output from the child process + EXPECT_EQ(m_process->ConsumeStdOut().value().length(), LargeTextSize); + EXPECT_EQ(m_process->ConsumeStdErr().value().length(), LargeTextSize); + } + + TEST_F(ProcessTestFixture, LargePipeRedirectsAndReadWhilstRunning_TotalOutputsAreLargeTextFileSize) + { + // Given a process outputting large text with both standard output and standard error redirected to parent + const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, NoSleep); + TestImpact::ProcessInfo processInfo( + m_id, + TestImpact::StdOutputRouting::ToParent, + TestImpact::StdErrorRouting::ToParent, + ValidProcessPath, args); + m_process = TestImpact::LaunchProcess(processInfo); + + size_t stdOutBytes = 0; + size_t stdErrBytes = 0; + while (m_process->IsRunning()) + { + // When the outputs are read as the process is running + if (auto output = m_process->ConsumeStdOut(); output.has_value()) + { + stdOutBytes += output.value().length(); + } + + if (auto output = m_process->ConsumeStdErr(); output.has_value()) + { + stdErrBytes += output.value().length(); + } + } + + // Expect the output lengths to be that of the large text output from the child process + EXPECT_EQ(stdOutBytes, LargeTextSize); + EXPECT_EQ(stdErrBytes, LargeTextSize); + + // Expect the outputs to be empty + EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); + EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp new file mode 100644 index 0000000000..e416d3836e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp @@ -0,0 +1,352 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +#include +#include + +namespace UnitTest +{ + namespace + { + AZStd::string GenerateBuildTargetName(size_t index) + { + return AZStd::string::format("Target%u", index); + } + + AZStd::string GenerateBuildTargetOutputName(size_t index) + { + return AZStd::string::format("Output%u", index); + } + + AZ::IO::Path GenerateBuildTargetPath(size_t index) + { + return AZStd::string::format("C:\\Repo\\Dir%u", index); + } + + AZStd::string GenerateTestTargetSuite(size_t index) + { + return AZStd::string::format("Suite%u", index); + } + + TestImpact::LaunchMethod GenerateLaunchMethod(size_t index) + { + return index % 2 == 0 ? TestImpact::LaunchMethod::StandAlone : TestImpact::LaunchMethod::TestRunner; + } + + AZStd::string GenerateStaticSourceFile(size_t index) + { + return AZStd::string::format("StaticSource%u", index); + } + + TestImpact::AutogenPairs GenerateAutogenSourceFiles(size_t index) + { + TestImpact::AutogenPairs autogen; + autogen.m_input = AZStd::string::format("InputSource%u", index); + autogen.m_outputs.push_back(AZStd::string::format("OutputSource%u", index)); + autogen.m_outputs.push_back(AZStd::string::format("OutputHeader%u", index)); + return autogen; + } + + TestImpact::TargetSources GenerateTargetSources(size_t index) + { + TestImpact::TargetSources sources; + sources.m_staticSources.resize(index + 1); + for (size_t i = 0; i < sources.m_staticSources.size(); i++) + { + sources.m_staticSources[i] = GenerateStaticSourceFile(i); + } + + // Only generate autogen sources for even-numbered indexes + if (index % 2 == 0) + { + sources.m_autogenSources.resize(index + 1); + for (size_t i = 0; i < sources.m_autogenSources.size(); i++) + { + sources.m_autogenSources[i] = GenerateAutogenSourceFiles(i); + } + } + + return sources; + } + + TestImpact::ProductionTargetDescriptor GenerateProductionTargetDescriptor(size_t index = 0) + { + return TestImpact::ProductionTargetDescriptor( + { + TestImpact::BuildMetaData + { + GenerateBuildTargetName(index), GenerateBuildTargetOutputName(index), GenerateBuildTargetPath(index) + }, + GenerateTargetSources(index) + }); + } + + TestImpact::TestTargetDescriptor GenerateTestTargetDescriptor(size_t index = 0) + { + return TestImpact::TestTargetDescriptor( + { + TestImpact::BuildMetaData + { + GenerateBuildTargetName(index), GenerateBuildTargetOutputName(index), GenerateBuildTargetPath(index) + }, + GenerateTargetSources(index) + }, + TestImpact::TestTargetMeta + { + GenerateTestTargetSuite(index), GenerateLaunchMethod(index) + }); + } + } // namespace + + template + typename Target::Descriptor GenerateTargetDescriptor(size_t index = 0) + { + static_assert( + AZStd::is_same_v || AZStd::is_same_v, + "Unexpected target type, wants TestImpact::ProductionTarget or TestImpact::TestTarget"); + + if constexpr (AZStd::is_same_v) + { + return GenerateProductionTargetDescriptor(index); + } + else + { + return GenerateTestTargetDescriptor(index); + } + } + + void ValidateSources(const TestImpact::TargetSources& sources, size_t index = 0) + { + EXPECT_EQ(sources.m_staticSources.size(), index + 1); + for (size_t i = 0; i < sources.m_staticSources.size(); i++) + { + EXPECT_EQ(sources.m_staticSources[i], GenerateStaticSourceFile(i)); + } + + // Even numbered indexes have autogen sources + if (index % 2 == 0) + { + EXPECT_EQ(sources.m_autogenSources.size(), index + 1); + for (size_t i = 0; i < sources.m_autogenSources.size(); i++) + { + enum + { + OutputSource = 0, + OutputHeader = 1 + }; + + const TestImpact::AutogenPairs expectedAutogenSources = GenerateAutogenSourceFiles(i); + EXPECT_EQ(sources.m_autogenSources[i].m_input, expectedAutogenSources.m_input); + EXPECT_EQ(sources.m_autogenSources[i].m_outputs[OutputSource], expectedAutogenSources.m_outputs[OutputSource]); + EXPECT_EQ(sources.m_autogenSources[i].m_outputs[OutputHeader], expectedAutogenSources.m_outputs[OutputHeader]); + } + } + else + { + EXPECT_TRUE(sources.m_autogenSources.empty()); + } + } + + void ValidateTarget(const TestImpact::ProductionTarget& target, size_t index = 0) + { + EXPECT_EQ(target.GetName(), GenerateBuildTargetName(index)); + EXPECT_EQ(target.GetOutputName(), GenerateBuildTargetOutputName(index)); + EXPECT_EQ(target.GetPath(), GenerateBuildTargetPath(index)); + EXPECT_EQ(target.GetType(), TestImpact::TargetType::Production); + ValidateSources(target.GetSources(), index); + } + + void ValidateTarget(const TestImpact::TestTarget& target, size_t index = 0) + { + EXPECT_EQ(target.GetName(), GenerateBuildTargetName(index)); + EXPECT_EQ(target.GetOutputName(), GenerateBuildTargetOutputName(index)); + EXPECT_EQ(target.GetPath(), GenerateBuildTargetPath(index)); + EXPECT_EQ(target.GetType(), TestImpact::TargetType::Test); + EXPECT_EQ(target.GetSuite(), GenerateTestTargetSuite(index)); + EXPECT_EQ(target.GetLaunchMethod(), GenerateLaunchMethod(index)); + ValidateSources(target.GetSources(), index); + } + + template + class TargetListFixture + : public AllocatorsTestFixture + { + public: + using TargetListType = TargetList; + using TargetType = typename TargetList::TargetType; + + static constexpr size_t m_numTargets = 10; + }; + + using TargetTypes = testing::Types; + TYPED_TEST_CASE(TargetListFixture, TargetTypes); + + TYPED_TEST(TargetListFixture, CreateTarget_ExpectValidTarget) + { + // Given a target of the specified type + TargetType target(GenerateTargetDescriptor()); + + // Expect the target to match the procedurally generated target descriptor + ValidateTarget(target); + } + + TYPED_TEST(TargetListFixture, CreateEmptyTargetList_ExpectTargetException) + { + try + { + // Given an empty target list + TargetListType targetList({}); + + // Do not expect the target list construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::TargetException& e) + { + // Expect a target exception to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TYPED_TEST(TargetListFixture, CreateTargetListWithDuplicateDescriptor_ExpectTargetException) + { + try + { + // Given a set of target descriptors containing a single duplicate + AZStd::vector descriptors; + descriptors.reserve(m_numTargets); + for (size_t i = 0; i < m_numTargets; i++) + { + // Wrap the last index round to repeat the first index + descriptors.push_back(GenerateTargetDescriptor(i % (m_numTargets - 1))); + } + + // When constructing the target list containing the duplicate target descriptor + TargetListType targetList(AZStd::move(descriptors)); + + // Do not expect the target list construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::TargetException& e) + { + // Expect a target exception to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TYPED_TEST(TargetListFixture, CreateTargetListWithValidDescriptors_ExpectValidTargetList) + { + // Given a valid set of target descriptor + AZStd::vector descriptors; + descriptors.reserve(m_numTargets); + for (size_t i = 0; i < m_numTargets; i++) + { + descriptors.push_back(GenerateTargetDescriptor(i)); + } + + // When constructing the target list containing the valid target descriptors + TargetListType targetList(AZStd::move(descriptors)); + + // Expect the number of targets in the list to match the number of target descriptors used to construct the list + EXPECT_EQ(targetList.GetNumTargets(), m_numTargets); + + for (size_t i = 0; i < targetList.GetNumTargets(); i++) + { + // Expect the target to match the procedurally generated target descriptor + ValidateTarget(targetList.GetTargets()[i], i); + + // Expect the target obtained by name to match the procedurally generated target descriptor + auto target = targetList.GetTarget(GenerateBuildTargetName(i)); + EXPECT_TRUE(target); + EXPECT_EQ(target->GetName(), GenerateBuildTargetName(i)); + } + } + + TYPED_TEST(TargetListFixture, FindNonExistantTargets_ExpectEmptyResults) + { + // Given a valid set of target descriptor + AZStd::vector descriptors; + descriptors.reserve(m_numTargets); + for (size_t i = 0; i < m_numTargets; i++) + { + descriptors.push_back(GenerateTargetDescriptor(i)); + } + + // When constructing the target list containing the valid target descriptors + TargetListType targetList(AZStd::move(descriptors)); + + // Expect the number of targets in the list to match the number of target descriptors used to construct the list + EXPECT_EQ(targetList.GetNumTargets(), m_numTargets); + + for (size_t i = 0; i < targetList.GetNumTargets(); i++) + { + // When attempting to find a target that does not exist + auto target = targetList.GetTarget(GenerateBuildTargetName(i + targetList.GetNumTargets())); + + // Expect an empty result + EXPECT_FALSE(target); + } + } + + TYPED_TEST(TargetListFixture, FindNonExistantTargetsAndThrow_ExpectTargetExceptionss) + { + // Given a valid set of target descriptor + AZStd::vector descriptors; + descriptors.reserve(m_numTargets); + for (size_t i = 0; i < m_numTargets; i++) + { + descriptors.push_back(GenerateTargetDescriptor(i)); + } + + // When constructing the target list containing the valid target descriptors + TargetListType targetList(AZStd::move(descriptors)); + + // Expect the number of targets in the list to match the number of target descriptors used to construct the list + EXPECT_EQ(targetList.GetNumTargets(), m_numTargets); + + for (size_t i = 0; i < targetList.GetNumTargets(); i++) + { + try + { + // When attempting to find a target that does not exist + auto target = targetList.GetTargetOrThrow(GenerateBuildTargetName(i + targetList.GetNumTargets())); + + // Do not expect the target list construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::TargetException& e) + { + // Expect a target exception to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactInstrumentedTestRunnerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactInstrumentedTestRunnerTest.cpp new file mode 100644 index 0000000000..897eda012c --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactInstrumentedTestRunnerTest.cpp @@ -0,0 +1,822 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + using JobExceptionPolicy = TestImpact::InstrumentedTestRunner::JobExceptionPolicy; + using CoverageExceptionPolicy = TestImpact::InstrumentedTestRunner::CoverageExceptionPolicy; + + struct TargetPaths + { + AZ::IO::Path m_targetBinary; + AZ::IO::Path m_testRunArtifact; + AZ::IO::Path m_testCoverageArtifact; + }; + + // Indices for looking up job command arguments for the different coverage levels + enum CoverageLevel : uint8_t + { + LineLevel = 0, + SourceLevel + }; + + // Get the job command for an instrumented test run + AZStd::string GetRunCommandForTarget(const TargetPaths& testTarget, CoverageLevel coverageLevel, const char* sourcesFilter) + { + AZStd::string args = AZStd::string::format( + "%s " // 1. Instrumented test runner + "--coverage_level %s " // 2. Coverage level + "--export_type cobertura:\"%s\" " // 3. Test coverage artifact path + "--modules \"%s\" " // 4. Modules path + "--excluded_modules \"%s\" " // 5. Exclude modules + "--sources \"%s\" -- " // 6. Sources path + "\"%s\" " // 7. Test runner binary + "\"%s\" " // 8. Test target bin + "AzRunUnitTests " + "--gtest_output=xml:%s", // 9. Test run result artifact + + LY_TEST_IMPACT_INSTRUMENTATION_BIN, // 1. + (coverageLevel == CoverageLevel::LineLevel ? "line" : "source"), // 2. + testTarget.m_testCoverageArtifact.c_str(), // 3. + LY_TEST_IMPACT_MODULES_DIR, // 4. + LY_TEST_IMPACT_AZ_TESTRUNNER_BIN, // 5. + sourcesFilter, // 6. + LY_TEST_IMPACT_AZ_TESTRUNNER_BIN, // 7. + testTarget.m_targetBinary.c_str(), // 8. + testTarget.m_testRunArtifact.c_str() // 9. + ); + + // OpenCppCoverage doesn't support forward slash directory separators so replace all with escaped backslashes + return AZStd::regex_replace(args, AZStd::regex("/"), "\\"); + } + + // Get the job command for an instrumented test run with valid source filters to produce coverage artifact + AZStd::string GetRunCommandForTargetWithSources(const TargetPaths& testTarget, CoverageLevel coverageLevel) + { + return GetRunCommandForTarget(testTarget, coverageLevel, LY_TEST_IMPACT_COVERAGE_SOURCES_DIR); + } + + // Get the job command for an instrumented test run without valid source filters to produce empty coverage artifact + AZStd::string GetRunCommandForTargetWithoutSources(const TargetPaths& testTarget, CoverageLevel coverageLevel) + { + return GetRunCommandForTarget(testTarget, coverageLevel, "C:\\No\\Sources\\Here\\At\\All\\Ever\\Ever\\Ever"); + } + + class InstrumentedTestRunnerFixture + : public AllocatorsTestFixture + { + public: + void SetUp() override; + void TearDown() override; + + protected: + using JobInfo = TestImpact::InstrumentedTestRunner::JobInfo; + using JobData = TestImpact::InstrumentedTestRunner::JobData; + + AZStd::vector m_jobInfos; + AZStd::unique_ptr m_testRunner; + AZStd::vector> m_testTargetJobArgs; + AZStd::vector m_testTargetPaths; + AZStd::vector m_expectedTestTargetRuns; + AZStd::vector> m_expectedTestTargetCoverages; + AZStd::vector m_expectedTestTargetResult; + size_t m_maxConcurrency = 0; + CoverageLevel m_coverageLevel = CoverageLevel::LineLevel; + inline static AZ::u32 s_uniqueTestCaseId = 0; // Unique id for each test case to be used as the suffix for the written files + }; + + void InstrumentedTestRunnerFixture::SetUp() + { + AllocatorsTestFixture::SetUp(); + + // Suffix each artifact file with the unique test case id to ensure that there are no possible race conditions between cleaning + // up the artifact files after each test case and those artifact files being written and read again in future test cases + const AZStd::string fileExtension = AZStd::string::format(".%u.xml", s_uniqueTestCaseId++); + + const AZStd::string runPath = AZStd::string(LY_TEST_IMPACT_TEST_TARGET_RESULTS_DIR) + "/%s.Run" + fileExtension; + const AZStd::string coveragePath = AZStd::string(LY_TEST_IMPACT_TEST_TARGET_COVERAGE_DIR) + "/%s.Coverage" + fileExtension; + + // TestTargetA + m_testTargetPaths.emplace_back(TargetPaths{ + LY_TEST_IMPACT_TEST_TARGET_A_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME), + AZStd::string::format(coveragePath.c_str(), LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME)}); + + // TestTargetB + m_testTargetPaths.emplace_back(TargetPaths{ + LY_TEST_IMPACT_TEST_TARGET_B_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME), + AZStd::string::format(coveragePath.c_str(), LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME)}); + + // TestTargetC + m_testTargetPaths.emplace_back(TargetPaths{ + LY_TEST_IMPACT_TEST_TARGET_C_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME), + AZStd::string::format(coveragePath.c_str(), LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME)}); + + // TestTargetD + m_testTargetPaths.emplace_back(TargetPaths{ + LY_TEST_IMPACT_TEST_TARGET_D_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME), + AZStd::string::format(coveragePath.c_str(), LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME)}); + + m_expectedTestTargetRuns.emplace_back(GetTestTargetATestRunSuites(), AZStd::chrono::milliseconds{500}); // TestTargetA + m_expectedTestTargetRuns.emplace_back(GetTestTargetBTestRunSuites(), AZStd::chrono::milliseconds{500}); // TestTargetB + m_expectedTestTargetRuns.emplace_back(GetTestTargetCTestRunSuites(), AZStd::chrono::milliseconds{500}); // TestTargetC + m_expectedTestTargetRuns.emplace_back(GetTestTargetDTestRunSuites(), AZStd::chrono::milliseconds{500}); // TestTargetD + + // TestTargetA + m_expectedTestTargetCoverages.emplace_back(AZStd::array{ + TestImpact::TestCoverage(GetTestTargetALineModuleCoverages()), + TestImpact::TestCoverage(GetTestTargetASourceModuleCoverages())}); + + // TestTargetB + m_expectedTestTargetCoverages.emplace_back(AZStd::array{ + TestImpact::TestCoverage(GetTestTargetBLineModuleCoverages()), + TestImpact::TestCoverage(GetTestTargetBSourceModuleCoverages())}); + + // TestTargetC + m_expectedTestTargetCoverages.emplace_back(AZStd::array{ + TestImpact::TestCoverage(GetTestTargetCLineModuleCoverages()), + TestImpact::TestCoverage(GetTestTargetCSourceModuleCoverages())}); + + // TestTargetD + m_expectedTestTargetCoverages.emplace_back(AZStd::array{ + TestImpact::TestCoverage(GetTestTargetDLineModuleCoverages()), + TestImpact::TestCoverage(GetTestTargetDSourceModuleCoverages())}); + + m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Failed); // TestTargetA + m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Passed); // TestTargetB + m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Passed); // TestTargetC + m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Passed); // TestTargetD + + // Generate the job command arguments for both line level and source level coverage permutations + for (const auto& testTarget : m_testTargetPaths) + { + m_testTargetJobArgs.emplace_back(AZStd::array{ + GetRunCommandForTargetWithSources(testTarget, CoverageLevel::LineLevel), + GetRunCommandForTargetWithSources(testTarget, CoverageLevel::SourceLevel)}); + } + } + + void InstrumentedTestRunnerFixture::TearDown() + { + DeleteFiles(LY_TEST_IMPACT_TEST_TARGET_COVERAGE_DIR, "*.xml"); + DeleteFiles(LY_TEST_IMPACT_TEST_TARGET_RESULTS_DIR, "*.xml"); + + AllocatorsTestFixture::TearDown(); + } + + using ConcurrencyAndCoveragePermutation = AZStd::tuple + < + size_t, // Max number of concurrent processes + CoverageLevel // Coverage level + >; + + // Fixture parameterized for different max number of concurrent jobs and coverage levels + class InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageParams + : public InstrumentedTestRunnerFixture + , public ::testing::WithParamInterface + { + public: + void SetUp() override + { + InstrumentedTestRunnerFixture::SetUp(); + auto [maxConcurrency, coverageLevel] = GetParam(); + m_maxConcurrency = maxConcurrency; + m_coverageLevel = coverageLevel; + } + }; + + using ConcurrencyAndJobExceptionPermutation = AZStd::tuple + < + size_t, // Max number of concurrent processes + CoverageLevel, // Coverage level + JobExceptionPolicy // Test job exception policy + >; + + // Fixture parameterized for different max number of concurrent jobs, coverage levels and different job exception policies + class InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndJobExceptionParams + : public InstrumentedTestRunnerFixture + , public ::testing::WithParamInterface + { + public: + void SetUp() override + { + InstrumentedTestRunnerFixture::SetUp(); + const auto& [maxConcurrency, coverageLevel, jobExceptionPolicy] = GetParam(); + m_maxConcurrency = maxConcurrency; + m_coverageLevel = coverageLevel; + m_jobExceptionPolicy = jobExceptionPolicy; + } + + protected: + JobExceptionPolicy m_jobExceptionPolicy = JobExceptionPolicy::Never; + }; + + class InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndFailedToLaunchExceptionParams + : public InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndJobExceptionParams + { + }; + + class InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndExecutedWithFailureExceptionParams + : public InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndJobExceptionParams + { + }; + + using ConcurrencyAndCoverageExceptionPermutation = AZStd::tuple + < + size_t, // Max number of concurrent processes + CoverageExceptionPolicy // Test coverage exception policy + >; + + // Fixture parameterized for different max number of concurrent jobs and different coverage exception policies + class InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndCoverageExceptionParams + : public InstrumentedTestRunnerFixture + , public ::testing::WithParamInterface + { + public: + void SetUp() override + { + InstrumentedTestRunnerFixture::SetUp(); + const auto& [maxConcurrency, coverageExceptionPolicy] = GetParam(); + m_maxConcurrency = maxConcurrency; + m_coverageExceptionPolicy = coverageExceptionPolicy; + } + + protected: + CoverageExceptionPolicy m_coverageExceptionPolicy = CoverageExceptionPolicy::Never; + }; + + namespace + { + AZStd::array MaxConcurrentRuns = {1, 2, 3, 4}; + + AZStd::array CoverageLevels = {CoverageLevel::LineLevel, CoverageLevel::SourceLevel}; + + AZStd::array FailedToLaunchExceptionPolicies = { + JobExceptionPolicy::Never, JobExceptionPolicy::OnFailedToExecute}; + + AZStd::array ExecutedWithFailureExceptionPolicies = { + JobExceptionPolicy::Never, JobExceptionPolicy::OnExecutedWithFailure}; + } // namespace + + // Validates that the specified test coverage matches the expected output + void ValidateTestTargetCoverage(const TestImpact::TestCoverage& actualResult, const TestImpact::TestCoverage& expectedResult) + { + EXPECT_TRUE(actualResult == expectedResult); + } + + // Validates that the specified test coverage is empty + void ValidateEmptyTestTargetCoverage(const TestImpact::TestCoverage& actualResult) + { + EXPECT_TRUE(actualResult.GetSourcesCovered().empty()); + EXPECT_TRUE(actualResult.GetModuleCoverages().empty()); + EXPECT_EQ(actualResult.GetNumSourcesCovered(), 0); + EXPECT_EQ(actualResult.GetNumModulesCovered(), 0); + } + + TEST_P( + InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndCoverageExceptionParams, + EmptyTestCoverages_ExpectEmptyTestCoveragesOrTestRunException) + { + // Given a test runner with no client callback or run timeout or runner timeout + m_testRunner = + AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); + + // Given a mixture of instrumented test run jobs with and without coverage sources + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + const AZStd::string args = (jobId % 2) ? GetRunCommandForTargetWithoutSources(m_testTargetPaths[jobId], m_coverageLevel) + : m_testTargetJobArgs[jobId][m_coverageLevel]; + + JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); + m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); + } + + try + { + // When the instrumented test run jobs are executed with different exception policies + const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, m_coverageExceptionPolicy, JobExceptionPolicy::Never); + + // Expect this statement to be reachable only if no exception policy for empty coverages + EXPECT_FALSE(::IsFlagSet(m_coverageExceptionPolicy, CoverageExceptionPolicy::OnEmptyCoverage)); + + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); + + if (jobId % 2) + { + // Expect jobs have a empty test coverages + ValidateEmptyTestTargetCoverage(job.GetPayload().value().second); + } + else + { + // Expect the jobs to successfully result in a test run and coverage that matches the expected test run data + ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); + } + } + } + catch ([[maybe_unused]] const TestImpact::TestRunException& e) + { + // Expect this statement to be reachable only if there is an exception policy for empty coverages + EXPECT_TRUE(::IsFlagSet(m_coverageExceptionPolicy, CoverageExceptionPolicy::OnEmptyCoverage)); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_P( + InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndFailedToLaunchExceptionParams, + InvalidCommandArgument_ExpectJobResulFailedToExecuteeOrTestJobException) + { + // Given a test runner with no client callback or run timeout or runner timeout + m_testRunner = + AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); + + // Given a mixture of instrumented test run jobs with valid and invalid command arguments + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + const AZStd::string args = (jobId % 2) ? InvalidProcessPath : m_testTargetJobArgs[jobId][m_coverageLevel]; + JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); + m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); + } + + try + { + // When the instrumented test run jobs are executed with different exception policies + const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, m_jobExceptionPolicy); + + // Expect this statement to be reachable only if no exception policy for launch failures + EXPECT_FALSE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)); + + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + if (jobId % 2) + { + // Expect invalid jobs have a job result of FailedToExecute + ValidateJobFailedToExecute(job); + } + else + { + // Expect the valid jobs to successfully result in a test run that matches the expected test run data + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); + ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); + } + } + } + catch ([[maybe_unused]] const TestImpact::TestJobException& e) + { + // Expect this statement to be reachable only if there is an exception policy for launch failures + EXPECT_TRUE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_P( + InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndExecutedWithFailureExceptionParams, + ErroneousReturnCode_ExpectJobResultExecutedWithFailureOrTestJobException) + { + // Given a test runner with no client callback or run timeout or runner timeout + m_testRunner = + AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); + + // Given a mixture of instrumented test run jobs that execute and return either successfully or with failure + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); + m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId][m_coverageLevel], AZStd::move(jobData))); + } + + try + { + // When the instrumented test run jobs are executed with different exception policies + const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, m_jobExceptionPolicy); + + // Expect this statement to be reachable only if no exception policy for jobs that return with error + EXPECT_FALSE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)); + + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + + // Expect the valid jobs to successfully result in a test run that matches the expected test run data + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); + ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); + } + } + catch ([[maybe_unused]] const TestImpact::TestJobException& e) + { + // Expect this statement to be reachable only if there is an exception policy for jobs that return with error + EXPECT_TRUE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)); + } + catch ([[maybe_unused]] const TestImpact::Exception& e) + { + FAIL(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(InstrumentedTestRunnerFixture, EmptyRunRawData_ExpectTestRunnerException) + { + // Given a test runner with no client callback, concurrency, run timeout or runner timeout + m_testRunner = + AZStd::make_unique(AZStd::nullopt, OneConcurrentProcess, AZStd::nullopt, AZStd::nullopt); + + // Given an test runner job that will return successfully but with an empty artifact string + JobData jobData("", m_testTargetPaths[TestTargetA].m_testCoverageArtifact); + m_jobInfos.emplace_back(JobInfo({TestTargetA}, m_testTargetJobArgs[TestTargetA][LineLevel], AZStd::move(jobData))); + + try + { + // When the test runner job is executed + const auto runnerJobs = + m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::TestRunException& e) + { + // Expect an runner exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(InstrumentedTestRunnerFixture, EmptyCoverageRawData_ExpectTestRunnerException) + { + // Given a test runner with no client callback, concurrency, run timeout or runner timeout + m_testRunner = + AZStd::make_unique(AZStd::nullopt, OneConcurrentProcess, AZStd::nullopt, AZStd::nullopt); + + // Given an test runner job that will return successfully but with an empty artifact string + JobData jobData(m_testTargetPaths[TestTargetA].m_testRunArtifact, ""); + m_jobInfos.emplace_back(JobInfo({TestTargetA}, m_testTargetJobArgs[TestTargetA][LineLevel], AZStd::move(jobData))); + + try + { + // When the test runner job is executed + const auto runnerJobs = + m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::TestRunException& e) + { + // Expect an runner exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(InstrumentedTestRunnerFixture, InvalidRunArtifact_ExpectArtifactException) + { + // Given a run artifact with invalid contents + WriteTextToFile("There is nothing valid here", m_testTargetPaths[TestTargetA].m_testRunArtifact); + + // Given a job command that will write the run artifact to a different location that what we will read from + TargetPaths invalidRunArtifact = m_testTargetPaths[TestTargetA]; + invalidRunArtifact.m_testRunArtifact /= ".xml"; + const AZStd::string args = GetRunCommandForTargetWithSources(invalidRunArtifact, LineLevel); + + // Given a test runner with no client callback, concurrency, run timeout or runner timeout + m_testRunner = + AZStd::make_unique(AZStd::nullopt, OneConcurrentProcess, AZStd::nullopt, AZStd::nullopt); + + // Given an test runner job that will return successfully but not produce a run artifact + JobData jobData(m_testTargetPaths[TestTargetA].m_testRunArtifact, m_testTargetPaths[TestTargetA].m_testCoverageArtifact); + m_jobInfos.emplace_back(JobInfo({TestTargetA}, args, AZStd::move(jobData))); + + try + { + // When the test runner job is executed + const auto runnerJobs = + m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an runner exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(InstrumentedTestRunnerFixture, InvalidCoverageArtifact_ExpectArtifactException) + { + // Given a coverage artifact with invalid contents + WriteTextToFile("There is nothing valid here", m_testTargetPaths[TestTargetA].m_testCoverageArtifact); + + // Given a job command that will write the coverage artifact to a different location that what we will read from + TargetPaths invalidCoverageArtifact = m_testTargetPaths[TestTargetA]; + invalidCoverageArtifact.m_testCoverageArtifact /= ".xml"; + const AZStd::string args = GetRunCommandForTargetWithSources(invalidCoverageArtifact, LineLevel); + + // Given a test runner with no client callback, concurrency, run timeout or runner timeout + m_testRunner = + AZStd::make_unique(AZStd::nullopt, OneConcurrentProcess, AZStd::nullopt, AZStd::nullopt); + + // Given an test runner job that will return successfully but not produce a run artifact + JobData jobData(m_testTargetPaths[TestTargetA].m_testRunArtifact, m_testTargetPaths[TestTargetA].m_testCoverageArtifact); + m_jobInfos.emplace_back(JobInfo({TestTargetA}, args, AZStd::move(jobData))); + + try + { + // When the test runner job is executed + const auto runnerJobs = + m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an runner exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_P(InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageParams, RunTestTargets_RunsAndCoverageMatchTestSuitesInTarget) + { + // Given a test runner with no client callback, runner timeout or runner timeout + m_testRunner = + AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); + + // Given an test runner job for each test target with no runner caching + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); + m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId][m_coverageLevel], AZStd::move(jobData))); + } + + // When the test runner jobs are executed + const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Expect each job to successfully result in a test runner that matches the expected test runner data for that test target + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); + ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); + } + } + + TEST_P( + InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageParams, + RunTestTargetsWithArbitraryJobIds_RunsAndCoverageMatchTestSuitesInTarget) + { + // Given a set of arbitrary job ids to be used for the test target jobs + enum + { + ArbitraryA = 36, + ArbitraryB = 890, + ArbitraryC = 19, + ArbitraryD = 1 + }; + + const AZStd::unordered_map sequentialToArbitrary = + { + {TestTargetA, ArbitraryA}, + {TestTargetB, ArbitraryB}, + {TestTargetC, ArbitraryC}, + {TestTargetD, ArbitraryD}, + }; + + const AZStd::unordered_map arbitraryToSequential = + { + {ArbitraryA, TestTargetA}, + {ArbitraryB, TestTargetB}, + {ArbitraryC, TestTargetC}, + {ArbitraryD, TestTargetD}, + }; + + // Given a test runner with no client callback, run timeout or runner timeout + m_testRunner = + AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); + + // Given an test run job for each test target + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); + m_jobInfos.emplace_back( + JobInfo({sequentialToArbitrary.at(jobId)}, m_testTargetJobArgs[jobId][m_coverageLevel], AZStd::move(jobData))); + } + + // When the instrumented test run jobs are executed + const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Expect each job to successfully result in a test run that matches the expected test run data for that test target + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = arbitraryToSequential.at(job.GetJobInfo().GetId().m_value); + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); + ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); + } + } + + TEST_P(InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageParams, RunTestTargetsWithCallback_RunsAndCoverageMatchTestSuitesInTarget) + { + // Given a client callback function that tracks the number of successful runs + size_t numSuccesses = 0; + const auto jobCallback = + [&numSuccesses]([[maybe_unused]] const TestImpact::InstrumentedTestRunner::JobInfo& jobInfo, const TestImpact::JobMeta& meta) + { + if (meta.m_result == TestImpact::JobResult::ExecutedWithSuccess) + { + numSuccesses++; + } + }; + + // Given a test runner with no run timeout or runner timeout + m_testRunner = + AZStd::make_unique(jobCallback, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); + + // Given an test run job for each test target + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); + m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId][m_coverageLevel], AZStd::move(jobData))); + } + + // When the instrumented test run jobs are executed + const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Expect the number of successful runs tracked in the callback to match the number of test targets run with no failures + EXPECT_EQ( + numSuccesses, + AZStd::count_if(m_expectedTestTargetResult.begin(), m_expectedTestTargetResult.end(), [](TestImpact::TestRunResult result) { + return result == TestImpact::TestRunResult::Passed; + })); + + // Expect each job to successfully result in a test run that matches the expected test run data for that test target + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); + ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); + } + } + + TEST_P(InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageParams, JobRunnerTimeout_InFlightJobsTimeoutAndQueuedJobsUnlaunched) + { + // Given a test runner with no client callback or runner timeout and 2 second run timeout + m_testRunner = AZStd::make_unique( + AZStd::nullopt, m_maxConcurrency, AZStd::chrono::seconds(2), AZStd::nullopt); + + // Given an test run job for each test target where half will sleep indefinitely + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); + const AZStd::string args = (jobId % 2) + ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, LongSleep).c_str()) + : m_testTargetJobArgs[jobId][m_coverageLevel]; + m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); + } + + // When the instrumented test run jobs are executed + const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Expect half the jobs to successfully result in a test run that matches the expected test run data for that test target + // with the other half having timed out + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + if (jobId % 2) + { + ValidateJobTimeout(job); + } + else + { + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); + ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); + } + } + } + + TEST_F(InstrumentedTestRunnerFixture, JobTimeout_InFlightJobTimeoutAndQueuedJobsUnlaunched) + { + // Given a test runner with no client callback or run timeout and a 5 second runner timeout + m_testRunner = AZStd::make_unique( + AZStd::nullopt, FourConcurrentProcesses, AZStd::nullopt, AZStd::chrono::seconds(5)); + + // Given an test run job for each test target where half will sleep indefinitely + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); + const AZStd::string args = (jobId % 2) + ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, LongSleep).c_str()) + : m_testTargetJobArgs[jobId][m_coverageLevel]; + m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); + } + + // When the instrumented test run jobs are executed + const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Expect half the jobs to successfully result in a test run that matches the expected test run data for that test target + // with the other half having timed out + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + if (jobId % 2) + { + ValidateJobTimeout(job); + } + else + { + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); + ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); + } + } + } + + INSTANTIATE_TEST_CASE_P( + , + InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndCoverageExceptionParams, + ::testing::Combine( + ::testing::ValuesIn(MaxConcurrentRuns), + ::testing::Values(CoverageExceptionPolicy::Never, CoverageExceptionPolicy::OnEmptyCoverage))); + + INSTANTIATE_TEST_CASE_P( + , + InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndFailedToLaunchExceptionParams, + ::testing::Combine( + ::testing::ValuesIn(MaxConcurrentRuns), ::testing::ValuesIn(CoverageLevels), + ::testing::ValuesIn(FailedToLaunchExceptionPolicies))); + + INSTANTIATE_TEST_CASE_P( + , + InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndExecutedWithFailureExceptionParams, + ::testing::Combine( + ::testing::ValuesIn(MaxConcurrentRuns), ::testing::ValuesIn(CoverageLevels), + ::testing::ValuesIn(ExecutedWithFailureExceptionPolicies))); + + INSTANTIATE_TEST_CASE_P( + , + InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageParams, + ::testing::Combine(::testing::ValuesIn(MaxConcurrentRuns), ::testing::ValuesIn(CoverageLevels))); +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestCoverageTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestCoverageTest.cpp new file mode 100644 index 0000000000..538097d11e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestCoverageTest.cpp @@ -0,0 +1,208 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +namespace UnitTest +{ + namespace + { + AZ::IO::Path GenerateSourcePath(AZ::u32 index) + { + return AZStd::string::format("SourceFile%u", index); + } + + AZ::IO::Path GenerateModulePath(AZ::u32 index) + { + return AZStd::string::format("Module%u", index); + } + + AZStd::vector GenerateLineCoverages(AZ::u32 numLines) + { + AZStd::vector lineCoverages; + for (size_t i = 0; i < numLines; i++) + { + // Fudge some superficially different but trivially checkable line coverage data + lineCoverages.emplace_back(TestImpact::LineCoverage{i, i * 2}); + } + + return lineCoverages; + } + + TestImpact::SourceCoverage GenerateSourceCoverage(AZ::u32 index, TestImpact::CoverageLevel coverageLevel) + { + TestImpact::SourceCoverage sourceCoverage; + sourceCoverage.m_path = GenerateSourcePath(index); + if (coverageLevel == TestImpact::CoverageLevel::Line) + { + sourceCoverage.m_coverage.emplace(GenerateLineCoverages(index + 1)); + } + + return sourceCoverage; + } + + AZStd::vector GenerateSourceCoverages(AZ::u32 numSources, TestImpact::CoverageLevel coverageLevel) + { + AZStd::vector sourceCoverages; + for (AZ::u32 i = 0; i < numSources; i++) + { + sourceCoverages.emplace_back(GenerateSourceCoverage(i, coverageLevel)); + } + + return sourceCoverages; + } + + TestImpact::ModuleCoverage GenerateModuleCoverage(AZ::u32 index, AZ::u32 numSources, TestImpact::CoverageLevel coverageLevel) + { + TestImpact::ModuleCoverage moduleCoverage; + moduleCoverage.m_path = GenerateModulePath(index); + moduleCoverage.m_sources = GenerateSourceCoverages(numSources, coverageLevel); + return moduleCoverage; + } + + AZStd::vector GenerateModuleCoverages(AZ::u32 numModules, TestImpact::CoverageLevel coverageLevel) + { + AZStd::vector moduleCoverages; + for (AZ::u32 i = 0; i < numModules; i++) + { + // Fudge some superficially different but trivially deducible module coverage data + moduleCoverages.emplace_back(GenerateModuleCoverage(i, i + 1, coverageLevel)); + } + + return moduleCoverages; + } + } // namespace + + using CoveragePermutation = AZStd::tuple + < + unsigned, // Number of modules covered + TestImpact::CoverageLevel // Test coverage level + >; + + // Fixture parameterized for different max number of concurrent jobs + class TestCoverageFixtureWithCoverageParams + : public AllocatorsTestFixture + , public ::testing::WithParamInterface + { + public: + void SetUp() override; + + protected: + void ValidateTestCoverage(const TestImpact::TestCoverage& testCoverage); + + size_t m_numModulesCovered; + TestImpact::CoverageLevel m_coverageLevel; + }; + + void TestCoverageFixtureWithCoverageParams::SetUp() + { + AllocatorsTestFixture::SetUp(); + const auto& [numModulesCovered, coverageLevel] = GetParam(); + m_numModulesCovered = numModulesCovered; + m_coverageLevel = coverageLevel; + } + + void TestCoverageFixtureWithCoverageParams::ValidateTestCoverage(const TestImpact::TestCoverage& testCoverage) + { + // Expect the coverage level to match that which was used to generate the module coverages generated + EXPECT_EQ(testCoverage.GetCoverageLevel(), m_coverageLevel); + + // Expect the number of modules covered to match the number of module coverages generated + EXPECT_EQ(testCoverage.GetNumModulesCovered(), m_numModulesCovered); + + // Expect the number of unique sources covered to match the number of modules coverages generated + EXPECT_EQ(testCoverage.GetNumSourcesCovered(), m_numModulesCovered); + + // Expect the unique sources covered to match the procedurally generated source paths + for (size_t sourceIndex = 0; sourceIndex < testCoverage.GetNumSourcesCovered(); sourceIndex++) + { + EXPECT_EQ(testCoverage.GetSourcesCovered()[sourceIndex], GenerateSourcePath(sourceIndex)); + } + + // Expect each module covered to match that of the corresponding procedurally generated modules + for (size_t moduleIndex = 0; moduleIndex < testCoverage.GetNumModulesCovered(); moduleIndex++) + { + const TestImpact::ModuleCoverage& moduleCoverage = testCoverage.GetModuleCoverages()[moduleIndex]; + + // Expect the module path to match that of the corresponding procedurally generated module + EXPECT_EQ(moduleCoverage.m_path, GenerateModulePath(moduleIndex)); + + // Expect the module's number of sources to match that of the corresponding procedurally generated module + EXPECT_EQ(moduleCoverage.m_sources.size(), moduleIndex + 1); + + for (size_t sourceIndex = 0; sourceIndex < moduleCoverage.m_sources.size(); sourceIndex++) + { + const TestImpact::SourceCoverage& sourceCoverage = moduleCoverage.m_sources[sourceIndex]; + + // Expect the source path to match the procedurally generated source path + EXPECT_EQ(sourceCoverage.m_path, GenerateSourcePath(sourceIndex)); + + if (m_coverageLevel == TestImpact::CoverageLevel::Line) + { + // Expect there to actually be line coverage data if this coverage was procedurally generated with line data + EXPECT_TRUE(sourceCoverage.m_coverage.has_value()); + + const AZStd::vector& lineCoverages = sourceCoverage.m_coverage.value(); + + // Expect the source's number of lines to match that of the corresponding procedurally generated source + EXPECT_EQ(lineCoverages.size(), sourceIndex + 1); + + for (size_t lineIndex = 0; lineIndex < lineCoverages.size(); lineIndex++) + { + // The expected line number and hit count are deduced as follows: + // Line number: line index + // Hit count: 2x line index + EXPECT_EQ(lineCoverages[lineIndex].m_lineNumber, lineIndex); + EXPECT_EQ(lineCoverages[lineIndex].m_hitCount, lineIndex * 2); + } + } + else + { + // Do not expect there to actually be line coverage data if this coverage was not procedurally generated with line data + EXPECT_FALSE(sourceCoverage.m_coverage.has_value()); + } + } + } + } + + TEST(TestCoverage, EmptyCoverage_ExpectTestRunException) + { + // When constructing a test coverage from the empty module coverages + TestImpact::TestCoverage testCoverage(AZStd::vector{}); + + // Expect the test coverage fields to be empty + EXPECT_EQ(testCoverage.GetNumModulesCovered(), 0); + EXPECT_EQ(testCoverage.GetNumSourcesCovered(), 0); + EXPECT_TRUE(testCoverage.GetModuleCoverages().empty()); + EXPECT_TRUE(testCoverage.GetSourcesCovered().empty()); + } + + TEST_P(TestCoverageFixtureWithCoverageParams, AllCoveragePermutations_ExpectTestCoverageMetaDatasToMatchPermutations) + { + // Given a procedurally generated test coverage + const TestImpact::TestCoverage testCoverage(GenerateModuleCoverages(m_numModulesCovered, m_coverageLevel)); + + // Expect the test coverage data and meta-data to match that of the rules used to procedurally generate the coverage data + ValidateTestCoverage(testCoverage); + } + + INSTANTIATE_TEST_CASE_P( + , + TestCoverageFixtureWithCoverageParams, + ::testing::Combine( + ::testing::Range(1u, 11u), + ::testing::Values(TestImpact::CoverageLevel::Line, TestImpact::CoverageLevel::Source)) + ); +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEnumeratorTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEnumeratorTest.cpp new file mode 100644 index 0000000000..9b67eafa54 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEnumeratorTest.cpp @@ -0,0 +1,890 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + using JobExceptionPolicy = TestImpact::TestEnumerator::JobExceptionPolicy; + using CacheExceptionPolicy = TestImpact::TestEnumerator::CacheExceptionPolicy; + + // Generates the command to run the given test target through AzTestRunner and get gtest to output the enumeration file + AZStd::string GetEnumerateCommandForTarget(AZStd::pair testTarget) + { + return AZStd::string::format( + "%s %s AzRunUnitTests --gtest_list_tests --gtest_output=xml:%s", + LY_TEST_IMPACT_AZ_TESTRUNNER_BIN, + testTarget.first.c_str(), // Path to test target bin + testTarget.second.c_str()); // Path to test target gtest enumeration file + } + + class TestEnumeratorFixture + : public AllocatorsTestFixture + { + public: + void SetUp() override; + + protected: + using JobInfo = TestImpact::TestEnumerator::JobInfo; + using JobData = TestImpact::TestEnumerator::JobData; + + AZStd::vector m_jobInfos; + AZStd::unique_ptr m_testEnumerator; + AZStd::vector m_testTargetJobArgs; + AZStd::vector> m_testTargetPaths; + AZStd::vector m_expectedTestTargetEnumerations; + AZStd::vector m_cacheFiles; + size_t m_maxConcurrency = 0; + }; + + void TestEnumeratorFixture::SetUp() + { + UnitTest::AllocatorsTestFixture::SetUp(); + + DeleteFiles(LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR, "*.cache"); + DeleteFiles(LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR, "*.xml"); + + // first: path to test target bin + // second: path to test target gtest enumeration file in XML format + const AZStd::string enumPath = AZStd::string(LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR) + "/%s.Enumeration.xml"; + m_testTargetPaths.emplace_back( + LY_TEST_IMPACT_TEST_TARGET_A_BIN, AZStd::string::format(enumPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME)); + m_testTargetPaths.emplace_back( + LY_TEST_IMPACT_TEST_TARGET_B_BIN, AZStd::string::format(enumPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME)); + m_testTargetPaths.emplace_back( + LY_TEST_IMPACT_TEST_TARGET_C_BIN, AZStd::string::format(enumPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME)); + m_testTargetPaths.emplace_back( + LY_TEST_IMPACT_TEST_TARGET_D_BIN, AZStd::string::format(enumPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME)); + + m_expectedTestTargetEnumerations.emplace_back(GetTestTargetATestEnumerationSuites()); + m_expectedTestTargetEnumerations.emplace_back(GetTestTargetBTestEnumerationSuites()); + m_expectedTestTargetEnumerations.emplace_back(GetTestTargetCTestEnumerationSuites()); + m_expectedTestTargetEnumerations.emplace_back(GetTestTargetDTestEnumerationSuites()); + + // Path to enumeration file in TIAF internal JSON format + m_cacheFiles.emplace_back( + AZStd::string::format("%s/%s.cache", LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR, LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME)); + m_cacheFiles.emplace_back( + AZStd::string::format("%s/%s.cache", LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR, LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME)); + m_cacheFiles.emplace_back( + AZStd::string::format("%s/%s.cache", LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR, LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME)); + m_cacheFiles.emplace_back( + AZStd::string::format("%s/%s.cache", LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR, LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME)); + + for (const auto& testTarget : m_testTargetPaths) + { + m_testTargetJobArgs.emplace_back(GetEnumerateCommandForTarget(testTarget)); + } + } + + // Fixture parameterized for different max number of concurrent jobs + class TestEnumeratorFixtureWithConcurrencyParams + : public TestEnumeratorFixture + , public ::testing::WithParamInterface + { + public: + void SetUp() override + { + TestEnumeratorFixture::SetUp(); + m_maxConcurrency = GetParam(); + } + }; + + using ConcurrencyAndJobExceptionPermutation = AZStd::tuple + < + size_t, // Max number of concurrent processes + JobExceptionPolicy // Test job exception policy + >; + + // Fixture parameterized for different max number of concurrent jobs and different job exception policies + class TestEnumeratorFixtureWithConcurrencyAndJobExceptionParams + : public TestEnumeratorFixture + , public ::testing::WithParamInterface + { + public: + void SetUp() override + { + TestEnumeratorFixture::SetUp(); + const auto& [maxConcurrency, jobExceptionPolicy] = GetParam(); + m_maxConcurrency = maxConcurrency; + m_jobExceptionPolicy = jobExceptionPolicy; + } + + protected: + JobExceptionPolicy m_jobExceptionPolicy = JobExceptionPolicy::Never; + }; + + using ConcurrencyAndCacheExceptionPermutation = AZStd::tuple + < + size_t, // Max number of concurrent processes + CacheExceptionPolicy // Test enumeration exception policy + >; + + // Fixture parameterized for different max number of concurrent jobs and different enumeration exception policies + class TestEnumeratorFixtureWithConcurrencyAndCacheExceptionParams + : public TestEnumeratorFixture + , public ::testing::WithParamInterface + { + public: + void SetUp() override + { + TestEnumeratorFixture::SetUp(); + const auto& [maxConcurrency, cacheExceptionPolicy] = GetParam(); + m_maxConcurrency = maxConcurrency; + m_cacheExceptionPolicy = cacheExceptionPolicy; + } + + protected: + CacheExceptionPolicy m_cacheExceptionPolicy = CacheExceptionPolicy::Never; + }; + + namespace + { + AZStd::array MaxConcurrentEnumerations = {{1, 2, 3, 4}}; + + AZStd::array JobExceptionPolicies = + { + JobExceptionPolicy::Never, + JobExceptionPolicy::OnExecutedWithFailure, + JobExceptionPolicy::OnFailedToExecute + }; + + AZStd::array CacheExceptionPolicies = + { + CacheExceptionPolicy::Never, + CacheExceptionPolicy::OnCacheNotExist, + CacheExceptionPolicy::OnCacheReadFailure, + CacheExceptionPolicy::OnCacheWriteFailure + }; + } + + // Validates that the specified job successfully read from its test enumeration cache + void ValidateJobSuccessfulCacheRead(const TestImpact::TestEnumerator::Job& job) + { + EXPECT_EQ(job.GetResult(), TestImpact::JobResult::NotExecuted); + EXPECT_EQ(job.GetStartTime(), AZStd::chrono::high_resolution_clock::time_point()); + EXPECT_EQ(job.GetEndTime(), AZStd::chrono::high_resolution_clock::time_point()); + EXPECT_EQ(job.GetDuration(), AZStd::chrono::milliseconds(0)); + EXPECT_FALSE(job.GetReturnCode().has_value()); + EXPECT_TRUE(job.GetPayload().has_value()); + } + + // Validates that the specified test enumeration matched the expected output + void ValidateTestTargetEnumeration(const TestImpact::TestEnumeration& actualResult, const TestImpact::TestEnumeration& expectedResult) + { + EXPECT_TRUE(actualResult == expectedResult); + EXPECT_EQ(actualResult.GetNumTestSuites(), CalculateNumTestSuites(expectedResult.GetTestSuites())); + EXPECT_EQ(actualResult.GetNumTests(), CalculateNumTests(expectedResult.GetTestSuites())); + EXPECT_EQ(actualResult.GetNumEnabledTests(), CalculateNumEnabledTests(expectedResult.GetTestSuites())); + EXPECT_EQ(actualResult.GetNumDisabledTests(), CalculateNumDisabledTests(expectedResult.GetTestSuites())); + } + + // Validates that the specified test enumeration cache matches the expected output + void ValidateTestEnumerationCache(const AZ::IO::Path& cacheFile, const TestImpact::TestEnumeration& expectedEnumeration) + { + // Cache file must exist + const auto fileSize = AZ::IO::SystemFile::Length(cacheFile.c_str()); + EXPECT_GT(fileSize, 0); + + // Read raw byte data from cache + AZStd::vector buffer(fileSize + 1); + buffer[fileSize] = 0; + EXPECT_TRUE(AZ::IO::SystemFile::Read(cacheFile.c_str(), buffer.data())); + + // Transform raw byte data to raw string data and attempt to construct the test enumeration + AZStd::string rawEnum(buffer.begin(), buffer.end()); + TestImpact::TestEnumeration actualEnumeration = TestImpact::DeserializeTestEnumeration(rawEnum); + + // Check that the constructed test enumeration matches the expected enumeration + ValidateTestTargetEnumeration(actualEnumeration, expectedEnumeration); + } + + // Validates that the specified cache file does not exist + void ValidateInvalidTestEnumerationCache(const AZ::IO::Path& cacheFile) + { + EXPECT_FALSE(AZ::IO::SystemFile::Exists(cacheFile.c_str())); + } + + TEST_P( + TestEnumeratorFixtureWithConcurrencyAndJobExceptionParams, InvalidCommandArgument_ExpectJobResulFailedToExecuteeOrTestJobException) + { + // Given a test enumerator with no client callback or enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + m_maxConcurrency, + AZStd::nullopt, + AZStd::nullopt); + + // Given a mixture of test enumeration jobs with valid and invalid command arguments + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + const AZStd::string args = (jobId % 2) ? InvalidProcessPath : m_testTargetJobArgs[jobId]; + JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); + m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); + } + + try + { + // When the test enumeration jobs are executed with different exception policies + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, m_jobExceptionPolicy); + + // Expect this statement to be reachable only if no exception policy for launch failures + EXPECT_FALSE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)); + + for (const auto& job : enumerationJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + if (jobId % 2) + { + // Expect invalid jobs have a job result of FailedToExecute + ValidateJobFailedToExecute(job); + } + else + { + // Expect the valid jobs job to successfully result in a test enumeration that matches the expected test enumeration data + ValidateJobExecutedSuccessfully(job); + ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); + } + } + } + catch ([[maybe_unused]] const TestImpact::TestJobException& e) + { + // Expect this statement to be reachable only if there is an exception policy for launch failures + EXPECT_TRUE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_P( + TestEnumeratorFixtureWithConcurrencyAndJobExceptionParams, ErroneousReturnCode_ExpectJobResultExecutedWithFailureOrTestJobException) + { + // Given a test enumerator with no client callback or enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + m_maxConcurrency, + AZStd::nullopt, + AZStd::nullopt); + + // Given a mixture of test enumeration jobs that execute and return either successfully or with failure + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); + const AZStd::string args = (jobId % 2) + ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, AZStd::chrono::milliseconds(0)).c_str()) + : m_testTargetJobArgs[jobId]; + m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); + } + + try + { + // When the test enumeration jobs are executed with different exception policies + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, m_jobExceptionPolicy); + + // Expect this statement to be reachable only if no exception policy for jobs that return with error + EXPECT_FALSE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)); + + for (const auto& job : enumerationJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + if (jobId % 2) + { + // Expect failed jobs to have job result ExecutedWithFailure and a non-zero return code + ValidateJobExecutedWithFailure(job); + } + else + { + // Expect the valid jobs job to successfully result in a test enumeration that matches the expected test enumeration data + ValidateJobExecutedSuccessfully(job); + ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); + } + } + } + catch ([[maybe_unused]] const TestImpact::TestJobException& e) + { + // Expect this statement to be reachable only if there is an exception policy for jobs that return with error + EXPECT_TRUE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_P(TestEnumeratorFixtureWithConcurrencyAndCacheExceptionParams, EmptyCacheRead_NoCacheDataButEnumerationsMatchTestSuitesInTarget) + { + // Given a test enumerator with no client callback, enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + m_maxConcurrency, + AZStd::nullopt, + AZStd::nullopt); + + // Given an test enumeration job for each test target that reads from an enumeration caching + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second, JobData::Cache{JobData::CachePolicy::Read, m_cacheFiles[jobId]}); + m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); + } + + try + { + // When the test enumeration jobs are executed with different exception policies + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, m_cacheExceptionPolicy, JobExceptionPolicy::Never); + + // Expect this statement to be reachable only if no exception policy for read attempts of non-existent caches + EXPECT_FALSE(::IsFlagSet(m_cacheExceptionPolicy, CacheExceptionPolicy::OnCacheNotExist)); + + // Expect each job to successfully result in a test enumeration that matches the expected test enumeration data for that test target + // even though the cache files could not be read + for (const auto& job : enumerationJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + ValidateJobExecutedSuccessfully(job); + ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); + ValidateInvalidTestEnumerationCache(job.GetJobInfo().GetCache()->m_file); + } + } + catch ([[maybe_unused]] const TestImpact::TestEnumerationException& e) + { + // Expect this statement to be reachable only if there is an exception policy for read attempts of non-existent caches + EXPECT_TRUE(::IsFlagSet(m_cacheExceptionPolicy, CacheExceptionPolicy::OnCacheNotExist)); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // Note: this test only cues up one test for enumeration but still runs the permutations for max concurrency so there is duplicated work + TEST_P( + TestEnumeratorFixtureWithConcurrencyAndCacheExceptionParams, + EmptyCacheDataRead_ExpectEnumerationsMatchTestSuitesInTargetOrTestEnumerationException) + { + // Given an enumeration cache for Test Target A with invalid JSON data + WriteTextToFile("", m_cacheFiles[TestTargetA]); + + // Given a test enumerator with no client callback, enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + m_maxConcurrency, + AZStd::nullopt, + AZStd::nullopt); + + // Given an test enumeration job that will attempt to read an invalid enumeration cache + JobData jobData(m_testTargetPaths[TestTargetA].second, JobData::Cache{JobData::CachePolicy::Read, m_cacheFiles[TestTargetA]}); + m_jobInfos.emplace_back(JobInfo({TestTargetA}, m_testTargetJobArgs[TestTargetA], AZStd::move(jobData))); + + try + { + // When the test enumeration jobs are executed with different exception policies + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, m_cacheExceptionPolicy, JobExceptionPolicy::Never); + + // Expect this statement to be reachable only if no exception policy for cache reads that fail + EXPECT_FALSE(::IsFlagSet(m_cacheExceptionPolicy, CacheExceptionPolicy::OnCacheReadFailure)); + + for (const auto& job : enumerationJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + + // Expect the valid jobs job to successfully result in a test enumeration that matches the expected test enumeration data + ValidateJobExecutedSuccessfully(job); + ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); + } + } + catch ([[maybe_unused]] const TestImpact::TestEnumerationException& e) + { + // Expect this statement to be reachable only if there is an exception policy for cache reads that fail + EXPECT_TRUE(::IsFlagSet(m_cacheExceptionPolicy, CacheExceptionPolicy::OnCacheReadFailure)); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_P( + TestEnumeratorFixtureWithConcurrencyAndCacheExceptionParams, + InvalidCacheWrite_ExpectEnumerationsMatchTestSuitesInTargetOrTestEnumerationException) + { + // Given a test enumerator with no client callback,, enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + m_maxConcurrency, + AZStd::nullopt, + AZStd::nullopt); + + // Given an test enumeration job that will attempt to write to an invalid enumeration cache + JobData jobData(m_testTargetPaths[TestTargetA].second, JobData::Cache{JobData::CachePolicy::Write, InvalidProcessPath}); + m_jobInfos.emplace_back(JobInfo({TestTargetA}, m_testTargetJobArgs[TestTargetA], AZStd::move(jobData))); + + try + { + // When the test enumeration job is executed + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, m_cacheExceptionPolicy, JobExceptionPolicy::Never); + + // Expect this statement to be reachable only if no exception policy for cache writes that fail + EXPECT_FALSE(::IsFlagSet(m_cacheExceptionPolicy, CacheExceptionPolicy::OnCacheWriteFailure)); + + for (const auto& job : enumerationJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + + // Expect the valid jobs job to successfully result in a test enumeration that matches the expected test enumeration data + ValidateJobExecutedSuccessfully(job); + ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); + } + } + catch ([[maybe_unused]] const TestImpact::TestEnumerationException& e) + { + // Expect this statement to be reachable only if there is an exception policy for cache writes that fail + EXPECT_TRUE(::IsFlagSet(m_cacheExceptionPolicy, CacheExceptionPolicy::OnCacheWriteFailure)); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_P(TestEnumeratorFixtureWithConcurrencyParams, ValidAndInvalidCacheRead_CachedEnumerationsMatchTestSuitesInTarget) + { + // Given the cache file written for only test target B + WriteTextToFile(TestImpact::SerializeTestEnumeration(m_expectedTestTargetEnumerations[TestTargetB]), m_cacheFiles[TestTargetB]); + + // Given a test enumerator with no client callback, enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + m_maxConcurrency, + AZStd::nullopt, + AZStd::nullopt); + + // Given test enumeration jobs test target A and D with no enumeration caching + m_jobInfos.emplace_back( + JobInfo({TestTargetA}, m_testTargetJobArgs[TestTargetA], JobData{m_testTargetPaths[TestTargetA].second, AZStd::nullopt})); + m_jobInfos.emplace_back( + JobInfo({TestTargetD}, m_testTargetJobArgs[TestTargetD], JobData{m_testTargetPaths[TestTargetD].second, AZStd::nullopt})); + + // Given test target B with enumeration cache reading and a valid cache file + m_jobInfos.emplace_back(JobInfo( + {TestTargetB}, m_testTargetJobArgs[TestTargetB], + JobData{m_testTargetPaths[TestTargetB].second, JobInfo::Cache{JobData::CachePolicy::Read, m_cacheFiles[TestTargetB]}})); + + // Given test target C with enumeration cache reading and an invalid cache file + m_jobInfos.emplace_back(JobInfo( + {TestTargetC}, m_testTargetJobArgs[TestTargetC], + JobData{m_testTargetPaths[TestTargetC].second, JobInfo::Cache{JobData::CachePolicy::Read, "nothing"}})); + + // When the test enumeration jobs are executed + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Expect each job to successfully result in a test enumeration that matches the expected test enumeration data for that test target + for (const auto& job : enumerationJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + + switch (jobId) + { + case TestTargetA: // No cache read + case TestTargetC: // Cache read, but invalid cache, so re-enumerate anyway + case TestTargetD: // No cache read + { + ValidateJobExecutedSuccessfully(job); + break; + } + case TestTargetB: // Cache read, successful cache read, so job not executed + { + ValidateJobSuccessfulCacheRead(job); + break; + } + default: + { + FAIL(); + } + } + + // Regardless of cache policy and cache failures all targets should still produce the expected test enumerations + ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); + } + } + + TEST_F(TestEnumeratorFixture, InvalidCacheDataRead_TestEnumerationException) + { + // Given an enumeration cache for Test Target A with invalid JSON data + WriteTextToFile("There is no valid cache data here", m_cacheFiles[TestTargetA]); + + // Given a test enumerator with no client callback, concurrency, enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + OneConcurrentProcess, + AZStd::nullopt, + AZStd::nullopt); + + // Given an test enumeration job that will attempt to read an invalid enumeration cache + JobData jobData(m_testTargetPaths[TestTargetA].second, JobData::Cache{JobData::CachePolicy::Read, m_cacheFiles[TestTargetA]}); + m_jobInfos.emplace_back(JobInfo({TestTargetA}, m_testTargetJobArgs[TestTargetA], AZStd::move(jobData))); + + try + { + // When the test enumeration jobs are executed with different exception policies + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); + } + catch ([[maybe_unused]] const TestImpact::TestEnumerationException& e) + { + // Expect this statement to be reachable only if there is an exception policy for jobs that return with error + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_P(TestEnumeratorFixtureWithConcurrencyParams, ValidCacheWrite_CachedEnumerationsMatchTestSuitesInTarget) + { + // Given a test enumerator with no client callback, enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + m_maxConcurrency, + AZStd::nullopt, + AZStd::nullopt); + + // Given an test enumeration job for each test target with write enumeration caching + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second, JobData::Cache{JobData::CachePolicy::Write, m_cacheFiles[jobId]}); + m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); + } + + // When the test enumeration jobs are executed + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Expect each job to successfully result in a test enumeration and cache that matches the expected test enumeration data for that + // test target + for (const auto& job : enumerationJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + ValidateJobExecutedSuccessfully(job); + ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); + ValidateTestEnumerationCache(job.GetJobInfo().GetCache()->m_file, m_expectedTestTargetEnumerations[jobId]); + } + } + + TEST_F(TestEnumeratorFixture, EmptyArtifact_ExpectTestEnumerationException) + { + // Given a test enumerator with no client callback, concurrency, enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + OneConcurrentProcess, + AZStd::nullopt, + AZStd::nullopt); + + // Given an test enumeration job that will return successfully but with an empty artifact string + m_jobInfos.emplace_back(JobInfo({0}, m_testTargetJobArgs[0], JobData("", AZStd::nullopt))); + + try + { + // When the test enumeration job is executed + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::TestEnumerationException& e) + { + // Expect an enumeration exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(TestEnumeratorFixture, InvalidArtifact_ExpectTestEnumerationException) + { + // Given a test enumerator with no client callback, concurrency, enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + OneConcurrentProcess, + AZStd::nullopt, + AZStd::nullopt); + + // Given an test enumeration job that will return successfully but not produce an artifact + m_jobInfos.emplace_back(JobInfo( + {0}, AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(0, AZStd::chrono::milliseconds(0)).c_str()), + JobData("", AZStd::nullopt))); + + try + { + // When the test enumeration job is executed + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::TestEnumerationException& e) + { + // Expect an enumeration exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_P(TestEnumeratorFixtureWithConcurrencyParams, EnumerateTestTargets_EnumerationsMatchTestSuitesInTarget) + { + // Given a test enumerator with no client callback, enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + m_maxConcurrency, + AZStd::nullopt, + AZStd::nullopt); + + // Given an test enumeration job for each test target with no enumeration caching + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); + m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); + } + + // When the test enumeration jobs are executed + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Expect each job to successfully result in a test enumeration that matches the expected test enumeration data for that test target + for (const auto& job : enumerationJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + ValidateJobExecutedSuccessfully(job); + ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); + } + } + + TEST_P(TestEnumeratorFixtureWithConcurrencyParams, EnumerateTestTargetsWithArbitraryJobIds_EnumerationsMatchTestSuitesInTarget) + { + // Given a set of arbitrary job ids to be used for the test target jobs + enum + { + ArbitraryA = 36, + ArbitraryB = 890, + ArbitraryC = 19, + ArbitraryD = 1 + }; + + const AZStd::unordered_map sequentialToArbitrary = + { + {TestTargetA, ArbitraryA}, + {TestTargetB, ArbitraryB}, + {TestTargetC, ArbitraryC}, + {TestTargetD, ArbitraryD}, + }; + + const AZStd::unordered_map arbitraryToSequential = + { + {ArbitraryA, TestTargetA}, + {ArbitraryB, TestTargetB}, + {ArbitraryC, TestTargetC}, + {ArbitraryD, TestTargetD}, + }; + + // Given a test enumerator with no client callback, enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + m_maxConcurrency, + AZStd::nullopt, + AZStd::nullopt); + + // Given an test enumeration job for each test target with no enumeration caching + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); + m_jobInfos.emplace_back(JobInfo({sequentialToArbitrary.at(jobId)}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); + } + + // When the test enumeration jobs are executed + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Expect each job to successfully result in a test enumeration that matches the expected test enumeration data for that test target + for (const auto& job : enumerationJobs) + { + const JobInfo::IdType jobId = arbitraryToSequential.at(job.GetJobInfo().GetId().m_value); + ValidateJobExecutedSuccessfully(job); + ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); + } + } + + TEST_P(TestEnumeratorFixtureWithConcurrencyParams, EnumerateTestTargetsWithCallback_EnumerationsMatchTestSuitesInTarget) + { + // Given a client callback function that tracks the number of successful enumerations + size_t numSuccesses = 0; + const auto jobCallback = [&numSuccesses]([[maybe_unused]] const TestImpact::TestEnumerator::JobInfo& jobInfo, const TestImpact::JobMeta& meta) + { + if (meta.m_result == TestImpact::JobResult::ExecutedWithSuccess) + { + numSuccesses++; + } + }; + + // Given a test enumerator with no enumeration timeout or enumerator timeout + m_testEnumerator = AZStd::make_unique( + jobCallback, + m_maxConcurrency, + AZStd::nullopt, + AZStd::nullopt); + + // Given an test enumeration job for each test target with no enumeration caching + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); + m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); + } + + // When the test enumeration jobs are executed + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Expect the number of successful enumerations tracked in the callback to match the number of test targets enumerated + EXPECT_EQ(numSuccesses, enumerationJobs.size()); + + // Expect each job to successfully result in a test enumeration that matches the expected test enumeration data for that test target + for (const auto& job : enumerationJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + ValidateJobExecutedSuccessfully(job); + ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); + } + } + + TEST_P(TestEnumeratorFixtureWithConcurrencyParams, JobRunnerTimeout_InFlightJobsTimeoutAndQueuedJobsUnlaunched) + { + // Given a test enumerator with no client callback or enumerator timeout and 500ms enumeration timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + m_maxConcurrency, + AZStd::chrono::milliseconds(500), + AZStd::nullopt); + + // Given an test enumeration job for each test target with no enumeration caching where half will sleep indefinitely + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); + const AZStd::string args = (jobId % 2) + ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, LongSleep).c_str()) + : m_testTargetJobArgs[jobId]; + m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); + } + + // When the test enumeration jobs are executed + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Expect half the jobs to successfully result in a test enumeration that matches the expected test enumeration data for that test + // target with the other half having timed out + for (const auto& job : enumerationJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + if (jobId % 2) + { + ValidateJobTimeout(job); + } + else + { + ValidateJobExecutedSuccessfully(job); + ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); + } + } + } + + TEST_F(TestEnumeratorFixture, JobTimeout_InFlightJobTimeoutAndQueuedJobsUnlaunched) + { + // Given a test enumerator with no client callback or enumeration timeout and a 5 second enumerator timeout + m_testEnumerator = AZStd::make_unique( + AZStd::nullopt, + FourConcurrentProcesses, + AZStd::nullopt, + AZStd::chrono::milliseconds(5000)); + + // Given an test enumeration job for each test target with no enumeration caching where half will sleep indefinitely + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); + const AZStd::string args = (jobId % 2) + ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, LongSleep).c_str()) + : m_testTargetJobArgs[jobId]; + m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); + } + + // When the test enumeration jobs are executed + const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); + + // Expect half the jobs to successfully result in a test enumeration that matches the expected test enumeration data for that test + // target with the other half having timed out + for (const auto& job : enumerationJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + if (jobId % 2) + { + ValidateJobTimeout(job); + } + else + { + ValidateJobExecutedSuccessfully(job); + ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); + } + } + } + + INSTANTIATE_TEST_CASE_P( + , + TestEnumeratorFixtureWithConcurrencyAndJobExceptionParams, + ::testing::Combine( + ::testing::ValuesIn(MaxConcurrentEnumerations), + ::testing::ValuesIn(JobExceptionPolicies)) + ); + + INSTANTIATE_TEST_CASE_P( + , + TestEnumeratorFixtureWithConcurrencyAndCacheExceptionParams, + ::testing::Combine( + ::testing::ValuesIn(MaxConcurrentEnumerations), + ::testing::ValuesIn(CacheExceptionPolicies)) + ); + + INSTANTIATE_TEST_CASE_P( + , + TestEnumeratorFixtureWithConcurrencyParams, + ::testing::ValuesIn(MaxConcurrentEnumerations) + ); +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEumerationSerializerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEumerationSerializerTest.cpp new file mode 100644 index 0000000000..aa94657378 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEumerationSerializerTest.cpp @@ -0,0 +1,99 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +#include +#include + +namespace UnitTest +{ + const TestImpact::TestEnumeration EmptyTestEnum(AZStd::vector{}); + + TEST(TestEnumerationSerializer, EmptyTestEnumerationSuites_ExpectTemptyTestEnumeration) + { + // Given an empty set of test enumeration suites + // When the test enumeration is serialized and deserialized back again + const AZStd::string serializedString = TestImpact::SerializeTestEnumeration(EmptyTestEnum); + const TestImpact::TestEnumeration actualEnumeration = TestImpact::DeserializeTestEnumeration(serializedString); + + // Expect the actual test enumeration to match the expected test enumeration + EXPECT_TRUE(actualEnumeration == EmptyTestEnum); + } + + TEST(TestEnumerationSerializer, SerializeAndDeserializeSuitesForTestTargetA_ActualSuiteDataMatchesExpectedSuiteData) + { + // Given the test of test enumeration suites for Test target A + const TestImpact::TestEnumeration expectedEnumeration = TestImpact::TestEnumeration(GetTestTargetATestEnumerationSuites()); + + // When the test enumeration is serialized and deserialized back again + const AZStd::string serializedString = TestImpact::SerializeTestEnumeration(expectedEnumeration); + const TestImpact::TestEnumeration actualEnumeration = TestImpact::DeserializeTestEnumeration(serializedString); + + // Expect the actual test enumeration to match the expected test enumeration + EXPECT_TRUE(actualEnumeration == expectedEnumeration); + + // Do not expect the actual test enumeration to match the empty test enumeration + EXPECT_FALSE(actualEnumeration == EmptyTestEnum); + } + + TEST(TestEnumerationSerializer, SerializeAndDeserializeSuitesForTestTargetB_ActualSuiteDataMatchesExpectedSuiteData) + { + // Given the test of test enumeration suites for Test target B + const TestImpact::TestEnumeration expectedEnumeration = TestImpact::TestEnumeration(GetTestTargetBTestEnumerationSuites()); + + // When the test enumeration is serialized and deserialized back again + const AZStd::string serializedString = TestImpact::SerializeTestEnumeration(expectedEnumeration); + const TestImpact::TestEnumeration actualEnumeration = TestImpact::DeserializeTestEnumeration(serializedString); + + // Expect the actual test enumeration to match the expected test enumeration + EXPECT_TRUE(actualEnumeration == expectedEnumeration); + + // Do not expect the actual test enumeration to match the empty test enumeration + EXPECT_FALSE(actualEnumeration == EmptyTestEnum); + } + + TEST(TestEnumerationSerializer, SerializeAndDeserializeSuitesForTestTargetC_ActualSuiteDataMatchesExpectedSuiteData) + { + // Given the test of test enumeration suites for Test target B + const TestImpact::TestEnumeration expectedEnumeration = TestImpact::TestEnumeration(GetTestTargetCTestEnumerationSuites()); + + // When the test enumeration is serialized and deserialized back again + const AZStd::string serializedString = TestImpact::SerializeTestEnumeration(expectedEnumeration); + const TestImpact::TestEnumeration actualEnumeration = TestImpact::DeserializeTestEnumeration(serializedString); + + // Expect the actual test enumeration to match the expected test enumeration + EXPECT_TRUE(actualEnumeration == expectedEnumeration); + + // Do not expect the actual test enumeration to match the empty test enumeration + EXPECT_FALSE(actualEnumeration == EmptyTestEnum); + } + + TEST(TestEnumerationSerializer, SerializeAndDeserializeSuitesForTestTargetD_ActualSuiteDataMatchesExpectedSuiteData) + { + // Given the test of test enumeration suites for Test target B + const TestImpact::TestEnumeration expectedEnumeration = TestImpact::TestEnumeration(GetTestTargetDTestEnumerationSuites()); + + // When the test enumeration is serialized and deserialized back again + const AZStd::string serializedString = TestImpact::SerializeTestEnumeration(expectedEnumeration); + const TestImpact::TestEnumeration actualEnumeration = TestImpact::DeserializeTestEnumeration(serializedString); + + // Expect the actual test enumeration to match the expected test enumeration + EXPECT_TRUE(actualEnumeration == expectedEnumeration); + + // Do not expect the actual test enumeration to match the empty test enumeration + EXPECT_FALSE(actualEnumeration == EmptyTestEnum); + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunSerializerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunSerializerTest.cpp new file mode 100644 index 0000000000..1fc7c62b3d --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunSerializerTest.cpp @@ -0,0 +1,99 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +#include +#include + +namespace UnitTest +{ + const TestImpact::TestRun EmptyTestRun(AZStd::vector{}, AZStd::chrono::milliseconds{0}); + + TEST(TestRunSerializer, EmptyTestRunSuites_ExpectTemptyTestRun) + { + // Given an empty set of test run suites + // When the test run is serialized and deserialized back again + const auto serializedString = TestImpact::SerializeTestRun(EmptyTestRun); + const auto actualRun = TestImpact::DeserializeTestRun(serializedString); + + // Expect the actual test run to match the expected test run + EXPECT_TRUE(actualRun == EmptyTestRun); + } + + TEST(TestRunSerializer, SerializeAndDeserializeSuitesForTestTargetA_ActualSuiteDataMatchesExpectedSuiteData) + { + // Given the test of test run suites for Test target A + const auto expectedRun = TestImpact::TestRun(GetTestTargetATestRunSuites(), AZStd::chrono::milliseconds{500}); + + // When the test run is serialized and deserialized back again + const auto serializedString = TestImpact::SerializeTestRun(expectedRun); + const auto actualRun = TestImpact::DeserializeTestRun(serializedString); + + // Expect the actual test run to match the expected test run + EXPECT_TRUE(actualRun == expectedRun); + + // Do not expect the actual test run to match the empty test run + EXPECT_FALSE(actualRun == EmptyTestRun); + } + + TEST(TestRunSerializer, SerializeAndDeserializeSuitesForTestTargetB_ActualSuiteDataMatchesExpectedSuiteData) + { + // Given the test of test run suites for Test target B + const auto expectedRun = TestImpact::TestRun(GetTestTargetBTestRunSuites(), AZStd::chrono::milliseconds{500}); + + // When the test run is serialized and deserialized back again + const auto serializedString = TestImpact::SerializeTestRun(expectedRun); + const auto actualRun = TestImpact::DeserializeTestRun(serializedString); + + // Expect the actual test run to match the expected test run + EXPECT_TRUE(actualRun == expectedRun); + + // Do not expect the actual test run to match the empty test run + EXPECT_FALSE(actualRun == EmptyTestRun); + } + + TEST(TestRunSerializer, SerializeAndDeserializeSuitesForTestTargetC_ActualSuiteDataMatchesExpectedSuiteData) + { + // Given the test of test run suites for Test target B + const auto expectedRun = TestImpact::TestRun(GetTestTargetCTestRunSuites(), AZStd::chrono::milliseconds{500}); + + // When the test run is serialized and deserialized back again + const auto serializedString = TestImpact::SerializeTestRun(expectedRun); + const auto actualRun = TestImpact::DeserializeTestRun(serializedString); + + // Expect the actual test run to match the expected test run + EXPECT_TRUE(actualRun == expectedRun); + + // Do not expect the actual test run to match the empty test run + EXPECT_FALSE(actualRun == EmptyTestRun); + } + + TEST(TestRunSerializer, SerializeAndDeserializeSuitesForTestTargetD_ActualSuiteDataMatchesExpectedSuiteData) + { + // Given the test of test run suites for Test target B + const auto expectedRun = TestImpact::TestRun(GetTestTargetDTestRunSuites(), AZStd::chrono::milliseconds{500}); + + // When the test run is serialized and deserialized back again + const auto serializedString = TestImpact::SerializeTestRun(expectedRun); + const auto actualRun = TestImpact::DeserializeTestRun(serializedString); + + // Expect the actual test run to match the expected test run + EXPECT_TRUE(actualRun == expectedRun); + + // Do not expect the actual test run to match the empty test run + EXPECT_FALSE(actualRun == EmptyTestRun); + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunnerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunnerTest.cpp new file mode 100644 index 0000000000..08a0306e7f --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunnerTest.cpp @@ -0,0 +1,516 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + using JobExceptionPolicy = TestImpact::TestRunner::JobExceptionPolicy; + + AZStd::string GetRunCommandForTarget(AZStd::pair testTarget) + { + return AZStd::string::format( + "%s %s AzRunUnitTests --gtest_output=xml:%s", LY_TEST_IMPACT_AZ_TESTRUNNER_BIN, testTarget.first.c_str(), + testTarget.second.c_str()); + } + + class TestRunnerFixture + : public AllocatorsTestFixture + { + public: + void SetUp() override; + + protected: + using JobInfo = TestImpact::TestRunner::JobInfo; + using JobData = TestImpact::TestRunner::JobData; + + AZStd::vector m_jobInfos; + AZStd::unique_ptr m_testRunner; + AZStd::vector m_testTargetJobArgs; + AZStd::vector> m_testTargetPaths; + AZStd::vector m_expectedTestTargetRuns; + AZStd::vector m_expectedTestTargetResult; + size_t m_maxConcurrency = 0; + }; + + void TestRunnerFixture::SetUp() + { + UnitTest::AllocatorsTestFixture::SetUp(); + + DeleteFiles(LY_TEST_IMPACT_TEST_TARGET_RESULTS_DIR, "*.xml"); + + // first: path to test target bin + // second: path to test target gtest results file in XML format + const AZStd::string runPath = AZStd::string(LY_TEST_IMPACT_TEST_TARGET_RESULTS_DIR) + "/%s.Run.xml"; + m_testTargetPaths.emplace_back( + LY_TEST_IMPACT_TEST_TARGET_A_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME)); + m_testTargetPaths.emplace_back( + LY_TEST_IMPACT_TEST_TARGET_B_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME)); + m_testTargetPaths.emplace_back( + LY_TEST_IMPACT_TEST_TARGET_C_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME)); + m_testTargetPaths.emplace_back( + LY_TEST_IMPACT_TEST_TARGET_D_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME)); + + m_expectedTestTargetRuns.emplace_back(GetTestTargetATestRunSuites(), AZStd::chrono::milliseconds{500}); + m_expectedTestTargetRuns.emplace_back(GetTestTargetBTestRunSuites(), AZStd::chrono::milliseconds{500}); + m_expectedTestTargetRuns.emplace_back(GetTestTargetCTestRunSuites(), AZStd::chrono::milliseconds{500}); + m_expectedTestTargetRuns.emplace_back(GetTestTargetDTestRunSuites(), AZStd::chrono::milliseconds{500}); + + m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Failed); + m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Passed); + m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Passed); + m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Passed); + + for (const auto& testTarget : m_testTargetPaths) + { + m_testTargetJobArgs.emplace_back(GetRunCommandForTarget(testTarget)); + } + } + + // Fixture parameterized for different max number of concurrent jobs + class TestRunnerFixtureWithConcurrencyParams + : public TestRunnerFixture + , public ::testing::WithParamInterface + { + public: + void SetUp() override + { + TestRunnerFixture::SetUp(); + m_maxConcurrency = GetParam(); + } + }; + + using ConcurrencyAndJobExceptionPermutation = AZStd::tuple + < + size_t, // Max number of concurrent processes + JobExceptionPolicy // Test job exception policy + >; + + // Fixture parameterized for different max number of concurrent jobs and different job exception policies + class TestRunnerFixtureWithConcurrencyAndJobExceptionParams + : public TestRunnerFixture + , public ::testing::WithParamInterface + { + public: + void SetUp() override + { + TestRunnerFixture::SetUp(); + const auto& [maxConcurrency, jobExceptionPolicy] = GetParam(); + m_maxConcurrency = maxConcurrency; + m_jobExceptionPolicy = jobExceptionPolicy; + } + + protected: + JobExceptionPolicy m_jobExceptionPolicy = JobExceptionPolicy::Never; + }; + + class TestRunnerFixtureWithConcurrencyAndFailedToLaunchExceptionParams + : public TestRunnerFixtureWithConcurrencyAndJobExceptionParams + { + }; + + class TestRunnerFixtureWithConcurrencyAndExecutedWithFailureExceptionParams + : public TestRunnerFixtureWithConcurrencyAndJobExceptionParams + { + }; + + namespace + { + AZStd::array MaxConcurrentRuns = {1, 2, 3, 4}; + + AZStd::array FailedToLaunchExceptionPolicies = { + JobExceptionPolicy::Never, JobExceptionPolicy::OnFailedToExecute}; + + AZStd::array ExecutedWithFailureExceptionPolicies = { + JobExceptionPolicy::Never, JobExceptionPolicy::OnExecutedWithFailure}; + } // namespace + + TEST_P( + TestRunnerFixtureWithConcurrencyAndFailedToLaunchExceptionParams, + InvalidCommandArgument_ExpectJobResulFailedToExecuteeOrTestJobException) + { + // Given a test runner with no client callback or run timeout or runner timeout + m_testRunner = AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); + + // Given a mixture of test run jobs with valid and invalid command arguments + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + const AZStd::string args = (jobId % 2) ? InvalidProcessPath : m_testTargetJobArgs[jobId]; + JobData jobData(m_testTargetPaths[jobId].second); + m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); + } + + try + { + // When the test run jobs are executed with different exception policies + const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, m_jobExceptionPolicy); + + // Expect this statement to be reachable only if no exception policy for launch failures + EXPECT_FALSE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)); + + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + if (jobId % 2) + { + // Expect invalid jobs have a job result of FailedToExecute + ValidateJobFailedToExecute(job); + } + else + { + // Expect the valid jobs to successfully result in a test run that matches the expected test run data + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); + } + } + } + catch ([[maybe_unused]] const TestImpact::TestJobException& e) + { + // Expect this statement to be reachable only if there is an exception policy for launch failures + EXPECT_TRUE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_P( + TestRunnerFixtureWithConcurrencyAndExecutedWithFailureExceptionParams, + ErroneousReturnCode_ExpectJobResultExecutedWithFailureOrTestJobException) + { + // Given a test runner with no client callback or run timeout or runner timeout + m_testRunner = AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); + + // Given a mixture of test run jobs that execute and return either successfully or with failure + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second); + m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); + } + + try + { + // When the test run jobs are executed with different exception policies + const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, m_jobExceptionPolicy); + + // Expect this statement to be reachable only if no exception policy for jobs that return with error + EXPECT_FALSE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)); + + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + + // Expect the valid jobs to successfully result in a test run that matches the expected test run data + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); + } + } + catch ([[maybe_unused]] const TestImpact::TestJobException& e) + { + // Expect this statement to be reachable only if there is an exception policy for jobs that return with error + EXPECT_TRUE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)); + } + catch ([[maybe_unused]] const TestImpact::Exception& e) + { + FAIL(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(TestRunnerFixture, EmptyArtifact_ExpectTestRunnerException) + { + // Given a test runner with no client callback, concurrency, run timeout or runner timeout + m_testRunner = AZStd::make_unique(AZStd::nullopt, OneConcurrentProcess, AZStd::nullopt, AZStd::nullopt); + + // Given an test runner job that will return successfully but with an empty artifact string + m_jobInfos.emplace_back(JobInfo({0}, m_testTargetJobArgs[0], JobData(""))); + + try + { + // When the test runner job is executed + const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::TestRunException& e) + { + // Expect an runner exception + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(TestRunnerFixture, InvalidRunArtifact_ExpectArtifactException) + { + // Given a test run artifact with invalid contents + WriteTextToFile("There is nothing valid here", m_testTargetPaths[TestTargetA].second); + + // Given a job command that will write the test run artifact to a different location that what we will read from + auto invalidRunArtifact = m_testTargetPaths[TestTargetA]; + invalidRunArtifact.second /= ".xml"; + const AZStd::string args = GetRunCommandForTarget(invalidRunArtifact); + + // Given a test runner with no client callback, concurrency, run timeout or runner timeout + m_testRunner = AZStd::make_unique(AZStd::nullopt, OneConcurrentProcess, AZStd::nullopt, AZStd::nullopt); + + // Given an test runner job that will return successfully but not produce an artifact + JobData jobData(m_testTargetPaths[TestTargetA].second); + m_jobInfos.emplace_back(JobInfo({TestTargetA}, args, AZStd::move(jobData))); + + try + { + // When the test runner job is executed + const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); + + // Do not expect this statement to be reachable + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::ArtifactException& e) + { + // Expect an runner exception + SUCCEED(); + } + catch (const TestImpact::Exception& e) + { + std::cout << e.what(); + FAIL(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_P(TestRunnerFixtureWithConcurrencyParams, RunTestTargets_RunsMatchTestSuitesInTarget) + { + // Given a test runner with no client callback, runner timeout or runner timeout + m_testRunner = AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); + + // Given an test runner job for each test target with no runner caching + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second); + m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); + } + + // When the test runner jobs are executed + const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); + + // Expect each job to successfully result in a test runner that matches the expected test runner data for that test target + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); + } + } + + TEST_P(TestRunnerFixtureWithConcurrencyParams, RunTestTargetsWithArbitraryJobIds_RunsMatchTestSuitesInTarget) + { + // Given a set of arbitrary job ids to be used for the test target jobs + enum + { + ArbitraryA = 36, + ArbitraryB = 890, + ArbitraryC = 19, + ArbitraryD = 1 + }; + + const AZStd::unordered_map sequentialToArbitrary = + { + {TestTargetA, ArbitraryA}, + {TestTargetB, ArbitraryB}, + {TestTargetC, ArbitraryC}, + {TestTargetD, ArbitraryD}, + }; + + const AZStd::unordered_map arbitraryToSequential = + { + {ArbitraryA, TestTargetA}, + {ArbitraryB, TestTargetB}, + {ArbitraryC, TestTargetC}, + {ArbitraryD, TestTargetD}, + }; + + // Given a test runner with no client callback, run timeout or runner timeout + m_testRunner = AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); + + // Given an test run job for each test target + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second); + m_jobInfos.emplace_back(JobInfo({sequentialToArbitrary.at(jobId)}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); + } + + // When the test run jobs are executed + const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); + + // Expect each job to successfully result in a test run that matches the expected test run data for that test target + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = arbitraryToSequential.at(job.GetJobInfo().GetId().m_value); + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); + } + } + + TEST_P(TestRunnerFixtureWithConcurrencyParams, RunTestTargetsWithCallback_RunsMatchTestSuitesInTarget) + { + // Given a client callback function that tracks the number of successful runs + size_t numSuccesses = 0; + const auto jobCallback = + [&numSuccesses]([[maybe_unused]] const TestImpact::TestRunner::JobInfo& jobInfo, const TestImpact::JobMeta& meta) { + if (meta.m_result == TestImpact::JobResult::ExecutedWithSuccess) + { + numSuccesses++; + } + }; + + // Given a test runner with no run timeout or runner timeout + m_testRunner = AZStd::make_unique(jobCallback, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); + + // Given an test run job for each test target + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second); + m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); + } + + // When the test run jobs are executed + const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); + + // Expect the number of successful runs tracked in the callback to match the number of test targets run with no failures + EXPECT_EQ( + numSuccesses, + AZStd::count_if(m_expectedTestTargetResult.begin(), m_expectedTestTargetResult.end(), [](TestImpact::TestRunResult result) { + return result == TestImpact::TestRunResult::Passed; + })); + + // Expect each job to successfully result in a test run that matches the expected test run data for that test target + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); + } + } + + TEST_P(TestRunnerFixtureWithConcurrencyParams, JobRunnerTimeout_InFlightJobsTimeoutAndQueuedJobsUnlaunched) + { + // Given a test runner with no client callback or runner timeout and 500ms run timeout + m_testRunner = + AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::chrono::milliseconds(500), AZStd::nullopt); + + // Given an test run job for each test target where half will sleep indefinitely + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second); + const AZStd::string args = (jobId % 2) + ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, LongSleep).c_str()) + : m_testTargetJobArgs[jobId]; + m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); + } + + // When the test run jobs are executed + const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); + + // Expect half the jobs to successfully result in a test run that matches the expected test run data for that test target + // with the other half having timed out + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + if (jobId % 2) + { + ValidateJobTimeout(job); + } + else + { + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); + } + } + } + + TEST_F(TestRunnerFixture, JobTimeout_InFlightJobTimeoutAndQueuedJobsUnlaunched) + { + // Given a test runner with no client callback or run timeout and a 5 second runner timeout + m_testRunner = AZStd::make_unique( + AZStd::nullopt, FourConcurrentProcesses, AZStd::nullopt, AZStd::chrono::milliseconds(5000)); + + // Given an test run job for each test target where half will sleep indefinitely + for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) + { + JobData jobData(m_testTargetPaths[jobId].second); + const AZStd::string args = (jobId % 2) + ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, LongSleep).c_str()) + : m_testTargetJobArgs[jobId]; + m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); + } + + // When the test run jobs are executed + const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); + + // Expect half the jobs to successfully result in a test run that matches the expected test run data for that test target + // with the other half having timed out + for (const auto& job : runnerJobs) + { + const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; + if (jobId % 2) + { + ValidateJobTimeout(job); + } + else + { + ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); + ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); + } + } + } + + INSTANTIATE_TEST_CASE_P( + , + TestRunnerFixtureWithConcurrencyAndFailedToLaunchExceptionParams, + ::testing::Combine(::testing::ValuesIn(MaxConcurrentRuns), ::testing::ValuesIn(FailedToLaunchExceptionPolicies))); + + INSTANTIATE_TEST_CASE_P( + , + TestRunnerFixtureWithConcurrencyAndExecutedWithFailureExceptionParams, + ::testing::Combine(::testing::ValuesIn(MaxConcurrentRuns), ::testing::ValuesIn(ExecutedWithFailureExceptionPolicies))); + + INSTANTIATE_TEST_CASE_P(, TestRunnerFixtureWithConcurrencyParams, ::testing::ValuesIn(MaxConcurrentRuns)); +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactExceptionTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactExceptionTest.cpp new file mode 100644 index 0000000000..bf2ccb6bc7 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactExceptionTest.cpp @@ -0,0 +1,150 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +namespace UnitTest +{ + constexpr const char* Message = "String Constructor"; + + TEST(ExceptionTest, DefaultConstructor_HasEmptyMessageString) + { + // Given an exception instantiated with the default constructor + const TestImpact::Exception e; + + // Expect the message to be an empty string + EXPECT_STREQ(e.what(), "\0"); + } + + TEST(ExceptionTest, StringConstructor_HasSpecifiedMessageString) + { + // Given an exception instantiated with a string + const AZStd::string msg(Message); + const TestImpact::Exception e(msg); + + // Expect the message to be the specified string + EXPECT_STREQ(e.what(), Message); + } + + TEST(ExceptionTest, StringLiteralConstructor_HasSpecifiedMessageString) + { + // Given an exception instantiated with a string literal + const TestImpact::Exception e(Message); + + // Expect the message to be the specified string + EXPECT_STREQ(e.what(), Message); + } + + TEST(ExceptionTest, InitializedWithLocalString_HasCopyOfLocalStringString) + { + try + { + // Given an exception instantiated with a string in local scope + throw(TestImpact::Exception(AZStd::string::format("%s", Message))); + + // Do not expect this code to be reachable + FAIL(); + } + catch (const TestImpact::Exception& e) + { + // Expect the message to be the specified out-of-scope string + EXPECT_STREQ(e.what(), Message); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(ExceptionTest, InitializedWithLocalStringLiteral_HasCopyOfLocalStringString) + { + try + { + // Given an exception instantiated with a copy of the message string in local scope + throw(TestImpact::Exception(AZStd::string::format("%s", Message).c_str())); + + // Do not expect this code to be reachable + FAIL(); + } + catch (const TestImpact::Exception& e) + { + // Expect the message to be the specified out-of-scope string literal + EXPECT_STREQ(e.what(), Message); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(ExceptionTest, EvalMacroFails_ExceptionTestThrown) + { + try + { + // Given an exception instantiated with a string literal in local scope + AZ_TestImpact_Eval(false, TestImpact::Exception, Message); + + // Do not expect this code to be reachable + FAIL(); + } + catch (const TestImpact::Exception& e) + { + // Expect the message contain the specified string + EXPECT_STREQ(e.what(), Message); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST(ExceptionTest, EvalMacroSucceeds_ExceptionTestNotThrown) + { + try + { + // Given an exception instantiated with a string literal in local scope + AZ_TestImpact_Eval(true, TestImpact::Exception, Message); + + // Expect this code to be reachable + SUCCEED(); + } + catch (...) + { + // Do not expect any exceptions + FAIL(); + } + } + + TEST(ExceptionTest, Throw_ExceptionTestThrown) + { + try + { + // Given an exception instantiated with a string literal in local scope + throw(TestImpact::Exception(Message)); + } + catch (const TestImpact::Exception& e) + { + // Expect the message contain the specified string + EXPECT_STREQ(e.what(), Message); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactFrameworkPathTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactFrameworkPathTest.cpp new file mode 100644 index 0000000000..2982cb0551 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactFrameworkPathTest.cpp @@ -0,0 +1,137 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +namespace UnitTest +{ + class FrameworkPathTestFixture + : public AllocatorsTestFixture + { + public: + void SetUp() override + { + UnitTest::AllocatorsTestFixture::SetUp(); + + m_parentPathAbs = AZStd::string(AZStd::string("parent") + AZ_TRAIT_OS_PATH_SEPARATOR + "path"); + m_childPathRel = AZStd::string(AZStd::string("child") + AZ_TRAIT_OS_PATH_SEPARATOR + "path"); + m_childPathAbs = AZStd::string(m_parentPathAbs + AZ_TRAIT_OS_PATH_SEPARATOR + m_childPathRel); + + m_pathComponentA = AZStd::string("DirA"); + m_pathComponentB = AZStd::string("DirB"); + m_pathComponentC = AZStd::string("DirC"); + + m_posixPath = m_pathComponentA + AZ::IO::PosixPathSeparator + m_pathComponentB + AZ::IO::PosixPathSeparator + m_pathComponentC; + + m_windowsPath = + m_pathComponentA + AZ::IO::WindowsPathSeparator + m_pathComponentB + AZ::IO::WindowsPathSeparator + m_pathComponentC; + + m_mixedPath = + m_pathComponentA + AZ::IO::WindowsPathSeparator + m_pathComponentB + AZ::IO::PosixPathSeparator + m_pathComponentC; + + m_referredPath = + m_pathComponentA + AZ_TRAIT_OS_PATH_SEPARATOR + m_pathComponentB + AZ_TRAIT_OS_PATH_SEPARATOR + m_pathComponentC; + } + + void TearDown() override + { + UnitTest::AllocatorsTestFixture::TearDown(); + } + + AZStd::string m_parentPathAbs; + AZStd::string m_childPathRel; + AZStd::string m_childPathAbs; + + AZStd::string m_pathComponentA; + AZStd::string m_pathComponentB; + AZStd::string m_pathComponentC; + + AZ::IO::Path m_posixPath; + AZ::IO::Path m_windowsPath; + AZ::IO::Path m_mixedPath; + AZ::IO::Path m_referredPath; + }; + + TEST_F(FrameworkPathTestFixture, DefaultConstructor_HasEmptyAbsAndRelPaths) + { + // Given an empty framework path + TestImpact::FrameworkPath path; + + // Expect the absolute path to be empty + EXPECT_TRUE(path.Absolute().empty()); + + // Expect the relative path to be empty + EXPECT_TRUE(path.Relative().empty()); + } + + TEST_F(FrameworkPathTestFixture, OrphanConstructor_HasAbsAndEmptyRelPaths) + { + // Given an orhpan framework path + TestImpact::FrameworkPath path(m_parentPathAbs); + + // Expect the absolute path to be equal to the specified path + EXPECT_EQ(path.Absolute().String(), m_parentPathAbs); + + // Expect the relative path to be current directory symbol + EXPECT_STREQ(path.Relative().c_str(), "."); + } + + TEST_F(FrameworkPathTestFixture, ParentConstructor_HasAbsAndRelPaths) + { + // Given a child framework path + TestImpact::FrameworkPath path(m_childPathAbs, TestImpact::FrameworkPath(m_parentPathAbs)); + + // Expect the absolute path to be equal to the concatenation of the parent and child path + EXPECT_EQ(path.Absolute().String(), m_childPathAbs); + + // Expect the relative path to equal to the specified path + EXPECT_EQ(path.Relative().String(), m_childPathRel); + } + + TEST_F(FrameworkPathTestFixture, PosixSeperators_HasUniformPreferredSeperators) + { + // Given an orhpan framework path with Posix separators + TestImpact::FrameworkPath path(m_posixPath); + + // Expect the absolute path to be equal to the specified path with preferred separators + EXPECT_EQ(path.Absolute(), m_referredPath); + + // Expect the relative path to be current directory symbol + EXPECT_STREQ(path.Relative().c_str(), "."); + } + + TEST_F(FrameworkPathTestFixture, WindowsSeperators_HasUniformPreferredSeperators) + { + // Given an orhpan framework path with Windows separators + TestImpact::FrameworkPath path(m_windowsPath); + + // Expect the absolute path to be equal to the specified path with preferred separators + EXPECT_EQ(path.Absolute(), m_referredPath); + + // Expect the relative path to be current directory symbol + EXPECT_STREQ(path.Relative().c_str(), "."); + } + + TEST_F(FrameworkPathTestFixture, MixedSeperators_HasUniformPreferredSeperators) + { + // Given an orhpan framework path with mixed separators + TestImpact::FrameworkPath path(m_windowsPath); + + // Expect the absolute path to be equal to the specified path with preferred separators + EXPECT_EQ(path.Absolute(), m_referredPath); + + // Expect the relative path to be current directory symbol + EXPECT_STREQ(path.Relative().c_str(), "."); + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestJobRunnerCommon.h b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestJobRunnerCommon.h new file mode 100644 index 0000000000..9d5ddd6c8c --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestJobRunnerCommon.h @@ -0,0 +1,159 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include +#include + +#include +#include +#include + +template +constexpr bool IsFlagSet(Flags flags, Flags flag) +{ + return TestImpact::Bitwise::IsFlagSet(flags, flag); +} + +namespace UnitTest +{ + // Named constants for array of targets lookup + enum + { + TestTargetA, + TestTargetB, + TestTargetC, + TestTargetD + }; + + // Named constants for max concurrency values + enum + { + OneConcurrentProcess = 1, + FourConcurrentProcesses = 4 + }; + + // Validates that the specified job was executed and returned successfully + template + void ValidateJobExecutedSuccessfully(const Job& job) + { + EXPECT_EQ(job.GetResult(), TestImpact::JobResult::ExecutedWithSuccess); + EXPECT_TRUE(job.GetDuration() > AZStd::chrono::milliseconds(0)); + EXPECT_TRUE(job.GetReturnCode().has_value()); + EXPECT_EQ(job.GetReturnCode(), 0); + EXPECT_TRUE(job.GetPayload().has_value()); + } + + // Validates that the specified job has not been executed + template + void ValidateJobNotExecuted(const Job& job) + { + EXPECT_EQ(job.GetResult(), TestImpact::JobResult::NotExecuted); + EXPECT_EQ(job.GetStartTime(), AZStd::chrono::high_resolution_clock::time_point()); + EXPECT_EQ(job.GetEndTime(), AZStd::chrono::high_resolution_clock::time_point()); + EXPECT_EQ(job.GetDuration(), AZStd::chrono::milliseconds(0)); + EXPECT_FALSE(job.GetReturnCode().has_value()); + EXPECT_FALSE(job.GetPayload().has_value()); + } + + // Validates that the specified job failed to execute + template + void ValidateJobFailedToExecute(const Job& job) + { + EXPECT_EQ(job.GetResult(), TestImpact::JobResult::FailedToExecute); + EXPECT_EQ(job.GetStartTime(), AZStd::chrono::high_resolution_clock::time_point()); + EXPECT_EQ(job.GetEndTime(), AZStd::chrono::high_resolution_clock::time_point()); + EXPECT_EQ(job.GetDuration(), AZStd::chrono::milliseconds(0)); + EXPECT_FALSE(job.GetReturnCode().has_value()); + EXPECT_FALSE(job.GetPayload().has_value()); + } + + // Validates that the specified job executed but returned with error + template + void ValidateJobExecutedWithFailure(const Job& job) + { + EXPECT_EQ(job.GetResult(), TestImpact::JobResult::ExecutedWithFailure); + EXPECT_TRUE(job.GetDuration() > AZStd::chrono::milliseconds(0)); + EXPECT_TRUE(job.GetReturnCode().has_value()); + EXPECT_GT(job.GetReturnCode().value(), 0); + EXPECT_FALSE(job.GetPayload().has_value()); + } + + // Validates that the specified job was executed but was terminated by the job runner + template + void ValidateJobTimeout(const Job& job) + { + EXPECT_EQ(job.GetResult(), TestImpact::JobResult::Terminated); + EXPECT_TRUE(job.GetDuration() > AZStd::chrono::milliseconds(0)); + EXPECT_TRUE(job.GetReturnCode().has_value()); + EXPECT_EQ(job.GetReturnCode().value(), TestImpact::ProcessTimeoutErrorCode); + EXPECT_FALSE(job.GetPayload().has_value()); + } + + // Validates that the specified job executed but returned with error + template + void ValidateJobExecutedWithFailedTests(const Job& job) + { + EXPECT_EQ(job.GetResult(), TestImpact::JobResult::ExecutedWithFailure); + EXPECT_TRUE(job.GetDuration() > AZStd::chrono::milliseconds(0)); + EXPECT_TRUE(job.GetReturnCode().has_value()); + EXPECT_GT(job.GetReturnCode().value(), 0); + EXPECT_TRUE(job.GetPayload().has_value()); + } + + // Validates whether a test run completed (passed/failed) + template + void ValidateTestRunCompleted(const Job& job, TestImpact::TestRunResult result) + { + if (result == TestImpact::TestRunResult::Passed) + { + ValidateJobExecutedSuccessfully(job); + } + else + { + ValidateJobExecutedWithFailedTests(job); + } + } + + // Validates that the specified test run matches the expected output + inline void ValidateTestTargetRun(const TestImpact::TestRun& actualResult, const TestImpact::TestRun& expectedResult) + { + EXPECT_TRUE(CheckTestRunsAreEqualIgnoreDurations(actualResult, expectedResult)); + EXPECT_EQ(actualResult.GetNumTestSuites(), CalculateNumTestSuites(expectedResult.GetTestSuites())); + EXPECT_EQ(actualResult.GetNumTests(), CalculateNumTests(expectedResult.GetTestSuites())); + EXPECT_EQ(actualResult.GetNumEnabledTests(), CalculateNumEnabledTests(expectedResult.GetTestSuites())); + EXPECT_EQ(actualResult.GetNumDisabledTests(), CalculateNumDisabledTests(expectedResult.GetTestSuites())); + EXPECT_GT(actualResult.GetDuration(), AZStd::chrono::milliseconds{0}); + EXPECT_EQ(actualResult.GetNumPasses(), CalculateNumPassedTests(expectedResult.GetTestSuites())); + EXPECT_EQ(actualResult.GetNumFailures(), CalculateNumFailedTests(expectedResult.GetTestSuites())); + EXPECT_EQ(actualResult.GetNumRuns(), CalculateNumRunTests(expectedResult.GetTestSuites())); + EXPECT_EQ(actualResult.GetNumNotRuns(), CalculateNumNotRunTests(expectedResult.GetTestSuites())); + } + + // Delete any existing data in the test run folder as not to pollute tests with data from previous test runs + // Note: the file IO operations of this fixture means it cannot be sharded by the test sharder due to file race conditions + inline void DeleteFiles(const AZStd::string& path, const AZStd::string& pattern) + { + AZ::IO::SystemFile::FindFiles(AZStd::string::format("%s/%s", path.c_str(), pattern.c_str()).c_str(), [&path](const char* file, bool isFile) + { + if (isFile) + { + AZ::IO::SystemFile::Delete(AZStd::string::format("%s/%s", path.c_str(), file).c_str()); + } + + return true; + }); + }; +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestMain.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestMain.cpp new file mode 100644 index 0000000000..632e84ad9f --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestMain.cpp @@ -0,0 +1,33 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +class TestImpactTestEnvironment : public AZ::Test::ITestEnvironment +{ +protected: + void SetupEnvironment() override + { + AZ::AllocatorInstance::Create(); + AZ::AllocatorInstance::Create(); + } + + void TeardownEnvironment() override + { + AZ::AllocatorInstance::Destroy(); + AZ::AllocatorInstance::Destroy(); + } +}; + +AZ_UNIT_TEST_HOOK(new TestImpactTestEnvironment); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.cpp new file mode 100644 index 0000000000..90f384de85 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.cpp @@ -0,0 +1,1843 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include + +#include +#include + +namespace UnitTest +{ + void WriteTextToFile(const AZStd::string& text, const AZ::IO::Path& path) + { + const AZStd::vector textBytes(text.begin(), text.end()); + AZ::IO::SystemFile textFile; + AZ_TestImpact_Eval( + textFile.Open( + path.c_str(), + AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY), + TestImpact::Exception, "Couldn't open cache file for writing"); + const auto bytesWritten = textFile.Write(textBytes.data(), textBytes.size()); + AZ_TestImpact_Eval(bytesWritten == textBytes.size(), TestImpact::Exception, "Couldn't write text file data"); + } + + AZStd::string ConstructTestProcessArgs(TestImpact::ProcessId pid, AZStd::chrono::milliseconds sleepTime) + { + return AZStd::string::format("--id %i --sleep %u", pid, sleepTime.count()); + } + + AZStd::string ConstructTestProcessArgsLargeText(TestImpact::ProcessId pid, AZStd::chrono::milliseconds sleepTime) + { + return ConstructTestProcessArgs(pid, sleepTime) + " large"; + } + + AZStd::string KnownTestProcessOutputString(TestImpact::ProcessId pid) + { + return AZStd::string("TestProcessMainStdOut" + AZStd::to_string(pid)); + } + + AZStd::string KnownTestProcessErrorString(TestImpact::ProcessId pid) + { + return AZStd::string("TestProcessMainStdErr" + AZStd::to_string(pid)); + } + + AZStd::string GenerateSuiteOrFixtureName(const AZStd::string& name) + { + return name; + } + + AZStd::string GenerateTypedFixtureName(const AZStd::string& name, size_t typeNum) + { + return AZStd::string::format("%s/%u", name.c_str(), typeNum); + } + + AZStd::string GenerateParameterizedFixtureName(const AZStd::string& name, const AZStd::optional& prefix) + { + if (prefix.has_value()) + { + return AZStd::string::format("%s/%s", prefix->c_str(), name.c_str()); + } + + return AZStd::string::format("%s", name.c_str()); + } + + AZStd::string GenerateParameterizedTestName(const AZStd::string& name, size_t testNum) + { + return AZStd::string::format("%s/%u", name.c_str(), testNum); + } + + AZStd::string StringVectorToJSONElements(const AZStd::vector strings) + { + AZStd::string output; + + for (size_t i = 0, last = strings.size() - 1; i < strings.size(); i++) + { + output += AZStd::string::format("\"%s\"", strings[i].c_str()); + + if (i != last) + { + output += ","; + } + + output += "\n"; + } + + return output; + } + + AZStd::string GenerateBuildTargetDescriptorString( + const AZStd::string& name, + const AZStd::string& outputName, + const AZStd::string& path, + const AZStd::vector& staticSources, + const AZStd::vector& autogenInputs, + const AZStd::vector& autogenOutputs) + { + constexpr const char* const targetTemplate = + "{\n" + " \"sources\": {\n" + " \"input\": [\n" + "%s\n" + " ],\n" + " \"output\": [\n" + "%s\n" + " ],\n" + " \"static\": [\n" + "%s\n" + " ]\n" + " },\n" + " \"target\": {\n" + " \"name\": \"%s\",\n" + " \"output_name\": \"%s\",\n" + " \"path\": \"%s\"\n" + " }\n" + "}\n" + "\n"; + + AZStd::string output = AZStd::string::format(targetTemplate, + StringVectorToJSONElements(autogenInputs).c_str(), + StringVectorToJSONElements(autogenOutputs).c_str(), + StringVectorToJSONElements(staticSources).c_str(), + name.c_str(), + outputName.c_str(), + path.c_str()); + + return output; + } + + TestImpact::BuildTargetDescriptor GenerateBuildTargetDescriptor( + const AZStd::string& name, + const AZStd::string& outputName, + const AZStd::string& path, + const AZStd::vector& staticSources, + const TestImpact::AutogenSources& autogenSources) + { + TestImpact::BuildTargetDescriptor targetDescriptor; + targetDescriptor.m_buildMetaData = {name, outputName, path}; + targetDescriptor.m_sources = {staticSources, autogenSources}; + return targetDescriptor; + } + + TestImpact::TestEnumerationSuite GenerateParamterizedSuite( + const AZStd::pair& fixture, + const AZStd::optional& permutation, + const AZStd::vector> tests, + size_t permutationCount) + { + TestImpact::TestEnumerationSuite suite = + TestImpact::TestEnumerationSuite{GenerateParameterizedFixtureName(fixture.first, permutation), fixture.second, {}}; + + for (const auto& test : tests) + { + for (auto i = 0; i < permutationCount; i++) + { + suite.m_tests.push_back(TestImpact::TestEnumerationCase{GenerateParameterizedTestName(test.first, i), test.second}); + } + } + + return suite; + } + + void GenerateTypedSuite( + const AZStd::pair& fixture, + const AZStd::vector> tests, + size_t permutationCount, + AZStd::vector& parentSuiteList) + { + for (auto i = 0; i < permutationCount; i++) + { + parentSuiteList.push_back(TestImpact::TestEnumerationSuite{GenerateTypedFixtureName(fixture.first, i), fixture.second, {}}); + + for (const auto& test : tests) + { + parentSuiteList.back().m_tests.push_back(TestImpact::TestEnumerationCase{test.first, test.second}); + } + } + } + + size_t CalculateNumPassedTests(const AZStd::vector& suites) + { + size_t numPassedTests = 0; + for (const auto& suite : suites) + { + numPassedTests += AZStd::count_if(suite.m_tests.begin(), suite.m_tests.end(), [](const auto& test) { + return test.m_result.has_value() && test.m_result.value() == TestImpact::TestRunResult::Passed; + }); + } + + return numPassedTests; + } + + size_t CalculateNumFailedTests(const AZStd::vector& suites) + { + size_t numFailedTests = 0; + for (const auto& suite : suites) + { + for (const auto& test : suite.m_tests) + { + if (test.m_result.has_value() && test.m_result.value() == TestImpact::TestRunResult::Failed) + { + numFailedTests++; + } + } + } + + return numFailedTests; + } + + size_t CalculateNumRunTests(const AZStd::vector& suites) + { + size_t numRunTests = 0; + for (const auto& suite : suites) + { + for (const auto& test : suite.m_tests) + { + if (test.m_status == TestImpact::TestRunStatus::Run) + { + numRunTests++; + } + } + } + + return numRunTests; + } + + size_t CalculateNumNotRunTests(const AZStd::vector& suites) + { + size_t numNotRunTests = 0; + for (const auto& suite : suites) + { + for (const auto& test : suite.m_tests) + { + if (test.m_status == TestImpact::TestRunStatus::NotRun) + { + numNotRunTests++; + } + } + } + + return numNotRunTests; + } + + AZStd::vector GetTestTargetATestEnumerationSuites() + { + AZStd::vector suites; + + suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestCase"), true, {}}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test2_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test3_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test4_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test5_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test6_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test7_WillFail", true}); + + suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestFixture"), true, {}}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test2_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test3_WillPass", true}); + + return suites; + } + + AZStd::vector GetTestTargetBTestEnumerationSuites() + { + AZStd::vector suites; + + suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestCase"), true, {}}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test2_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test3_WillPass", true}); + + suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestFixture"), true, {}}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); + + const size_t numParams = 27; + suites.push_back(GenerateParamterizedSuite( + {"TestFixtureWithParams", true}, "PermutationA", {{"Test1_WillPass", true}, {"Test2_WillPass", true}}, numParams)); + suites.push_back(GenerateParamterizedSuite( + {"TestFixtureWithParams", true}, AZStd::nullopt, {{"Test1_WillPass", true}, {"Test2_WillPass", true}}, numParams)); + + return suites; + } + + AZStd::vector GetTestTargetCTestEnumerationSuites() + { + AZStd::vector suites; + + suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestFixture"), true, {}}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test2_WillPass", true}); + + const size_t numTypes = 4; + for (auto i = 0; i < numTypes; i++) + { + suites.push_back(TestImpact::TestEnumerationSuite{GenerateTypedFixtureName("TestFixtureWithTypes", i), true, {}}); + for (auto j = 1; j < numTypes + 1; j++) + { + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{AZStd::string::format("Test%u_WillPass", j), true}); + } + } + + return suites; + } + + AZStd::vector GetTestTargetDTestEnumerationSuites() + { + AZStd::vector suites; + + suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestCase"), true, {}}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"DISABLED_Test2_WillPass", false}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test3_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test4_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test5_WillPass", true}); + + suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestFixture1"), true, {}}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test2_WillPass", true}); + + suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("DISABLED_TestFixture2"), false, {}}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); + suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test2_WillPass", true}); + + const size_t numTypes = 4; + GenerateTypedSuite( + {"TestFixtureWithTypes1", true}, {{"Test1_WillPass", true}, {"DISABLED_Test2_WillPass", false}, {"Test3_WillPass", true}}, + numTypes, suites); + + GenerateTypedSuite( + {"DISABLED_TestFixtureWithTypes2", false}, + {{"Test1_WillPass", true}, {"DISABLED_Test2_WillPass", false}, {"Test3_WillPass", true}}, numTypes, suites); + + const size_t numParams = 27; + suites.push_back(GenerateParamterizedSuite( + {"TestFixtureWithParams1", true}, "PermutationA", {{"Test1_WillPass", true}, {"DISABLED_Test2_WillPass", false}}, numParams)); + suites.push_back(GenerateParamterizedSuite( + {"TestFixtureWithParams1", true}, AZStd::nullopt, {{"Test1_WillPass", true}, {"DISABLED_Test2_WillPass", false}}, numParams)); + suites.push_back(GenerateParamterizedSuite( + {"DISABLED_TestFixtureWithParams2", false}, "PermutationA", {{"Test1_WillPass", true}, {"DISABLED_Test2_WillPass", false}}, + numParams)); + suites.push_back(GenerateParamterizedSuite( + {"DISABLED_TestFixtureWithParams2", false}, AZStd::nullopt, {{"Test1_WillPass", true}, {"DISABLED_Test2_WillPass", false}}, + numParams)); + + return suites; + } + + TestImpact::TestRunCase TestRunCaseFromTestEnumerationCase(const TestImpact::TestEnumerationCase& enumCase) + { + TestImpact::TestRunCase runCase; + runCase.m_name = enumCase.m_name; + runCase.m_enabled = enumCase.m_enabled; + return runCase; + } + + TestImpact::TestRunSuite TestRunSuiteFromTestEnumerationSuite(const TestImpact::TestEnumerationSuite& enumSuite) + { + TestImpact::TestRunSuite runSuite; + runSuite.m_name = enumSuite.m_name; + runSuite.m_enabled = enumSuite.m_enabled; + + for (const auto& enumCase : enumSuite.m_tests) + { + runSuite.m_tests.push_back(TestRunCaseFromTestEnumerationCase(enumCase)); + } + + return runSuite; + } + + AZStd::vector TestRunSuitesFromTestEnumerationSuites( + const AZStd::vector& enumSuites) + { + AZStd::vector runSuites; + runSuites.reserve(enumSuites.size()); + + for (const auto& enumSuite : enumSuites) + { + runSuites.push_back(TestRunSuiteFromTestEnumerationSuite(enumSuite)); + } + + return runSuites; + } + + void SetTestRunSuiteData(TestImpact::TestRunSuite& testSuite, AZStd::chrono::milliseconds duration) + { + testSuite.m_duration = duration; + } + + void SetTestRunCaseData( + TestImpact::TestRunCase& testCase, AZStd::chrono::milliseconds duration, TestImpact::TestRunStatus status, + AZStd::optional result = AZStd::nullopt) + { + testCase.m_duration = duration; + testCase.m_status = status; + testCase.m_result = result; + } + + AZStd::vector GetTestTargetATestRunSuites() + { + AZStd::vector suites(TestRunSuitesFromTestEnumerationSuites(GetTestTargetATestEnumerationSuites())); + + enum + { + TestCaseIndex = 0, + TestFixtureIndex + }; + + { + auto& suite = suites[TestCaseIndex]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{3}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[4], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[5], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[6], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Failed); + } + { + auto& suite = suites[TestFixtureIndex]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{38}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{4}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + + return suites; + } + + AZStd::vector GetTestTargetBTestRunSuites() + { + AZStd::vector suites(TestRunSuitesFromTestEnumerationSuites(GetTestTargetBTestEnumerationSuites())); + + enum + { + TestCaseIndex = 0, + TestFixtureIndex, + PermutationATestFixtureWithParamsIndex, + TestFixtureWithParamsIndex + }; + + { + auto& suite = suites[TestCaseIndex]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{202}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{3}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[TestFixtureIndex]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{62}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{5}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[PermutationATestFixtureWithParamsIndex]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{3203}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[4], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[5], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[6], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[7], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[8], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[9], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[10], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[11], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[12], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[13], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[14], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[15], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[16], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[17], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[18], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[19], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[20], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[21], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[22], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[23], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[24], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[25], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[26], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[27], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[28], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[29], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[30], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[31], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[32], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[33], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[34], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[35], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[36], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[37], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[38], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[39], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[40], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[41], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[42], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[43], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[44], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[45], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[46], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[47], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[48], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[49], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[50], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[51], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[52], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[53], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[TestFixtureWithParamsIndex]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{3360}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[4], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[5], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[6], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[7], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[8], AZStd::chrono::milliseconds{2}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[9], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[10], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[11], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[12], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[13], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[14], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[15], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[16], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[17], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[18], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[19], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[20], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[21], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[22], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[23], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[24], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[25], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[26], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[27], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[28], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[29], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[30], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[31], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[32], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[33], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[34], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[35], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[36], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[37], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[38], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[39], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[40], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[41], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[42], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[43], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[44], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[45], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[46], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[47], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[48], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[49], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[50], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[51], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[52], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[53], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + + return suites; + } + + AZStd::vector GetTestTargetCTestRunSuites() + { + AZStd::vector suites(TestRunSuitesFromTestEnumerationSuites(GetTestTargetCTestEnumerationSuites())); + + enum + { + TestCaseIndex = 0, + TestFixtureWithTypes0Index, + TestFixtureWithTypes1Index, + TestFixtureWithTypes2Index, + TestFixtureWithTypes3Index + }; + + { + auto& suite = suites[TestCaseIndex]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{125}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{4}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[TestFixtureWithTypes0Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{210}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[3], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[TestFixtureWithTypes1Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{208}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[TestFixtureWithTypes2Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{199}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[TestFixtureWithTypes3Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{49}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + + return suites; + } + + AZStd::vector GetTestTargetDTestRunSuites() + { + AZStd::vector suites(TestRunSuitesFromTestEnumerationSuites(GetTestTargetDTestEnumerationSuites())); + + enum + { + TestCaseIndex = 0, + TestFixture1Index, + DISABLEDTestFixture2Index, + TestFixtureWithTypes10Index, + TestFixtureWithTypes11Index, + TestFixtureWithTypes12Index, + TestFixtureWithTypes13Index, + DISABLEDTestFixtureWithTypes20Index, + DISABLEDTestFixtureWithTypes21Index, + DISABLEDTestFixtureWithTypes22Index, + DISABLEDTestFixtureWithTypes23Index, + PermutationATestFixtureWithParams1Index, + TestFixtureWithParams1Index, + PermutationADISABLED_TestFixtureWithParams2Index, + DISABLED_TestFixtureWithParams2Index, + }; + + { + auto& suite = suites[TestCaseIndex]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{3}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[4], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[TestFixture1Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{4}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{2}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[DISABLEDTestFixture2Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{0}); + SetTestRunCaseData(suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + } + { + auto& suite = suites[TestFixtureWithTypes10Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{1}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[TestFixtureWithTypes11Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{3}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[TestFixtureWithTypes12Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{0}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[TestFixtureWithTypes13Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{1}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + } + { + auto& suite = suites[DISABLEDTestFixtureWithTypes20Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{0}); + SetTestRunCaseData(suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + } + { + auto& suite = suites[DISABLEDTestFixtureWithTypes21Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{0}); + SetTestRunCaseData(suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + } + { + auto& suite = suites[DISABLEDTestFixtureWithTypes22Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{0}); + SetTestRunCaseData(suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + } + { + auto& suite = suites[DISABLEDTestFixtureWithTypes23Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{0}); + SetTestRunCaseData(suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + } + { + auto& suite = suites[PermutationATestFixtureWithParams1Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{173}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[4], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[5], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[6], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[7], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[8], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[9], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[10], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[11], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[12], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[13], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[14], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[15], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[16], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[17], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[18], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[19], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[20], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[21], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[22], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[23], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[24], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[25], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[26], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData(suite.m_tests[27], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[28], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[29], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[30], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[31], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[32], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[33], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[34], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[35], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[36], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[37], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[38], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[39], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[40], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[41], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[42], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[43], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[44], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[45], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[46], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[47], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[48], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[49], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[50], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[51], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[52], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[53], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + } + { + auto& suite = suites[TestFixtureWithParams1Index]; + SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{102}); + SetTestRunCaseData( + suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[4], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[5], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[6], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[7], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[8], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[9], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[10], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[11], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[12], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[13], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[14], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[15], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[16], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[17], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[18], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[19], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[20], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[21], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[22], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[23], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[24], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[25], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData( + suite.m_tests[26], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); + SetTestRunCaseData(suite.m_tests[27], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[28], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[29], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[30], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[31], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[32], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[33], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[34], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[35], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[36], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[37], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[38], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[39], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[40], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[41], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[42], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[43], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[44], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[45], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[46], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[47], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[48], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[49], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[50], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[51], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[52], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + SetTestRunCaseData(suite.m_tests[53], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); + } + // The other two fixtures are all disabled (not run) + + return suites; + } + + AZStd::vector GetTestTargetASourceModuleCoverages() + { + AZStd::vector moduleCoverages; + + TestImpact::ModuleCoverage moduleCoverage; + moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetA.Tests.dll"; + + TestImpact::SourceCoverage sourceCoverage; + sourceCoverage.m_path = + "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetA\\Code\\Tests\\TestImpactTestTargetA.cpp"; + + moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); + moduleCoverages.push_back(AZStd::move(moduleCoverage)); + return moduleCoverages; + } + + AZStd::vector GetTestTargetBSourceModuleCoverages() + { + AZStd::vector moduleCoverages; + + TestImpact::ModuleCoverage moduleCoverage; + moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetB.Tests.dll"; + + TestImpact::SourceCoverage sourceCoverage; + sourceCoverage.m_path = + "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetB\\Code\\Tests\\TestImpactTestTargetB.cpp"; + + moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); + moduleCoverages.push_back(AZStd::move(moduleCoverage)); + return moduleCoverages; + } + + AZStd::vector GetTestTargetCSourceModuleCoverages() + { + AZStd::vector moduleCoverages; + + TestImpact::ModuleCoverage moduleCoverage; + moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetC.Tests.dll"; + + TestImpact::SourceCoverage sourceCoverage; + sourceCoverage.m_path = + "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp"; + + moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); + moduleCoverages.push_back(AZStd::move(moduleCoverage)); + return moduleCoverages; + } + + AZStd::vector GetTestTargetDSourceModuleCoverages() + { + AZStd::vector moduleCoverages; + + TestImpact::ModuleCoverage moduleCoverage; + moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetD.Tests.dll"; + + TestImpact::SourceCoverage sourceCoverage; + sourceCoverage.m_path = + "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp"; + + moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); + moduleCoverages.push_back(AZStd::move(moduleCoverage)); + return moduleCoverages; + } + + AZStd::vector GetTestTargetALineModuleCoverages() + { + AZStd::vector moduleCoverages; + + TestImpact::ModuleCoverage moduleCoverage; + moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetA.Tests.dll"; + + TestImpact::SourceCoverage sourceCoverage; + sourceCoverage.m_path = + "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetA\\Code\\Tests\\TestImpactTestTargetA.cpp"; + sourceCoverage.m_coverage = AZStd::vector(); + + auto& lines = sourceCoverage.m_coverage.value(); + lines.push_back(TestImpact::LineCoverage{22, 1}); + lines.push_back(TestImpact::LineCoverage{23, 1}); + lines.push_back(TestImpact::LineCoverage{24, 1}); + lines.push_back(TestImpact::LineCoverage{25, 1}); + lines.push_back(TestImpact::LineCoverage{27, 1}); + lines.push_back(TestImpact::LineCoverage{28, 1}); + lines.push_back(TestImpact::LineCoverage{29, 1}); + lines.push_back(TestImpact::LineCoverage{30, 1}); + lines.push_back(TestImpact::LineCoverage{32, 1}); + lines.push_back(TestImpact::LineCoverage{33, 1}); + lines.push_back(TestImpact::LineCoverage{34, 1}); + lines.push_back(TestImpact::LineCoverage{35, 1}); + lines.push_back(TestImpact::LineCoverage{37, 1}); + lines.push_back(TestImpact::LineCoverage{38, 1}); + lines.push_back(TestImpact::LineCoverage{39, 1}); + lines.push_back(TestImpact::LineCoverage{40, 1}); + lines.push_back(TestImpact::LineCoverage{42, 1}); + lines.push_back(TestImpact::LineCoverage{43, 1}); + lines.push_back(TestImpact::LineCoverage{44, 1}); + lines.push_back(TestImpact::LineCoverage{45, 1}); + lines.push_back(TestImpact::LineCoverage{47, 1}); + lines.push_back(TestImpact::LineCoverage{48, 1}); + lines.push_back(TestImpact::LineCoverage{49, 1}); + lines.push_back(TestImpact::LineCoverage{50, 1}); + lines.push_back(TestImpact::LineCoverage{52, 1}); + lines.push_back(TestImpact::LineCoverage{53, 1}); + lines.push_back(TestImpact::LineCoverage{54, 1}); + lines.push_back(TestImpact::LineCoverage{55, 1}); + lines.push_back(TestImpact::LineCoverage{57, 1}); + lines.push_back(TestImpact::LineCoverage{58, 1}); + lines.push_back(TestImpact::LineCoverage{59, 1}); + lines.push_back(TestImpact::LineCoverage{60, 1}); + lines.push_back(TestImpact::LineCoverage{62, 1}); + lines.push_back(TestImpact::LineCoverage{63, 1}); + lines.push_back(TestImpact::LineCoverage{64, 1}); + lines.push_back(TestImpact::LineCoverage{65, 1}); + lines.push_back(TestImpact::LineCoverage{67, 1}); + lines.push_back(TestImpact::LineCoverage{68, 1}); + lines.push_back(TestImpact::LineCoverage{69, 1}); + lines.push_back(TestImpact::LineCoverage{70, 1}); + lines.push_back(TestImpact::LineCoverage{73, 1}); + + moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); + moduleCoverages.push_back(AZStd::move(moduleCoverage)); + return moduleCoverages; + } + + AZStd::vector GetTestTargetBLineModuleCoverages() + { + AZStd::vector moduleCoverages; + + TestImpact::ModuleCoverage moduleCoverage; + moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetB.Tests.dll"; + + TestImpact::SourceCoverage sourceCoverage; + sourceCoverage.m_path = + "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetB\\Code\\Tests\\TestImpactTestTargetB.cpp"; + sourceCoverage.m_coverage = AZStd::vector(); + + auto& lines = sourceCoverage.m_coverage.value(); + lines.push_back(TestImpact::LineCoverage{29, 1}); + lines.push_back(TestImpact::LineCoverage{30, 1}); + lines.push_back(TestImpact::LineCoverage{31, 1}); + lines.push_back(TestImpact::LineCoverage{32, 1}); + lines.push_back(TestImpact::LineCoverage{34, 1}); + lines.push_back(TestImpact::LineCoverage{35, 1}); + lines.push_back(TestImpact::LineCoverage{36, 1}); + lines.push_back(TestImpact::LineCoverage{37, 1}); + lines.push_back(TestImpact::LineCoverage{39, 1}); + lines.push_back(TestImpact::LineCoverage{40, 1}); + lines.push_back(TestImpact::LineCoverage{41, 1}); + lines.push_back(TestImpact::LineCoverage{42, 1}); + lines.push_back(TestImpact::LineCoverage{44, 1}); + lines.push_back(TestImpact::LineCoverage{45, 1}); + lines.push_back(TestImpact::LineCoverage{46, 1}); + lines.push_back(TestImpact::LineCoverage{47, 1}); + lines.push_back(TestImpact::LineCoverage{49, 1}); + lines.push_back(TestImpact::LineCoverage{50, 1}); + lines.push_back(TestImpact::LineCoverage{51, 1}); + lines.push_back(TestImpact::LineCoverage{52, 1}); + lines.push_back(TestImpact::LineCoverage{54, 1}); + lines.push_back(TestImpact::LineCoverage{55, 1}); + lines.push_back(TestImpact::LineCoverage{56, 1}); + lines.push_back(TestImpact::LineCoverage{57, 1}); + lines.push_back(TestImpact::LineCoverage{59, 1}); + lines.push_back(TestImpact::LineCoverage{66, 1}); + lines.push_back(TestImpact::LineCoverage{68, 1}); + lines.push_back(TestImpact::LineCoverage{75, 1}); + lines.push_back(TestImpact::LineCoverage{78, 1}); + + moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); + moduleCoverages.push_back(AZStd::move(moduleCoverage)); + return moduleCoverages; + } + + AZStd::vector GetTestTargetCLineModuleCoverages() + { + AZStd::vector moduleCoverages; + + TestImpact::ModuleCoverage moduleCoverage; + moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetC.Tests.dll"; + + TestImpact::SourceCoverage sourceCoverage; + sourceCoverage.m_path = + "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp"; + sourceCoverage.m_coverage = AZStd::vector(); + + auto& lines = sourceCoverage.m_coverage.value(); + lines.push_back(TestImpact::LineCoverage{32, 1}); + lines.push_back(TestImpact::LineCoverage{33, 1}); + lines.push_back(TestImpact::LineCoverage{34, 1}); + lines.push_back(TestImpact::LineCoverage{35, 1}); + lines.push_back(TestImpact::LineCoverage{37, 1}); + lines.push_back(TestImpact::LineCoverage{38, 1}); + lines.push_back(TestImpact::LineCoverage{39, 1}); + lines.push_back(TestImpact::LineCoverage{40, 1}); + lines.push_back(TestImpact::LineCoverage{42, 1}); + lines.push_back(TestImpact::LineCoverage{43, 1}); + lines.push_back(TestImpact::LineCoverage{44, 1}); + lines.push_back(TestImpact::LineCoverage{45, 1}); + lines.push_back(TestImpact::LineCoverage{47, 1}); + lines.push_back(TestImpact::LineCoverage{48, 1}); + lines.push_back(TestImpact::LineCoverage{49, 1}); + lines.push_back(TestImpact::LineCoverage{50, 1}); + lines.push_back(TestImpact::LineCoverage{52, 1}); + lines.push_back(TestImpact::LineCoverage{53, 1}); + lines.push_back(TestImpact::LineCoverage{54, 1}); + lines.push_back(TestImpact::LineCoverage{55, 1}); + lines.push_back(TestImpact::LineCoverage{57, 1}); + lines.push_back(TestImpact::LineCoverage{58, 1}); + lines.push_back(TestImpact::LineCoverage{59, 1}); + lines.push_back(TestImpact::LineCoverage{60, 1}); + lines.push_back(TestImpact::LineCoverage{63, 1}); + + moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); + moduleCoverages.push_back(AZStd::move(moduleCoverage)); + return moduleCoverages; + } + + AZStd::vector GetTestTargetDLineModuleCoverages() + { + AZStd::vector moduleCoverages; + + TestImpact::ModuleCoverage moduleCoverage; + moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetD.Tests.dll"; + + TestImpact::SourceCoverage sourceCoverage; + sourceCoverage.m_path = + "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp"; + sourceCoverage.m_coverage = AZStd::vector(); + + auto& lines = sourceCoverage.m_coverage.value(); + lines.push_back(TestImpact::LineCoverage{56, 1}); + lines.push_back(TestImpact::LineCoverage{57, 1}); + lines.push_back(TestImpact::LineCoverage{58, 1}); + lines.push_back(TestImpact::LineCoverage{59, 1}); + lines.push_back(TestImpact::LineCoverage{61, 1}); + lines.push_back(TestImpact::LineCoverage{62, 0}); + lines.push_back(TestImpact::LineCoverage{63, 0}); + lines.push_back(TestImpact::LineCoverage{64, 0}); + lines.push_back(TestImpact::LineCoverage{66, 1}); + lines.push_back(TestImpact::LineCoverage{67, 1}); + lines.push_back(TestImpact::LineCoverage{68, 1}); + lines.push_back(TestImpact::LineCoverage{69, 1}); + lines.push_back(TestImpact::LineCoverage{71, 1}); + lines.push_back(TestImpact::LineCoverage{72, 1}); + lines.push_back(TestImpact::LineCoverage{73, 1}); + lines.push_back(TestImpact::LineCoverage{74, 1}); + lines.push_back(TestImpact::LineCoverage{76, 1}); + lines.push_back(TestImpact::LineCoverage{77, 1}); + lines.push_back(TestImpact::LineCoverage{78, 1}); + lines.push_back(TestImpact::LineCoverage{79, 1}); + lines.push_back(TestImpact::LineCoverage{81, 1}); + lines.push_back(TestImpact::LineCoverage{82, 1}); + lines.push_back(TestImpact::LineCoverage{83, 1}); + lines.push_back(TestImpact::LineCoverage{84, 1}); + lines.push_back(TestImpact::LineCoverage{86, 1}); + lines.push_back(TestImpact::LineCoverage{87, 1}); + lines.push_back(TestImpact::LineCoverage{88, 1}); + lines.push_back(TestImpact::LineCoverage{89, 1}); + lines.push_back(TestImpact::LineCoverage{91, 1}); + lines.push_back(TestImpact::LineCoverage{92, 0}); + lines.push_back(TestImpact::LineCoverage{93, 0}); + lines.push_back(TestImpact::LineCoverage{94, 0}); + lines.push_back(TestImpact::LineCoverage{96, 1}); + lines.push_back(TestImpact::LineCoverage{97, 0}); + lines.push_back(TestImpact::LineCoverage{98, 0}); + lines.push_back(TestImpact::LineCoverage{99, 0}); + lines.push_back(TestImpact::LineCoverage{101, 1}); + lines.push_back(TestImpact::LineCoverage{102, 1}); + lines.push_back(TestImpact::LineCoverage{103, 1}); + lines.push_back(TestImpact::LineCoverage{104, 1}); + lines.push_back(TestImpact::LineCoverage{106, 1}); + lines.push_back(TestImpact::LineCoverage{107, 0}); + lines.push_back(TestImpact::LineCoverage{108, 0}); + lines.push_back(TestImpact::LineCoverage{109, 0}); + lines.push_back(TestImpact::LineCoverage{111, 1}); + lines.push_back(TestImpact::LineCoverage{112, 0}); + lines.push_back(TestImpact::LineCoverage{113, 0}); + lines.push_back(TestImpact::LineCoverage{114, 0}); + lines.push_back(TestImpact::LineCoverage{116, 1}); + lines.push_back(TestImpact::LineCoverage{117, 0}); + lines.push_back(TestImpact::LineCoverage{118, 0}); + lines.push_back(TestImpact::LineCoverage{119, 0}); + lines.push_back(TestImpact::LineCoverage{121, 1}); + lines.push_back(TestImpact::LineCoverage{128, 1}); + lines.push_back(TestImpact::LineCoverage{130, 1}); + lines.push_back(TestImpact::LineCoverage{137, 1}); + lines.push_back(TestImpact::LineCoverage{139, 1}); + lines.push_back(TestImpact::LineCoverage{146, 1}); + lines.push_back(TestImpact::LineCoverage{148, 1}); + lines.push_back(TestImpact::LineCoverage{155, 1}); + lines.push_back(TestImpact::LineCoverage{157, 1}); + lines.push_back(TestImpact::LineCoverage{158, 1}); + lines.push_back(TestImpact::LineCoverage{159, 1}); + lines.push_back(TestImpact::LineCoverage{160, 1}); + lines.push_back(TestImpact::LineCoverage{162, 1}); + lines.push_back(TestImpact::LineCoverage{163, 0}); + lines.push_back(TestImpact::LineCoverage{164, 0}); + lines.push_back(TestImpact::LineCoverage{165, 0}); + lines.push_back(TestImpact::LineCoverage{167, 1}); + lines.push_back(TestImpact::LineCoverage{168, 1}); + lines.push_back(TestImpact::LineCoverage{169, 1}); + lines.push_back(TestImpact::LineCoverage{170, 1}); + lines.push_back(TestImpact::LineCoverage{172, 1}); + lines.push_back(TestImpact::LineCoverage{173, 0}); + lines.push_back(TestImpact::LineCoverage{174, 0}); + lines.push_back(TestImpact::LineCoverage{175, 0}); + lines.push_back(TestImpact::LineCoverage{177, 1}); + lines.push_back(TestImpact::LineCoverage{178, 0}); + lines.push_back(TestImpact::LineCoverage{179, 0}); + lines.push_back(TestImpact::LineCoverage{180, 0}); + lines.push_back(TestImpact::LineCoverage{182, 1}); + lines.push_back(TestImpact::LineCoverage{183, 0}); + lines.push_back(TestImpact::LineCoverage{184, 0}); + lines.push_back(TestImpact::LineCoverage{185, 0}); + lines.push_back(TestImpact::LineCoverage{188, 1}); + + moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); + moduleCoverages.push_back(AZStd::move(moduleCoverage)); + return moduleCoverages; + } + + template + bool CheckTestCasesAreEqual(const TestCase& lhs, const TestCase& rhs) + { + if (lhs.m_enabled != rhs.m_enabled) + { + AZ_Error("CheckTestCasesAreEqual", false, "lhs.m_enabled: %u, rhs.m_enabled: %u", lhs.m_enabled, rhs.m_enabled); + return false; + } + + if (lhs.m_name != rhs.m_name) + { + AZ_Error("CheckTestCasesAreEqual", false, "lhs.m_name: %s, rhs.m_name: %s", lhs.m_name.c_str(), rhs.m_name.c_str()); + return false; + } + + return true; + } + + template + bool CheckTestSuitesAreEqual(const TestSuite& lhs, const TestSuite& rhs) + { + if (lhs.m_enabled != rhs.m_enabled) + { + AZ_Error("CheckTestSuitesAreEqual", false, "lhs.m_enabled: %u, rhs.m_enabled: %u", lhs.m_enabled, rhs.m_enabled); + return false; + } + + if (lhs.m_name != rhs.m_name) + { + AZ_Error("CheckTestSuitesAreEqual", false, "lhs.m_name: %s, rhs.m_name: %s", lhs.m_name.c_str(), rhs.m_name.c_str()); + return false; + } + + return AZStd::equal(lhs.m_tests.begin(), lhs.m_tests.end(), rhs.m_tests.begin(), [](const auto& left, const auto& right) { + return left == right; + }); + } + + template + bool CheckTestSuiteVectorsAreEqual(const AZStd::vector& lhs, const AZStd::vector& rhs) + { + return AZStd::equal(lhs.begin(), lhs.end(), rhs.begin(), [](const TestSuite& left, const TestSuite& right) { + return left == right; + }); + } + + template + bool CheckTestContainersAreEqual(const TestContainer& lhs, const TestContainer& rhs) + { + if (lhs.GetTestSuites().size() != rhs.GetTestSuites().size()) + { + return false; + } + + return AZStd::equal( + lhs.GetTestSuites().begin(), lhs.GetTestSuites().end(), rhs.GetTestSuites().begin(), [](const auto& left, const auto& right) { + return left == right; + }); + } + + bool operator==(const TestImpact::TestEnumerationCase& lhs, const TestImpact::TestEnumerationCase& rhs) + { + return CheckTestCasesAreEqual(lhs, rhs); + } + + bool operator==(const TestImpact::TestRunCase& lhs, const TestImpact::TestRunCase& rhs) + { + if (!CheckTestCasesAreEqual(lhs, rhs)) + { + return false; + } + + if (lhs.m_status != rhs.m_status) + { + AZ_Error( + "TestRunCase ==", false, "lhs.m_status: %u, rhs.m_status: %u", static_cast(lhs.m_status), + static_cast(rhs.m_status)); + return false; + } + + if (lhs.m_duration != rhs.m_duration) + { + AZ_Error("TestRunCase ==", false, "lhs.m_duration: %u, rhs.m_duration: %u", lhs.m_duration.count(), rhs.m_duration.count()); + return false; + } + + if (lhs.m_result != rhs.m_result) + { + if (!lhs.m_result.has_value() && rhs.m_result.has_value()) + { + AZ_Error("TestRunCase ==", false, "lhs.m_result: null, rhs.m_result: %u", static_cast(rhs.m_result.value())); + } + else if (lhs.m_result.has_value() && !rhs.m_result.has_value()) + { + AZ_Error("TestRunCase ==", false, "lhs.m_result: %u, rhs.m_result: null", static_cast(lhs.m_result.value())); + } + else + { + AZ_Error( + "TestRunCase ==", false, "lhs.m_result: %u, rhs.m_result: %u", static_cast(lhs.m_result.value()), + static_cast(rhs.m_result.value())); + } + + return false; + } + + return true; + } + + bool operator==(const TestImpact::TestEnumerationSuite& lhs, const TestImpact::TestEnumerationSuite& rhs) + { + return CheckTestSuitesAreEqual(lhs, rhs); + } + + bool operator==(const TestImpact::TestRunSuite& lhs, const TestImpact::TestRunSuite& rhs) + { + if (!CheckTestSuitesAreEqual(lhs, rhs)) + { + return false; + } + + if (lhs.m_duration != rhs.m_duration) + { + AZ_Error( + "TestEnumerationSuite ==", false, "lhs.m_duration: %u, rhs.m_duration: %u", lhs.m_duration.count(), rhs.m_duration.count()); + return false; + } + + return true; + } + + bool operator==(const AZStd::vector& lhs, const AZStd::vector& rhs) + { + return CheckTestSuiteVectorsAreEqual(lhs, rhs); + } + + bool operator==(const AZStd::vector& lhs, const AZStd::vector& rhs) + { + return CheckTestSuiteVectorsAreEqual(lhs, rhs); + } + + bool operator==(const TestImpact::TestEnumeration& lhs, const TestImpact::TestEnumeration& rhs) + { + return CheckTestContainersAreEqual(lhs, rhs); + } + + bool operator==(const TestImpact::TestRun& lhs, const TestImpact::TestRun& rhs) + { + if (lhs.GetDuration() != rhs.GetDuration()) + { + AZ_Error("TestRun ==", false, "lhs.GetDuration(): %u, rhs.GetDuration(): %u", lhs.GetDuration(), rhs.GetDuration()); + return false; + } + + if (lhs.GetNumDisabledTests() != rhs.GetNumDisabledTests()) + { + AZ_Error( + "TestRun ==", false, "lhs.GetNumDisabledTests(): %u, rhs.GetNumDisabledTests(): %u", lhs.GetNumDisabledTests(), + rhs.GetNumDisabledTests()); + return false; + } + + if (lhs.GetNumEnabledTests() != rhs.GetNumEnabledTests()) + { + AZ_Error( + "TestRun ==", false, "lhs.GetNumEnabledTests(): %u, rhs.GetNumEnabledTests(): %u", lhs.GetNumEnabledTests(), + rhs.GetNumEnabledTests()); + return false; + } + + if (lhs.GetNumFailures() != rhs.GetNumFailures()) + { + AZ_Error("TestRun ==", false, "lhs.GetNumFailures(): %u, rhs.GetNumFailures(): %u", lhs.GetNumFailures(), rhs.GetNumFailures()); + return false; + } + + if (lhs.GetNumNotRuns() != rhs.GetNumNotRuns()) + { + AZ_Error("TestRun ==", false, "lhs.GetNumNotRuns(): %u, rhs.GetNumNotRuns(): %u", lhs.GetNumNotRuns(), rhs.GetNumNotRuns()); + return false; + } + + if (lhs.GetNumPasses() != rhs.GetNumPasses()) + { + AZ_Error("TestRun ==", false, "lhs.GetNumPasses(): %u, rhs.GetNumPasses(): %u", lhs.GetNumPasses(), rhs.GetNumPasses()); + return false; + } + + if (lhs.GetNumRuns() != rhs.GetNumRuns()) + { + AZ_Error("TestRun ==", false, "lhs.GetNumRuns(): %u, rhs.GetNumRuns(): %u", lhs.GetNumRuns(), rhs.GetNumRuns()); + return false; + } + + return CheckTestContainersAreEqual(lhs, rhs); + } + + bool CheckTestRunCaseVectorsAreEqual( + const AZStd::vector& lhs, const AZStd::vector& rhs) + { + return AZStd::equal( + lhs.begin(), lhs.end(), rhs.begin(), [](const TestImpact::TestRunCase& leftCase, const TestImpact::TestRunCase& rightCase) { + if (!CheckTestCasesAreEqual(leftCase, rightCase)) + { + return false; + } + + if (leftCase.m_status != rightCase.m_status) + { + AZ_Error( + "CheckTestRunsAreEqualIgnoreDurations", false, "leftCase.m_status: %u, rightCase.m_status: %u", + static_cast(leftCase.m_status), static_cast(rightCase.m_status)); + return false; + } + + if (leftCase.m_result != rightCase.m_result) + { + if (!leftCase.m_result.has_value() && rightCase.m_result.has_value()) + { + AZ_Error( + "CheckTestRunsAreEqualIgnoreDurations", false, "leftCase.m_result: null, rightCase.m_result: %u", + static_cast(rightCase.m_result.value())); + } + else if (leftCase.m_result.has_value() && !rightCase.m_result.has_value()) + { + AZ_Error( + "CheckTestRunsAreEqualIgnoreDurations", false, "leftCase.m_result: %u, rightCase.m_result: null", + static_cast(leftCase.m_result.value())); + } + else + { + AZ_Error( + "CheckTestRunsAreEqualIgnoreDurations", false, "leftCase.m_result: %u, rightCase.m_result: %u", + static_cast(leftCase.m_result.value()), static_cast(rightCase.m_result.value())); + } + + return false; + } + + return true; + }); + } + + bool CheckTestRunsAreEqualIgnoreDurations(const TestImpact::TestRun& lhs, const TestImpact::TestRun& rhs) + { + return AZStd::equal( + lhs.GetTestSuites().begin(), lhs.GetTestSuites().end(), rhs.GetTestSuites().begin(), + [](const TestImpact::TestRunSuite& leftSuite, const TestImpact::TestRunSuite& rightSuite) { + if (leftSuite.m_enabled != rightSuite.m_enabled) + { + AZ_Error( + "CheckTestRunsAreEqualIgnoreDurations", false, "leftSuite.m_enabled: %u, rightSuite.m_enabled: %u", + leftSuite.m_enabled, rightSuite.m_enabled); + return false; + } + + if (leftSuite.m_name != rightSuite.m_name) + { + AZ_Error( + "CheckTestRunsAreEqualIgnoreDurations", false, "leftSuite.m_name: %s, rightSuite.m_name: %s", + leftSuite.m_name.c_str(), rightSuite.m_name.c_str()); + return false; + } + + return CheckTestRunCaseVectorsAreEqual(leftSuite.m_tests, rightSuite.m_tests); + }); + } + + bool operator==(const TestImpact::BuildMetaData& lhs, const TestImpact::BuildMetaData& rhs) + { + if (lhs.m_name != rhs.m_name) + { + return false; + } + else if (lhs.m_outputName != rhs.m_outputName) + { + return false; + } + else if (lhs.m_path != rhs.m_path) + { + return false; + } + + return true; + } + + bool operator==(const TestImpact::TargetSources& lhs, const TestImpact::TargetSources& rhs) + { + if (lhs.m_staticSources != rhs.m_staticSources) + { + return false; + } + else if (lhs.m_autogenSources.size() != rhs.m_autogenSources.size()) + { + return false; + } + else + { + for (size_t i = 0; i < lhs.m_autogenSources.size(); i++) + { + if (lhs.m_autogenSources[i].m_input != rhs.m_autogenSources[i].m_input) + { + return false; + } + else if (lhs.m_autogenSources[i].m_outputs.size() != rhs.m_autogenSources[i].m_outputs.size()) + { + return false; + } + + for (size_t j = 0; j < lhs.m_autogenSources[i].m_outputs.size(); j++) + { + if (lhs.m_autogenSources[i].m_outputs[j] != rhs.m_autogenSources[i].m_outputs[j]) + { + return false; + } + } + } + } + + return true; + } + + bool operator==(const TestImpact::BuildTargetDescriptor& lhs, const TestImpact::BuildTargetDescriptor& rhs) + { + return lhs.m_buildMetaData == rhs.m_buildMetaData && lhs.m_sources == rhs.m_sources; + } + + bool operator==(const TestImpact::TestTargetMeta& lhs, const TestImpact::TestTargetMeta& rhs) + { + if (lhs.m_suite != rhs.m_suite) + { + return false; + } + else if (lhs.m_launchMethod != rhs.m_launchMethod) + { + return false; + } + + return true; + } + + bool operator==(const TestImpact::ProductionTargetDescriptor& lhs, const TestImpact::ProductionTargetDescriptor& rhs) + { + return lhs.m_buildMetaData == rhs.m_buildMetaData; + } + + bool operator==(const TestImpact::TestTargetDescriptor& lhs, const TestImpact::TestTargetDescriptor& rhs) + { + return lhs.m_buildMetaData == rhs.m_buildMetaData && lhs.m_sources == rhs.m_sources && lhs.m_testMetaData == rhs.m_testMetaData; + } + + bool operator==(const TestImpact::LineCoverage& lhs, const TestImpact::LineCoverage& rhs) + { + if (lhs.m_hitCount != rhs.m_hitCount) + { + AZ_Error("LineCoverage ==", false, "lhs.m_hitCount: %u, rhs.m_hitCount: %u", lhs.m_hitCount, rhs.m_hitCount); + return false; + } + + if (lhs.m_lineNumber != rhs.m_lineNumber) + { + AZ_Error("LineCoverage ==", false, "lhs.m_lineNumber: %u, rhs.m_lineNumber: %u", lhs.m_lineNumber, rhs.m_lineNumber); + return false; + } + + return true; + } + + bool operator==(const TestImpact::SourceCoverage& lhs, const TestImpact::SourceCoverage& rhs) + { + if (lhs.m_path != rhs.m_path) + { + AZ_Error("LineCoverage ==", false, "lhs.m_path: %s, rhs.m_path: %s", lhs.m_path.c_str(), rhs.m_path.c_str()); + return false; + } + + if (lhs.m_coverage.has_value() != rhs.m_coverage.has_value()) + { + AZ_Error( + "LineCoverage ==", false, "lhs.m_coverage.has_value(): %u, rhs.m_coverage.has_value(): %u", lhs.m_coverage.has_value(), + rhs.m_coverage.has_value()); + return false; + } + + if (lhs.m_coverage.has_value()) + { + return AZStd::equal( + lhs.m_coverage.value().begin(), lhs.m_coverage.value().end(), rhs.m_coverage.value().begin(), + [](const TestImpact::LineCoverage& left, const TestImpact::LineCoverage& right) { + return left == right; + }); + } + + return true; + } + + bool operator==(const TestImpact::ModuleCoverage& lhs, const TestImpact::ModuleCoverage& rhs) + { + if (lhs.m_path != rhs.m_path) + { + AZ_Error("ModuleCoverage ==", false, "lhs.m_path: %s, rhs.m_path: %s", lhs.m_path.c_str(), rhs.m_path.c_str()); + return false; + } + + return AZStd::equal( + lhs.m_sources.begin(), lhs.m_sources.end(), rhs.m_sources.begin(), + [](const TestImpact::SourceCoverage& left, const TestImpact::SourceCoverage& right) { + return left == right; + }); + } + + bool operator==(const AZStd::vector& lhs, const AZStd::vector& rhs) + { + if (lhs.size() != rhs.size()) + { + AZ_Error("ModuleCoverage ==", false, "lhs.size(): %u, rhs.size(): %u", lhs.size(), rhs.size()); + return false; + } + + return AZStd::equal( + lhs.begin(), lhs.end(), rhs.begin(), [](const TestImpact::ModuleCoverage& left, const TestImpact::ModuleCoverage& right) { + return left == right; + }); + } + + bool operator!=(const AZStd::vector& lhs, const AZStd::vector& rhs) + { + return !(lhs == rhs); + } + + bool operator==(const TestImpact::TestCoverage& lhs, const TestImpact::TestCoverage& rhs) + { + if (lhs.GetNumModulesCovered() != rhs.GetNumModulesCovered()) + { + return false; + } + + if (lhs.GetNumSourcesCovered() != rhs.GetNumSourcesCovered()) + { + return false; + } + + if (lhs.GetModuleCoverages() != rhs.GetModuleCoverages()) + { + return false; + } + + if (lhs.GetSourcesCovered().size() != rhs.GetSourcesCovered().size()) + { + return false; + } + + return true; + } +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.h new file mode 100644 index 0000000000..cc8d4190ec --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.h @@ -0,0 +1,220 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace UnitTest +{ + // Common parameters for process related tests + inline constexpr const char* ValidProcessPath = LY_TEST_IMPACT_TEST_PROCESS_BIN; + inline constexpr const char* InvalidProcessPath = "!!!@@@---???"; + inline constexpr const AZStd::chrono::milliseconds LongSleep = AZStd::chrono::minutes(60); + inline constexpr const size_t LargeTextSize = 0xFFFF - 1; // 65,535 chars less the null terminator + inline constexpr const AZStd::chrono::milliseconds ShortSleep = AZStd::chrono::milliseconds(500); + inline constexpr const AZStd::chrono::milliseconds NoSleep = AZStd::chrono::milliseconds(0); + + // Writes the specified text string to the specified file + void WriteTextToFile(const AZStd::string& text, const AZ::IO::Path& path); + + // Construct the arguments for launcing the test process + AZStd::string ConstructTestProcessArgs(TestImpact::ProcessId pid, AZStd::chrono::milliseconds sleepTime); + + // Construct the arguments for launcing the test process with large text dump + AZStd::string ConstructTestProcessArgsLargeText(TestImpact::ProcessId pid, AZStd::chrono::milliseconds sleepTime); + + // Known standard output string of the test process + AZStd::string KnownTestProcessOutputString(TestImpact::ProcessId pid); + + // Known standard error string of the test process + AZStd::string KnownTestProcessErrorString(TestImpact::ProcessId pid); + + // Generate a gtest typed test fixture name string based on the specified name and type + AZStd::string GenerateTypedFixtureName(const AZStd::string& name, size_t typeNum); + + // Generate a gtest parameterized test fixture name string based on the specified name and permutation number + AZStd::string GenerateParameterizedFixtureName( + const AZStd::string& name, + const AZStd::optional& prefix = AZStd::nullopt); + + // Generate a gtest parameterized test name string based on the specified name and permutation number + AZStd::string GenerateParameterizedTestName(const AZStd::string& name, size_t testNum); + + // Generate a JSON string of array elements from the specified vector + AZStd::string StringVectorToJSONElements(const AZStd::vector strings); + + // Generate a build target descriptor string in JSON format from the specified build target description + AZStd::string GenerateBuildTargetDescriptorString( + const AZStd::string& name, + const AZStd::string& outputName, + const AZStd::string& path, + const AZStd::vector& staticSources, + const AZStd::vector& autogenInputs, + const AZStd::vector& autogenOutputs); + + // Generate a build target descriptor from the specified build target description + // Note: no check for correctness of arguments is peformed + TestImpact::BuildTargetDescriptor GenerateBuildTargetDescriptor( + const AZStd::string& name, + const AZStd::string& outputName, + const AZStd::string& path, + const AZStd::vector& staticSources, + const TestImpact::AutogenSources& autogenSources); + + // Procedurally generate a parameterized test suite based on the supplied parameters + TestImpact::TestEnumerationSuite GenerateParamterizedSuite( + const AZStd::pair& fixture, + const AZStd::optional& permutation, + const AZStd::vector> tests, + size_t permutationCount); + + // Procedurally generate a typed test suite based on the supplied parameters + void GenerateTypedSuite( + const AZStd::pair& fixture, + const AZStd::vector> tests, + size_t permutationCount, + AZStd::vector& parentSuiteList); + + // Helper functions for calculating test suite meta-data + size_t CalculateNumPassedTests(const AZStd::vector& suites); + size_t CalculateNumFailedTests(const AZStd::vector& suites); + size_t CalculateNumRunTests(const AZStd::vector& suites); + size_t CalculateNumNotRunTests(const AZStd::vector& suites); + + template + size_t CalculateNumTestSuites(const AZStd::vector& suites) + { + return suites.size(); + } + + template + size_t CalculateNumTests(const AZStd::vector& suites) + { + size_t numTests = 0; + for (const auto& suite : suites) + { + numTests += suite.m_tests.size(); + } + + return numTests; + } + + template + size_t CalculateNumEnabledTests(const AZStd::vector& suites) + { + size_t numEnabledTests = 0; + for (const auto& suite : suites) + { + if (!suite.m_enabled) + { + continue; + } + + for (const auto& test : suite.m_tests) + { + if (test.m_enabled) + { + numEnabledTests++; + } + } + } + + return numEnabledTests; + } + + template + size_t CalculateNumDisabledTests(const AZStd::vector& suites) + { + size_t numDisabledTests = 0; + for (const auto& suite : suites) + { + if (!suite.m_enabled) + { + numDisabledTests += suite.m_tests.size(); + continue; + } + + for (const auto& test : suite.m_tests) + { + if (!test.m_enabled) + { + numDisabledTests++; + } + } + } + + return numDisabledTests; + } + + // Test enumeration suite representation of the test targets used for testing + AZStd::vector GetTestTargetATestEnumerationSuites(); + AZStd::vector GetTestTargetBTestEnumerationSuites(); + AZStd::vector GetTestTargetCTestEnumerationSuites(); + AZStd::vector GetTestTargetDTestEnumerationSuites(); + + // Test run suite representation of the test targets used for testing + AZStd::vector GetTestTargetATestRunSuites(); + AZStd::vector GetTestTargetBTestRunSuites(); + AZStd::vector GetTestTargetCTestRunSuites(); + AZStd::vector GetTestTargetDTestRunSuites(); + + // Line coverage representation of the test targets used for testing + AZStd::vector GetTestTargetALineModuleCoverages(); + AZStd::vector GetTestTargetBLineModuleCoverages(); + AZStd::vector GetTestTargetCLineModuleCoverages(); + AZStd::vector GetTestTargetDLineModuleCoverages(); + + // Source coverage representation of the test targets used for testing + AZStd::vector GetTestTargetASourceModuleCoverages(); + AZStd::vector GetTestTargetBSourceModuleCoverages(); + AZStd::vector GetTestTargetCSourceModuleCoverages(); + AZStd::vector GetTestTargetDSourceModuleCoverages(); + + // Helper comparisons for test validation (could potentially be moved to production source in the future) + bool operator==(const TestImpact::TestEnumerationCase& lhs, const TestImpact::TestEnumerationCase& rhs); + bool operator==(const TestImpact::TestEnumerationSuite& lhs, const TestImpact::TestEnumerationSuite& rhs); + bool operator==(const AZStd::vector& lhs, const AZStd::vector& rhs); + bool operator==(const TestImpact::TestEnumeration& lhs, const TestImpact::TestEnumeration& rhs); + + bool operator==(const TestImpact::TestRunCase& lhs, const TestImpact::TestRunCase& rhs); + bool operator==(const TestImpact::TestRunSuite& lhs, const TestImpact::TestRunSuite& rhs); + bool operator==(const AZStd::vector& lhs, const AZStd::vector& rhs); + bool operator==(const TestImpact::TestRun& lhs, const TestImpact::TestRun& rhs); + bool CheckTestRunsAreEqualIgnoreDurations(const TestImpact::TestRun& lhs, const TestImpact::TestRun& rhs); + + bool operator==(const TestImpact::BuildMetaData& lhs, const TestImpact::BuildMetaData& rhs); + bool operator==(const TestImpact::TargetSources& lhs, const TestImpact::TargetSources& rhs); + bool operator==(const TestImpact::BuildTargetDescriptor& lhs, const TestImpact::BuildTargetDescriptor& rhs); + bool operator==(const TestImpact::TestTargetMeta& lhs, const TestImpact::TestTargetMeta& rhs); + bool operator==(const TestImpact::ProductionTargetDescriptor& lhs, const TestImpact::ProductionTargetDescriptor& rhs); + bool operator==(const TestImpact::TestTargetDescriptor& lhs, const TestImpact::TestTargetDescriptor& rhs); + + bool operator==(const TestImpact::LineCoverage& lhs, const TestImpact::LineCoverage& rhs); + bool operator==(const TestImpact::SourceCoverage& lhs, const TestImpact::SourceCoverage& rhs); + bool operator==(const TestImpact::ModuleCoverage& lhs, const TestImpact::ModuleCoverage& rhs); + bool operator==(const AZStd::vector& lhs, const AZStd::vector& rhs); + bool operator==(const TestImpact::TestCoverage& lhs, const TestImpact::TestCoverage& rhs); +} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/CMakeLists.txt new file mode 100644 index 0000000000..8298bb7123 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/CMakeLists.txt new file mode 100644 index 0000000000..4b8e9d2809 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +ly_add_target( + NAME TestImpact.TestProcess.Console EXECUTABLE + NAMESPACE AZ + FILES_CMAKE + testimpactframework_testprocess_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzFramework +) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.cpp new file mode 100644 index 0000000000..cdfad1404f --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.cpp @@ -0,0 +1,93 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "TestImpactTestProcess.h" +#include "TestImpactTestProcessLargeText.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace TestImpact +{ + TestProcess::TestProcess(int argc, char* argv[]) + { + StartupEnvironment(); + ParseArgs(argc, argv); + } + + TestProcess::~TestProcess() + { + TeardownEnvironment(); + } + + void TestProcess::StartupEnvironment() + { + AZ::AllocatorInstance::Create(); + } + + void TestProcess::TeardownEnvironment() + { + AZ::AllocatorInstance::Destroy(); + } + + void TestProcess::ParseArgs(int argc, char* argv[]) + { + const AZStd::string idArg = "id"; + const AZStd::string sleepArg = "sleep"; + const AZStd::string largeArg = "large"; + + AZ::CommandLine commandLine; + commandLine.Parse(argc, argv); + + m_id = AZStd::stoi(commandLine.GetSwitchValue(idArg, 0)); + m_sleep = AZStd::stoi(commandLine.GetSwitchValue(sleepArg, 0)); + + m_dumpLargeText = false; + for (auto i = 0; i < commandLine.GetNumMiscValues(); i++) + { + const auto& miscValue = commandLine.GetMiscValue(i); + if (miscValue == largeArg) + { + m_dumpLargeText = true; + break; + } + } + } + + int TestProcess::MainFunc() + { + if (m_dumpLargeText) + { + // Dump the large text blob to stdout and stderr + std::cout << LongText; + std::cerr << LongText; + } + else + { + // Dump the short known output string with id appended to stdout and stderr + std::cout << "TestProcessMainStdOut" << m_id; + std::cerr << "TestProcessMainStdErr" << m_id; + } + + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(m_sleep)); + + return m_id; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.h b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.h new file mode 100644 index 0000000000..cfe18aa3df --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.h @@ -0,0 +1,38 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + class TestProcess + { + public: + TestProcess(int argc, char* argv[]); + ~TestProcess(); + + int MainFunc(); + + private: + void StartupEnvironment(); + void TeardownEnvironment(); + void ParseArgs(int argc, char* argv[]); + + int m_id; + int m_sleep; + bool m_dumpLargeText = false; + }; + +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.cpp new file mode 100644 index 0000000000..a4b899dd65 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.cpp @@ -0,0 +1,531 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "TestImpactTestProcessLargeText.h" + +namespace TestImpact +{ + // Long text blob measuring 65,535 in length (inc. null terminator) + const char* const LongText = + "m65NIFtFmZV9CiZj1go6fPV6xo2NcBeEHoJ1JktsoT9mHUclADmJhjTMT5qaKd0FD8GiredOA6wSUiEGFkc62QwNxuEFUQIuOeQAH8NE4MuhvjSWnw0MCXnf5TppuCfU" + "5Xx7fimsd8wYoxDiPSatXSaXWcCdn7QNttzmGbwuldr3Fmbntiz5aliAcVrlB82RCVQ4v4aT41wCivgPJ0CNbfQyiPDBzZd3cGwLmvt6Nu9pP4kbTVHRsdif0tvs8j2V" + "93fotwV7Q50Xl3jniX2AUnwO6OgZdl97lPHn9wZbO7V5vRGQe1n9aweXK4u3qlBfplvsZAVOoC1RuY1JmCz2V2iOfucmIbpxL5jR6qobgFZI0Z2ioi34ssqqyH9xFgwG" + "muF69vqJjYUzJGgneGQUqWTH1yr4AIFyqfBFvBe04UAsYfo7jPQWxCpCRXNQfTuN2609HPafOQ4IJh8IdL77CtRd8B1y1Ah8oqWzQZ54HZFOWQ1FZ1yCIxcnaBNAhauS" + "RJnYB4gcTmdtrZlFysea28CjyjroTKTzeyzqfNhfteIs3urS3IyspinKEFIAWQ8z1NYS0ks6o47r777gzQcQZ51T6fAdHO346PBHyeZBdXZxCXK615SKctxlG1p2TvhQ" + "2TMOtb9xTr24JrlijnR9WFGzG1CuoLeTN7cgm6rk86KVbFnG74XwKNvIZ5xbHaiD0qaSrZzoGtBxrDdldw8BjcXLZS9FFOToOxdUASqX8TkWEP9oA4CDImFRr3AlUtsS" + "g6m4nzc7Lp4wdM8Jd9pPl2rmF7HgZ5yjO3dew9G83ewcAyN3XhLjVH68ExbdN4RTKbHsczWEPKDWQAoEzGLn0YILmrKh1f2GaobAcnqbyPkN7pMax7CqYfUJao1DvedD" + "gfqcRzqEb9F90Xn4Il3SaW0Qmsxy6OGTJDSSOJr7hBsthALqpKdFpMbFYeAvh2PYhS6G8KvzcNeKLcZfsWdONwVWnenuyQDRxQjOzp87pHMEtWGDJ2UxTsIKGvvxjrqC" + "bBalQhDZk0mIwUJdb5hf5qkJLD5tPdozFDe53FkFYOKDlsWdRpSvTnYeBk1WMxGD31yywHFUlOfUf2a17A76dBYu6Jb2KMfHcNfxJhIJOXWfT8nnIAJwvir4SHqlzPbr" + "IEh35MHMM5XpbreQZjD7sB3ovEmZfXx4dCbztrIXs17zpskuW36cVQDxR4Mz1ZEbdUtdZg7r8AxUNHZJhe0v96QmodqTp8qh7WP8EUmmaTr8F3PMyyMXgJfuAczpmMfr" + "rXvcFE48Cr0ZRcMDuWpPthkVPFxXAcaOAn6eqaJdHRQlioODCbjjw1kEcV2VcONDXtS7pV3YguletmZOUxYIcqVqWYAyztnam06QtmuCTTjg0l4a6R3obuugJZAt1gBS" + "IN6gaVPKQwJQLvLM7ChKDB0jmYE3nIRliYjIZ02blecFbvqCxJMakyc818tULWFQNV3msn43rNLCO3VAUKaQrXGn3zWjuqeur9WFoUnANTZhndyiuspIx0mxrCuRXqpO" + "h4zNhN0d7Rh0v22PrkzgLvB23UcgxbX6xcw7UQ5g3Cq77sq1ko191ZpKuxrBXhDLye9GXv7BKh4Wcfc8qHAP3qjKmHZInIVlpholPrxgrxfPW9MZSGDFPxJfhkyb6VSG" + "SNcyMPMhdtoCtUUAfDXoqLCbQmN7On7eJ0NJZdZA9BWtzjbFIxYYmnxl39QLFmpEkgZaq0AtAsYh8L7sbm8v0yGB0wTCWLFYqAx8NBsooS2YlgT9SX4XFgG29HibgsX3" + "VkCoJ3m4nzukHgnpzI6WIoiNjj4Z8sHR5NLWYWfN3ZOCj9dw23dhcTnRVBbUxkpyOMybV2S7ytwd0vb3ZJ6hlOh6dYZNdw2M3iN0NaKYs55a5zxNAMyKBHfemBFLN18E" + "zVEQgKz9qJv8EEBmgZ2p8iKoeeQrSRLz8blVW1OPilcpKGvIh9kW4lgssMZ7qRju2oJ0DyJ3joEnFOvtGwljLgoy7AzHLpN0dF5lrD94IL3Kn7b5g2h8yAhwRAV3Pmtf" + "GHLnb0gKzLbyJVEoBhvH41B5MOHtLfiAtcyJ2zMV5R4ldJV1vJm0t79xK0X99YykIlUuguwxnzWwd7tQO6F00gaBGvpVcz1zEwJEenBbjncVzXqAQwnO9zKtzUOcC5nR" + "7CfvLz0l4dMfAAMHZ7wGlCnWdBVKO6L6OyUSLGvrpz6jhdlhS45QJKkzTB50ZgOmK7owJwPrTnvDvH3Dxq8SDbmynSfvESmx4utROmawwY9R3fySBFKsLSB3lYlmg8Q8" + "xjabKm0PCDMjeslYXgohxklk7XMCOzkutm2o72sOGgqvFNXFQz9BJAnid2f9KpGc29oIUobf1RyIcTXpnPMedrM8brEd4v1aGdcb9rJv0eE3v9xt3eznh85sf2AN7PwS" + "tROP09CXfnq0qj6SOr2Wop7uCypqKx3gRSTggIZJL2Q7bFvwhYmIIpxuGMuKOBf2xxzo4pqME0HfIuXNkElZpj729pyxas04iOheSqurWhM2EKKmaBrkvFVK0mEa2T9I" + "9Rjh1ZNYvJU59S1lrD0DP4jkOdQ9bHYA9jgpXmFFN8jgWRRu14eKdrTYsZ6w3HNAiN0nqpmQUl30d10UAeMJHkv4xD889zQRMmmPCgLLJfvhZYfiwlqzBjbh7OQhydxm" + "dHPaOcFZpEQwbvR3KhHSVcbhKewJGHLSbHNWsRRsJrfOBnwSm0TfU3FD3xEqkPEl8mmsgQvakUAxI49RV9B3ueJJ9K8mvsZBBIRhnSZoqwipEskXqIc2VIW1CnW9qi0y" + "OObnoJ7qBpu4aueF3lGn0rgPXiXpcw0KaFxoq3kUTSEJXsj7mwHVYxKJfoPdaTuWDfJZ5Hk4Xgy16pUiD11SxMl7GZcXuAGl319LqX3nIAiahaoHTAhYimesTaf8x5h6" + "kgPxITKZXkqdD21Buykh4jKvcPLVivIr7yM8NOKvDf6zgQmm6VHihI4XFLgvEUe6EoYoE0RJS7Luc8dVFdNsVcGICayaaznjL3sFV6J7d4TVd35PEGmXtvLKKSvQ6Be9" + "gb5KZEk8pcHvYqpqzyrX4ZFve3cpv0IyLAlsWSawYV5L7eluYJNsC0vBImelV5oCecW7J6WmYNxTBpQt4M1MWcFzw920KaE9mXf4gkNCQb14FrbT8sNzt5JVFgz9Fjy7" + "aoRbG9xGCqyZkUa7RwmiTgFuk5CRSCgMu2lUNydfnAHbapUpkFyYRAShQr5Hjnu9H0OPhWUSTM6tz5Zzj8OzAwTTTb3Zp5DBywqQXZZPVcUutswpPVHgRRI6t65rd9nB" + "7XGXuJNfSQHotH5V3smIAk5gJezFb31PsfLkJ898DYGkmCwWtOvJqCglscSDSLv0EUoQGIRpJu9W9PQR9pU2IKpJhYks388vSwtAnYrUNOxEJAmUfnKlUZsHbJtFezAM" + "YtcS1I1kBK4eaP61zPWBWQ0uTR2UMXE6oVNuHXKLlQZFVADVTNBNRFvRO8s7LTmE4iMEeuRiOHbOkzexztJuxi4NDnyG964iJ580zHsehJckVyRh9w8AMaRs7HE7BwKx" + "gGwKY9DmZ8V2CyvqDLjxtdt9yIaPxg4ZtsAHTzYY4gXCQGoZRp9AsWaoNSJKt2JRmZE8CwJ6kPUyN9DCqPH33c8mu1zU2JHLBrsjNpgAaOlcr95W9YRYi6eBHoE9sT4i" + "a1zNr1veiFCh281PLhH52Bh3L4eXj9Z9vitWE9nJ6U58aDGj7zP3ITxRBni1UVlvVOYkInOQysSB6Qc3kjKq2dpYjCUQBZRaJO4fGyK8GwsXkBJAVJ5cFFCY2DARgsB4" + "9vksxVhIC8iNCUHeuzaIqqoDVD4EABtN5sgbjqsTmA76iojm99w0sfOGPdDmqKfxybKi8Lyg7xOQUu0HIIF4HYOcnsG6xuHfW1CNqvkcZR9tnOeyax19PgZaAX0Ulo2L" + "0HNi98GXoKbmuD9l1eFWPRQVVFfnDDH84KuPZcciXWWAIMDhGwtfUd72GcgeUju0NqI6DsEf9WPDDAw8VS5U6rHeXmhp9hR7vbOnyAstoarOqPlx7L3q9P2jGJu7Lhzx" + "fmUY2iGty6xoG7MM4jnrywA15ErsQXjQbe1JOwVx7YycaIH3pLjd72qLp0Oy1lINFHQnbqDqdnWWYHHNFnnYrndj9fXueBzctmlxoUM2duxpZJQ6BB2CXQ7y9guWWx9N" + "PrrYO7QpMAzi5MOLLIqkbkQbta7SlxHh7eouqkBHOvWaAJR6iI52rEJkMUudh5VevD5Sh4QMXUR3OsfPTLyENMhT50zwLzBzSHKQ3EaT3MuyERp33j3KXvME05lZpUfy" + "g7UkcSCAm0duYh7RxWBYykoXmLxBtlGLs7XtqjGvzvuQohfJGMnUnYCxNTpxjIlmhORhc8lUBPZ8Flq51MhnYp6sYztV3hvyDKY1wSgCvauNNNxjdguRzMOyBl8PcNcV" + "a02kDeE2wqVZvhv48vyzMITPZvMKOzhA2U1giReSYereQOehKtyeXXqhTGooUxUfOfE5dKCbfLyBW5wuptRtY0LZ1VdxWQluqErsSNJvX6JVXic4n3vwc7ockTlU8iFk" + "UCG2tfegjf8EpSlydI4vTjphHnIkiTTG4g26ILlGEDnoxJL3U8jZhWgdf5aGNaJ2wiOLy7FZlJkD8StKWRFjyGyNM3LEDOFAmkyzWwwWOV0hBjAKwkne0Wdbn1vV88b3" + "w2u4Cfi1ZK3n7NeZwI96qpzWV4oA0aj7yl4mkjFtouALOD09aKqspPNBMkY80pMwNrwkp9EIZNDvAslGAdCs4LmmsAsXMNQqfDo9ubMeDGqgjV4FhhrA26UljfKbCKCe" + "05jYo8VhtjfRUAutYjCE3H4VSVGEs8OIkQyNdvPcI8o1kqDoG0OdadaId1v4z9Qos5KCvPh6wx1mh8OW6x8MOCUwWKy7kDbIk81zjfMPADNTHe4u5xRsir5VSiaBGb3G" + "TADf5NpPnxAOpUAJlbiaLRA4TRFAuBmITqBeL8htAJIggtZsuxUZkTIs5acgpRBYgdV3gAN8mmkBb0m78x2buNHIqdbV8ggNCuJICD14AK8PKSuxNvvnzqhEEkUZEr8h" + "dF4bDnZ5kL3djQDEl0C1eDGOOL0LqyspoADQFmxkZV2fJAJQnYU0ewqvRqZTCBNtRtg1vneRzDUDxlSkMZP6PddDOkJ03VxNDnttG250htR0M8elz9sHCzMxBh6T4k16" + "lwOGctboSL0GhW7ZFjeE7sfXgCfdH80r0K28pw2sm8l1cdY14ZWuqpRP25xI6DYlWAx78T6yQ9IdVIrF5eVYbAkVs7pUCgNgOWdJmhet21WJgc25sRRcMpy9Gxq7rugS" + "EhhVAtfM4HiA3ofo3WwDuWcsUCjqVcQjh209w0O4vRDhHediAccuO62DwZRG4Gh4xAG0yFEOsrrk9znlSV4XMc3HK2ARPEguqVCVgpjJgfr5jCZnwfwmTz4L6DAAVb9f" + "ctBNSpYhr22klJgG96CHGnnI5ybpv1BGTYlJwRS1DUDeoWh537pcRmD2l5LgyFu2aWuwPxEypG9hpPmf5Gj3uwdyL0ABlKTUleyBeEVCu5B1RgnzKe6BTnInP3XtQKJu" + "l49rhKfDo8JOtcyyQ4LXEVaWnOrx4hs6Atu1L0mWEZ2M1UGwfYrv4Cd1ORt3WU30ZWwr5EsVBj4i9WtVogL3DL66CtYNM4HTUdU9p8VUjEMiKoxt7XkligvrvgcDxqe4" + "gGRAsMd1tDSUXCxYOv9aMO64DEYOAEkODj6neQg2zZwANgRY4wAagV4vzL4YomGQmSA5xE5mE0InfMJJdSVLPdamy2wNshqwe5ow7aj5c0itCMyL0EZek5NmMBqBAn5F" + "9pULD0wljEo8TgcvPkVSmIWrA4iTMraEHeHS7KgzDYC0FtM8KHnE5j7SvGxvykldyoXfjHn8HMe5XdHqNxNREZarZcZ6nkP9CJYCaWpuYoCDza4RBp80HTTDSWZXjKpY" + "H2PTPsztToaX8GjgAfjjICrUBiuKdOUUmznqa4SUeKvKoKbLK1GIRnOOk38aS69xB8t89iuhkymMZrczCWKdVTdEaDMFKI0ILTy3wyfkbkiXjEVQ62byURuHDr3tF5K5" + "aqLl11VLvZzNsTbJ6DhWaKD0HzSD0C2r4LtRzSNtsUkWIqETP1tkqCXAzD1KeP90KAVsj9TLOa8AagEau3tF041igctfjTa0vrrXm47zncAKn6MpklJjoBrtE8c8r3p6" + "rOPEtDdM2dZxaWwukrtLao3F5aezSu1462z2PgnYMmv1FlMNe3eETZ7thoyChiQrxyxrkzTKjwj6ra1NNYok5dZE6bgUZTxhC6Y44eAOIqWLn0tTHb8mryBSwiBkrPAx" + "RzVkYak80HfVgxFyL9Rb1nsq39QZLxrWuniLssgEFrutQA89B3nvfCV13NacROJdL309VUPMbDAZXxc193UYtIEZngCj7H8DGdezs2OQ8j8eIvSizweHcbFSPanqtlKN" + "BJ4rQYsdh3Zfq5OYMdNytxp2CNoUHg36oI4zxSMwyYKPC4KicGSQnwLMEwmyXKu57CazUHdc7PZkK3Lea7kNWA5hUdnBmrxd0xIOVjhFi4e3wSgsHnGphH4UF2rDPvC2" + "GRvuPSbEwEMyXJL159oeR0O5z7DG683VtOuLQPCyLm1tgqAGUaVg2iZULOfOLZuHlC8ByzwKQnQGvTNL1g6lHxh1ygaDdO3weStxgpMe9O2mV9mfj8CsOZSbx12NtDvR" + "46yqInqtHCT5nIbzTYQloULXEuXWL5AQ67g7rUsInEqzO2iwh1LwxqLMvoB42DPBEvpb7fmVOFddxzaEE0kd4p6i7t9cUi0JR1e2ZMrMNK7zFyLmx9gvAzarDSjGOY3n" + "EvKdBn63lLSANMwoInAt4Aept7tWydGScGtaj1yusS5iwO0THRP177dKlAfCjkFGx6mR942AnCxo6oMmJ91gATIKWm2fgBaW5BUKy1fQ5MqQDnQzEzRYEjMi9k6vQEur" + "VkVM5059sI5UJxlVr9yaKGHt3KTeu1F7PGNWQCwPIMqpVMlx7CbQ3bhQWSJRNISWUWFrWCOY1n8jbtPdwojNG23ugMCGwZYedB29EHuBfHIDX2RPdKt2dJAHg4aXjx6i" + "1TRgq5nm4K0Avaq86WlMfK3JkK7VM7Tt4XzfLLx6Uutkv7XU7GGG0bbBbUrtIsug5NFvhvrwcRR9KL5kl9z9nS1iJu7tJdljEcGggHiqjRBe6hoSPxvAa7AF6kDV5iOV" + "Ca9LkWZjg9C2P6Ri4KcmiK3OwZhFzn6P6jFAF8Y0raLbZSZBLJwUUzT5fYLYzpiMMsmgT2HXl3tOzEEZKhdxRjBPR5br51LanQypKBNgFuR7FlRuSEJaNiEPZmXK3GeU" + "Pk6PSpR0AdFaEKbowx4wxsJvzKT96pSzR7LaEEqOa9phydtTh85HoIuPnKkVcFIHGpbynCGL4TUAbqv14rfQfmNrVTqx9hqpJApofNu9tyhBYJUrQ7PrOCPG2ur34lOw" + "sUg5MkHEyzVfq0WGqlC7TTGoO3bA3TWsO5SHGOhHsq5wuo4JXjPrOz5vEI2tQQlG2Pn275cZiCAJ2XZmxG7k3LRUJOQ6NpFg1Rv509bnfB1MBet2KCA05UYQMG5MMaWN" + "WlauS0eN6pka7p1l2D39aDiiIeOjwJvWfc6Tf4FM7muhWABXwDymT41VX5140sKrFZ6h84jdBDKBoTaqANexdXrs9D3Zr2BlgiP0hDX9bqmwvvxP4JACCBFFcA2ZSM0E" + "K9J9P15AjyZg6iWei3FjJ9t3B81dN7NwG2x8bGgw8RZt5m8A9D8F3D6LeVgGVLGuAa55DFnhCwtkCviBSTJn0btERDELpFhQU5H3A19P26dEFpIZIqM5kj9xQwqMUVbC" + "riaOOL3BVc9mGt6jSJn1TI7YyibFoT06lSybiMGB3CO6T2tov9puMTiVoWEQJDGNlUUhqLyokzdbo3d8lBe4aWUFq19Cs0HZt50o9xdbIQcyEtHBMXNZ3gwECJ6QbYC5" + "igpTeTLc2aXk1003QJsZ0qgr6OkeOqfdBFr37zB9EEYw29uP7en85bKbqk3Xdnvo8HbIeCIDyb4A5u3RH9BJbre6CCsWbqP9P6bCkJrtT7JuDcpb2q7sWKuP6vR3fILk" + "eR30Ptmb6XX6C6LL69TRXPDE5jkuWiO4Sb3SJi1zSqztVvDgyfrar1P0LXnE6vkJ4Anqws8raHKz9EYuo9ZLZoYqxkqieyzRiTkqyrz7e3pAk7VLpHItA1HjV5NNBWyB" + "2Z7xnMziRxWtgv6APf4huLiLE7WcTTkkdyv5vm7uPjtZHq5ziUx30IjXW4fexcBcp4kmFPwYDBTyI7yOLHPrXxJIFgcdalZTEbwxSVLdlJpucFkXfG6uIVWSTWR27O5F" + "ClBzl09FLL5w0EON184alTAtonqJou8HJjxPQC9zaqqgshTvdgLw9IVZLFGTQwfs3bgSiFH2foWClTcdns2SnY1BZf2EdPUETwJKaFlAcz9DO1shcPN7Jo1gjm0xz2um" + "vznuMIOu48BSYrkvev0C5P3fTNoaYsAmmA2c0oWfgeJNN0SgEJ1FuNWc00yLWOblaOEakAkIysiRemkijteLN2p7OoW5hzOqwADExvpRsy4gdHbCGfHGQPKf0wvhqXvm" + "OqdkPLqp2gynWmiUuKcWPulZlyhgQXAePzS0oVkYTrbv6yy5mLSvgro9L6Vf7Z0OsEa5fWOM0mC718tmE4vbtBXPgXiiTDQVqqOaUlAMndzA18rzlNLRlZtv7XW4bN4k" + "eOcuSPbNe9tTWo9XABv5tqe94TRBPyakEpX9hQ7Fc3yy0wQLXrFSzzZDPlKTajNnOOb1nBksaoD6N6x8vXBTh5rhsG2DL9jKnfrEveVJt6H5o2HrjYaNFPQuddKJb5pW" + "ns98Xb8puf5QSVs4VN8wyzW5veX5PfTUOHjDRrBAzMtmb0C0a0qCuZq8dRkqigJMRv3LCmjtoYYYs1uIuZMWyaqj1BD8g2oOxNc411hkQpwwxIqzm7cevxMFKm1SBkqb" + "JoZHNX7P91ufuEeStHxRXlwUXep029JUyu6DkxuGCxbQIAEvNTdI8zpThTbOxI0jlVKOpCNEBo9LetsePnoA0Hj7qha4owWWfElKHzdxPB5RBxxkjeVAXOQLL5waKW6V" + "nufDcpSYJkpySbB6cR1n0qeX5lIqMBTxyY2rhCq3lK8mauRd3KxTOoDRuWh06FeR6kUindQ3GmaSDvKDF3aWunZtjcpqFmg7thwPhdsw5QyUlS4JFRbpBlc7YU2PJ8zJ" + "UYY2aecQmncf9ixMiYMNQbFX9k9SPM1T0DdzF8DP7tmgKLDsTYqhOutxBRIYCxAS27jBZPsoxBYAw8oxO1NXeCCJ4SETjJw1sLGz9HcBPzONy5slxvbYpdNZumHg3bp6" + "FRXdNDFYKiHkjnWIHP23HEAc3m3gqO4ZR4t3uMORHPFWkMOPbBwoKf6RMlLJsGCEVi0f3hZ9bAMaLVdGgvNRKMvpHNELMw197TbFZiznwIfwwt8tX07AtcDMQmqatYss" + "WKReaIWsECI1KHLfAehJtSqQYy1XPnhbbE7aSWlrkHl7WIbxt9jY4huOuxxhabKaZ1mbIaeyOOefMXSwzbKY49MrblqAJG2mS5Kuj5njMpSjJXhrNndZwYHIEFaquWqk" + "PEo9mbmcDwQI432dz9hY4wmeL3GxJO2318EzCPTITS0mZ8gkNg2DzFmU7IRSSW1hb9SxjdiBZXEuwlMWLzawO3MhiWQQiQfeWp3eISuZZ84IUuC49kKrmDiWkGRxLjnW" + "6KxvADT097jBN8blD56XtitDKkCgnDVtnmQhiNPOReweIesKxwTYzCnXH7jNkZKQtqXjunfjORabiXUAe6iiWLet7Qls4frgiSSjS8oH27zifNCmgfkvTEjxQXXTTFeI" + "TdrjTU5HYmTQJ348OFFYNNuQkZhdhGKaloaMkfhKiB9zWHY6s2hSO9nAfwBNmPD80WhNQPP9WIwaW0hMTFCAGoZNMRcHrlSgdn9pW10yoErcm2yrSx3NKu8paYUwmtPs" + "1qtEla6PfuAJzyJ0VsLWoegOH3Y53UUhggjqIkfqoqIokAsw798RLLlsuMWUGl0UgGKsViEXmBTjCXGZEQNX6BdDhr32ysNyybnPvWOoHF9pNCYssG6dtdMzyxydLYQj" + "l7vnIwwhi92QKHr4qluQCmFqOHvWEc2Wdo5zVeZVjmSycRaFR08b6SZZS0Q09WyGhXO6jy6Zw45nUCY0KwmtB7vIcp0mq9mXSrvnMZt7rKivuHh8H5jOnDTXHrZa3hxW" + "QbTyE1SBTKavdQWknVhUFxvas6tOZk4H7W7JIHT7DYUoRYHQkDPhD2CbOH3ReIL2QvESfjbUBVej22j79E36JoaPGjukIXPpbiA2jsefOicv4T35jEj0bdSbQ3yEv9vb" + "z0YOZPutnxwLfffZmLnnzgU9ydVXnn5ih27Vzj7Lv9fB3spJcf2j02GbNc0rp9gOpu5PhgW5fbCe6IUyfCL3oNnjSm7BSMuumMVGFFJjIOX2idgXevLQTGbkeMwv0KHl" + "tEcMaVj6HrbKYUKvmAIhQ2lDGBF3l9ox1k2lFCRKzs9VvMZohSTLei3ueO57hBOnVCxmMKMLVEOJRTgv7ZX3yQ8CvlZQT7nTJ18O5k4HingPWGjENFctUQ2VfDQK8Out" + "ZKOoLEzCE5HzlyUEJGIjtEhvbKq8EWtBAx4p8v4O4ufAGcIlTzoOveKb7UyXhjqpp4o9BBMxMLh0mH7mNrNh7cmLPDpd1QaR2PdOF5BGs5YtCdHlw8Sb47B9Q0vWAsBz" + "P39MHbITqKLClcUKgJ8a7AEaKnXKVXJGXul6tjwuiuEqfQtpnmY1I0nj75OhV1H2w7CtoXqEMDjrk9yT3mxFPwwVLgCVBNvdAaPdKSTa9U2zw2sNatYgMHN9ors4Vb9y" + "H1elBvlBbl7HoXyIbV64P19xipFrCtZYV7otEtSFtKDkMygolFbc07bnnJrelGMxOpISP77dRfVMEFqLG3hYr8vW6AojA7p2uZ0sVMEwO45ItjLo68e9nl7jhK9DDou7" + "Aj0v1VV5fxZXB0I09r1fGCM65wR75rE6bGC3vUfclYIspmziq04GApz7XCnUyBv8llsK7nHzIglGUynfnPlHYO6tliqLHEE682nPTH8wbUggvPRcrfve09ALkHsSDUIU" + "5HgRPXwQS34C9xFUTNkoOsgzAW9nsnJz3kwadwRqFSHJR8F7IkRQvGsg4G0FKPUiIs01mJ82Lkm53AJ2a8JBYICsWPEFN7e50BqMioWt9b6OQ0vwvPhZ0NmjBDKSwf3O" + "8QxFuPdsQ8nALXz3MiM5UcEykdOElKHtah3DUwJKjX6I3Ie1rSB8qKPEqjrid2AAwJo89TlwrNjfgvLnjz0hjTUhcB71YhM3BJbl0hW0JWg5wLgNQuFp21gzIl96wEVR" + "yD93LuDNmqsEELurUUllJ2rF929e6gwA8aidNYAFmej1OWw2ULGgCXt5Ib6BjUVeexWxQqMsgOb3I7YKsmX7CNgBq5odN7FEbW4MY4VDpgx26bcWYFoV0FY58al7Y2Um" + "abSKv1jW4zEuCOS7NDaceJtPMXUqi4h9pFw3jdJdKP8JbWhHGCJwFotSc3xe57KPlpzZ08pafdZefJq1t2eMK3Z9tRvLnWNSmcuicTMJvdRbgDSONRRvpwxwyJpwuGxk" + "gqSr6hY5QV9xFO0u3JcY2zuYvaJ8wcB4CxqtNBVVghf5HvvPKOYS188mVw9FmrlWuE5dHCquIVj90gp5vbAYJgUCUrRxNzVaL7sTS6jSA1Li83Q1I1NsaJyZa1SKgyen" + "zjddCeUK8Tn3xHj2HVlK85C0s6drwfyxIrbnCbQc5KGEDBM4W9H3Pe4E7zB58UZCOYDq59JD5horMpFT1Ny4CIZz1heh3n5mb4jH2f5AdXnKLqnAPBCBWB2GW90xYEPO" + "ZvUzpGHELmD6Maf6OOlC86wjj9CjrQqI4ER3m0Fb4StARxGXkV0yvRQvttaCZkrMOXogiRvtlQjimLNfFfcsNBEK8UHwqcH5tB5oQ7ix4dNa3sdCrtSSZIdcfKhefcjF" + "xO0YLbtWKnWttWOUDkZx7RTPMYtdZXDF2jJ4mR71pP1vYcHhSGVjBymb7pN4ZqbiAhi8AqCBCch7pGV87bDnLEkxAXdZRs8ItHIT6ztTM1HEI7pzzKPYzhFjVGzQ41a5" + "xnpWXGGuj0p6cVSV23s8fbNIfv7Gh2M6lS7uOjSlltlD3Gicv1HHRV2MEYMwHbJMFiFSPykXge8BIjhE4cCawxau4rKpJFp84kHu7HPlE6G6fUadIXJmyaDyIZ5TTAPP" + "YiShh1HOghRKpNZLEOkmJpfRUwbV4NnCJalLFQKSR3eTRSBqw932wLS1CUcj4bXmHbKDtNOEIkdUp9lT0lOyVP9IUOOFH709dDTlf3Dd6IZNJpXzi3GScIxsFaZVq7NC" + "6cdcKwipRUsD1aghJqSpAJZFHFIU8nPsPJlu9CpWCUCwKIwV0eSWonw2tSGvJToFqhyHmBeBTXsbymRCXJEcA8WVqjkmiR2nHmh6AnGBhdiTZfBxu5lGotYDfR5b46zx" + "fVGo7Ys1LPj1g5U81hl27NqbgutFQ3maGBu5xZkpeka3JCmdbpaPRIO076emwiJYi03Bb6Ncogv3Eg8Cf9xrNmIdvFRVi3iPws6HynQaq48lDYPTRcVB2l29UiA0nJPv" + "bomNxXa2RTMYDs9PzoI9CLRbxvz51XRRrAP9XzASOu5Y7SqzRFYmoCyS6drTrxMts1lywiY2Mzl3022xsQOcHz1Z6g7urt1UrSCtvQYJzIzgtWuHkVN6m9c7qfor7bbG" + "xctZRuHSrQaZUqeTmFduVLQlJuOZkO6VVwyDRUz48pzPCAfQKtDKUj9uuDSNpBlRnreaNsrIL8U28FDk4RO9TYrn2JgWLbo1bYewmyJd3hPCJg6HlLYyv7YH29SoAVyl" + "haTjaIfnjijq8cOHS7Tm6DANE5iuP2MjomrIwI9XjwDtLViGzWEoM7JC7hspvkTqaGnaaDKVoHnLLfAcEeF6dEeJtzSkGmOmt9LrN7Kg5sWBXRfbyUKBsnPIkCwlrbeK" + "fTHdvlzFgBrgdrkadA6GB6h0HMqWcWvzyZoV3aSJqSQXMg3hAYBwPP1iGxygyAY33kKRQJcpW4738TOBPuEd1GXn6WaIlB9DDBKJomssrefT8TuOeTKUEy3HlWPkyixQ" + "1tvX3AGy13TmbTva6Bg48QncMhRQlpht7xuGJ8TTRSeyvcTWFB68tvA6Is15sVdvnvyiwTy75Q2XHM4K06BKGH4PzOm8WRM26Jp2FMcsyZka7134eZVDjQ7Vb0MG9W8M" + "qioMWg5BM3h3Gz5zYKU2iqLZ9qTsLxESdwetbCSb3kn9pIL39qdEcQ5DPkp49nVHmS1NlPquBDBlD3ZCdDvPHVQ57VKMGZ8JdMU1xOyurJBq53FUpxt0UJStd4aeJwRm" + "i3WD7OYA0TYzarDn9webrHrg5XLJL1Da49wQVFuDcoZK1RL5E2Cqd1xA6Bw3jUZ6kd3hEaPTff6J7yBdkMwSnV7hLLthPCexiOZ0PQuyITyzs7S9qaWXTu8IAm6Z9IsU" + "hoNFxetpiEQkVNkLOhDxmTDcks3Ot1vXYaIVJs7mmEaqHYdDDjvccMi7RJfyh8hRADyEWlMkqoY8io3NcZt4p6FtLv9btEwVkJ7cb6wjrqki8V2jas6vpjXwE26PWxh3" + "yBqfBZMG5YItqZBonduR9cM7xyCVQyocuHvTjOFLThhTgvBnNKgjv3twZL9mxlWIG6AEip0xLt0feP4GZDS7c0ojbOYomc01mZkq5NY9nC8SlUPuk4llPL7ToaA6VSGb" + "rgTx3JUx1LQj3Yq9oSJ1A4FqKxVN5MxuPQ4D7CvClVz09Mw8tjnvnCAv5fJdxGEvPT9Ma4vnivT2wrdA7Wl2HtH3KbakBXd4kfwfHgqdQo30sAr8jxZcRr8AavLgx43g" + "PKe307AulqTgKyRy3iiCrbTUUJKkq2BbptM4lzDi3GljCA9W2t1Qs4W4PWWptuYpMy4r2r3ikAlqLgKooKWwhZ5uhgzGQjb1YYpXlEYHMNDLBgXTDGGoqOO0z4Xlcg1i" + "yXRsy1p8kub5VW8xlWtAwzF58t9w2Wn90P02qNzkaNkw9V2jcKRr8MhtDmCG9PKaRliDmch9F91jnQJ4Hq3JD1B0qAKOB1PYSVh5mHXVEtREztkKYiNRup7DEwpGBRnG" + "qCTZvePHgryl9CIRSH4X2D2unXnfpfIg5PWh9NR1sl9ECu6hCLDprmil2RSQWCna4zbISSAUGurXSJ2m82aU14kseARLymGqBNSDBlPBUp4DCSJsIhk5DvIJNuFzP7cD" + "3O9byQeGI64Q3VzClDGntGdVmb5MZ0ZgGpPGqNM6LyN9b5n7oArfgsRF2zsp0smqLPd5dmqHv43FhzcA7wr3JxbxTcp53vmyDMgCpewPvik2HeemXS9UA6TAC9CwrMf4" + "OqrdXohx5x5TQ1Z3kNieRYeqoJ20sVzWgXK3BZ9n91nGPWUV3BsaIQbT7TbrMkcDQrHE5uKuvkQmiJX0AjUOrJBLq5LarUbze7IzxOhw9HaYaOT5XfimMO81cP0F3WJq" + "0cpVwMECtoIdKHioGUA0ZxwArOGbMqZ6jGVHShE6I6xRh62qdLDwJnN1BPxenXG6rxFA3woQh7eIR4wG81yxycQOx6KS9yaiUPZ0xJcWZgwbP036CLJLW71v35xmvx4w" + "OeMVDMIpDOm7RWddxM45nrx4R75c8oYSgAE8bDGhQGnAfC24prxHTJQo900wtDPKvosOFWWzcPgFTbTkK4QybVCMDdfLly1rAm0SJuLRfvDF3PMOoIm887m9LxX12E29" + "FjQK1nSkR5vO0f6SKew3yNoJ7eDahdjpwwzZIej7N4T8bLliw7gYruoFDqEPlLaS40rlCSTEr5QlCF92omO4mHx76bB5ZUalASqBurhaMFay5k8FSANzmbfhngNfgNos" + "wFAVSc9hZaa1PVPvE1Nef9WwvWPYwIvMPqi2QXu4NVkTYHgQjTi8mxE3L0ozUKAMZ1P2mtm9E7HzwFOIaE81DGVD6I0M32505m5IHXVLW7iCobNezqUgNU2htx9q2w2J" + "UlkLIosIHCgw3aarlE9F8axZ1juvhjNGFLpYwe3UkjgAVzURaXzuYK5Ma6XLbiNKPMZuvkXOQxLB5eOgMR58O3Yr4d5PDRLJeYMqh8lficRaPxeFs0ls0hFg9IAYuYgl" + "2fuhqJu5GloC75zb9NqMuQDIRicNur1AhFkNeGQUBylYBjMxSy4BZNOwl7MC8di37VCsid5oWhoYGumzWcLjbChAqjiW6Ogrn600KR66W6dotVjQ2Q4BWEIwr6CJ7nNF" + "ytapx621nMo2PBBUJFl7Zv2i85j9DuLxRwSplpH0Je1nGZU5YxBDst6f337AhExBD8yfurBLd8KVWhd3NPhulptB6UdQveFAtdfcvyjtZfmLnhQLzKGEkJbZnHckUwP8" + "63dAGYf6Ii737h8doUsxsoGzfRkoYzhw5mWZCMGUAAsX0iWPTJMvEXirBqAclhrVd8EypgPvR10yR9dA7Rc1BBGqiX4WA4vjODVnSblpNc6ubuTQlkQdwnmUbSe6Kk7W" + "gYDclvLrp8FqqVe9oA2OmVJSuVOcJwD5lZKjIhZjxV1lQFdpoce27RNiYgeaoGopE8tpYd6gpts8ZJ3ymzEJEzuM7HoMVQcx2gd9x1YP0zUYMCz2FGTEyteCAJSwUI1j" + "KY3krXyLnF4GzQDvvIdPGJW3gsSVfAl8mNlO2Mc9Ex09Q65D79SWw3MuUJkuCg1efM4utsThbeNcAmTAR98UIAr2auj1xknHmnoSVXoM0T5DVtcGrWkGUveXaotUNSrj" + "GrYu1JqislrdQ4AjrEBxYEvj0ic5eoAaYjVSiiFTY5FJqvUdbqjHyEoBiSjmliKDcQ9kGqnNj2fVzJRMgKeVwiNiz9DxyiPeU1a8pG6aW1CHrWw8J68KmRaiWQ2dxRpb" + "agY0Xlpsp7je3LxnEmBihYMcjX2R2k30bpFj5I1xxwciXIdzvXZSfxBNCBH7xJ220acIIiGvGqihMIIcMiKiotbUw1eqgnCTIDLOmKvzKnTbhuNQhN3bRhcT8XDZOEvT" + "wWSdnAAxsA43qbjPBWbftqGyVuDxNCJZ2qocGlM9YzzXJg2ScsPJFmbdk2Ou9YelpInJiDWpaMndSi8QZdO11nW2MNoertuHskTGZoF42mqgC2YqSpJ2DAlJPlF7h41U" + "uTXuSh3X5zDxa7n8CURmgR998iS4SuwbdJUYqotfvjAR1HtGizaWAVwL5wBm08tawEkWl8Qmo1Rm2h3ZZLlYfi2wvopailUsaSA9QMehQEnV91ZgiI4GuV2hzekuySY7" + "hcyxITjNkcOqNT23te9W8eUAOEvuiXADAU83lm0ehRpEn3V63NV4kAr88NZSFIcg4H4AeY3EVCYTvyojeHTMhb0RsaXMpELqpyRmAkmTALZLoEZsSskkioWF6lZrLn2Z" + "w0BmR024Kzh7m2gNewqWpz3xlvznzi4SQnJBe67EPPugbSltS0BQeg5JxynPYEX5Pu9RArpjihRKSDhzTai5xBA1H9Yv44UkZCdWUq4RaCCxEVzgeabPBhZOhbuUMuom" + "Kg20t1aEtLqIIiaX6KuKr0qjO7a8AvL6gsAqdCzI6Ml8RAOVlfsJHBTRZoDP1WAzHu5DBYJ5zJ5Q19TdAdc4VAfsPb9pNFtZdFdLx4kXiONawnVkzAzcItx236dpifCX" + "4bVCd8hOjhIn4KjksdvclQbNQd6x0bV35N0Km5AjeeSwBcCaXdDTPwH1iQDsNg7S1azrDSh1oqzAO3yamt2QJyRI0k9phTNVEHGXZFo2doqCMYTZf9pF46hzRVlY7EAw" + "Ke1Vbow85IMRNcrWtGte1ld6Sh89uWNSzfVqivSLpYd4zrmmAbbW7KbZxHT94tFgJiqtxW9bjkdkWrU6EBWosdMOlH8AEY6zk35Lr9FcTMzmsbfXEXzsmuD3iWC83cQT" + "LIHGrWc2koDgXZCY3SPXdboUfG8M4uxyUESkw87Q3hB2mfjSrwjbFZyhkBWXE8C1yhl7oG4KnJY9Z00TTNJJFQmG8UMAQqnraoTleI7iUN77eFm65OHEp62q9FpbJZih" + "8GFyFstnUEJZaPmHBGkGD3LNXsNeLr0RNAgdxC1FsjWPfjrSdNQ5JO7mg6DpZoiqnvnbwHMk2kB4wra1csCe5LI82FZrEHliaDPo93hoPvakmUKvCirxehDKHwCbRaR0" + "aR0CeCo23xc6P3Dti8dcdiWXkMa3MFdU1LjlDXeIcS6Ovhat8sgOp04s86She5MwS0n1pMRv8Qr7f15PFF8kVK0PpfQ6WUTMKoya6RtBZYkQ2A0pdrvNEVAWWgck9RWM" + "BXnsBbc0MfrJlDaQ00P9Nf21yd6CTNLZRFZYdWXd9ss4uIaRDQ1pPcwoF5dklMyLEyQinBUY3rWgSVKgRHHHa28Bzo6XXBwN80tE66hIFsxro2q5PXQwaYR7S8absEhX" + "haUb6kGrwQpO7ni0B9m0g8L5Xv6JyBAX2A7vMp2E8sLGTbWcnSdz51DfPJs5pW2GLbQ9AigvRftXWUpgDELvxlFOOWg4tsr2HT890j4PhnyFUxcQHNYN4lF4iFr2Muhl" + "mhYcCifWzrhCP0LZ31lGgNNQE4hyYwAUzI6JZ88Gw9sAKngB7mtmoBtJqxmxjPA5IXmXuwC20MDxYwi71gLFoRRqJy4eq74U9gU9vWZWTDjNAJ4v97ngZN6UvxnJmZMb" + "L7Spv7bOmiwoBeGOtp6kjVvgjzyM699Ljoy4l1TAgR0LNnEf1ue2iJeaBtwciWqvhdEaZcFqO7lGjo20BAPTQugYN7jVU2Oivh49sLLCJptKrhG5ovnPZZdIQnm5hR2s" + "dusQMFP1fi1jlEhecCihahLQnigi78WquMOE0q9NS2N85gn3fmXByTgOlW0QwWRxJIbXLz5AU3E09wpSRU4MGYB41JWPjKrb2FQ8LUBS5rhmtqEV8TMkmeFmFcH48UUL" + "FhNvRLnxXKoaJo5uNqXQ2C7IvJ0UAT5NrjCsMJdMm9uYpw8Mt0reYMvVVXN2sB6lhRbucZ2Piyun9zPjqYuzJIoyvEncHulxVcPQKCmdwfVxsQ4CrFRKYXO7F2WbiFY5" + "jvyfymTKDEX5816guSrltslhnPsivIy3oYVuBU204wOXhuO16iJS2PB5ZNVepUEQvUdYFqiRU9ltVxd3OhdoJZCsLUxMm3vkivld7QoHbYedzOyv7e2yjqPXz8RsiPam" + "orlZ6594zKqNiIZkDFpAc7Hdo3dae3HXnqFO97oezJCSFK3P2364nmYHVSGINtD7GQqOuC1ZOm46MfC8GQJUidFV24q2QhMfF828dl2zKF9tEk8GBKwdiqifMAFcJUEy" + "VZnliKQjFqHpxQZULef7YAsvB3uE56wnJEeeVNw3dBuGONKqaAZFhQkPm2pG3KG8U4Kky6s2IDBQPcggjzlIRtLEomkKH0sOFlVuPbEYc9AdMdiZJRaLewbogM4P183i" + "y4n32YdEldNOO2CWe3Zaw23QUIGxz6wfizTRVoInMzZwXUSu3G2OkJLdXaOBQCGaYmp0EOBXpOac2DSZBjdoT6H44VC2kxGJZPVXwjWkieGMUAAOiO5F8L8KevFQAxc1" + "yM9hjcKxfjjpRDeeK4puGeCCCSNweiX6mJxYMeT1TkDuuiJoVGgRgFZbD8C50dDGCkANlYCOtxOkbHJQE3bTNkkpoDEPG8MlaTKxEDdaDFcvojeBDT14WdB7bW0UOUnx" + "cUD1w0KvSGhN0q2CceXEiyURH7jlHHVeitZuDC6XwSCb7foDPah6IV3jiCxEJAydBhoIZUkQU0S09f94uG5eourmWN7ryDSlCQD8L7CbXV9E43dRyLMAFuqxSG6ANcDF" + "HfbRXKCJB20iD7oqjxEmCJ77L5uh6rhhOG9uA7W5WyiFHcpDdYGukcEl2A07gfiJEtuiWY4PgC4sBRx6XBH3u2ZSrXRtK7CJV5WCIhGc7TPyGPtAPXHtAkLvI1CaiNeO" + "s5XKBOushli18MMe9Mr5gO6V7yREIUdqUHaCywUbbai3R9mnGTfdKYpxvCiKx2ED375eHgmJZeKmeMwgZ70IP2xC2sxLEaRNX9Rbg0UxKaeDoZMlAK0HkBbboWdyChcX" + "LQK4vPcFUfnjUEokfuxRTqobNaXz1rdtN7OqlG2Q2xGkluIdtoyCORlnMinowepbDg7UjNyrs0SIP8XKLvWeGdNH2UJyc7kd0SVQRUUq0C29lmGawQ2WwybPzWqODKOm" + "pK7AFG4kny9nBNEADJxMcERvhKANAFldaBylGnuFT4NpaSmIlB9Wd4dFL2OcMNYivVkiDhZ6XZSgilmuA8020XOiDxiuAJqBGIXIYpRlDKea8DVaSaaxo1OA0KdCh8OK" + "Pcnu1Qw0qnYsCc2nrHQYmCQGLBjFnlrCCXgTJVvaJDh4Fp5A3Gp7ksrQgJZDyOU5KG3DmXaiebYB1LbcmaQ83XeBjxcdG5HXCpuL4xz0z9ln4uQ09RYD51OxtjVqujQG" + "rugxudP6uCi1NHxLzFj8Oev9GOGaVu185oRGv2dgifnxlBnI5aUgc1URLgEMkBxk6XeNwfIXkgSAX1iGPhirkUFhXGxUkxZ8nnGBD2Vvil1qvoi7RqNYN62K4t2qAEUt" + "to4HywGBYLmDWUb88jfm202KrPMu7wQFSTzx2RsYQ5tIlF8A1y9LOtELFfCwfyXSzGkFGAOXGCnpvnkL8lkbbwlMGnN9q4uAATdRBDWODIShjAQm1CbAyMhmSj3bImEV" + "Gz0n5HYCHdQNi4NGdCk8mMhU7jl2n09yWtHs4ja8j8HBD8oGg1rmOdQrsiyAStlzf5sK0XQr8I7sOkzQrXAiwyJ4dtcSnWWlTxVH2rMRnI9fGGrB5qEaYIgUcYXbaot2" + "T4yXMDlRbKka9coPyjBpsB0hkPLsuiwxyZPuehwyhbMmLTh38ugoxvCVUkuexNJgg9SJemdZCWAeO8kqPmPAhsKasUVx9KBEknh8Te7dgfCDkbZcl3dv6EBBfcf52Y7i" + "L7W92OarW4c9XDadAWCybRdLnbVAWE1bf8Yt9HDLEsHqnFMbF6mGzAn5D2LE94Nbl9CW5hYPMgpEsmfwrSHFQe5aCIMeLxoPWOnA5jL4Ej43XCq0HONY4XSyUbHw7Ldj" + "tTFfDVFknKXfS7kOea8BYe1IqrdHMoqnogFbXbJmDCScTV0sYskWQfdNP9XsQ5tg6ZuYKThhq98NHaDh5nZj7hXrmZ1Qld0Myx3gxQ7EzGVVr4TNZlu6nua3bHEBSReR" + "sz4SNKSPpAvqPmGEcIvmiOn1EOKWHwVxQe5MgtoWd4MsfWuLMRcjUezmWO7tNXvJUD6e9SMC1cVbRxIkQCDB2SDgfKq1ppC63vBFaRlXgBSM5yWuoItV6gsjstC513zN" + "45QSfTWnxklurxOyG5IUdp3qTJYOOuE26kxWAHSombL9vJhDKYVg1nDht0k1BYgrw2HDr9cFg9YrCyJEux48RuTWNRqhH3Jg5xqB3M3kgo089OqCWvVMJjFrBHMycp6W" + "qTb3MNUfWRX1xf2C1LtnCCy7iI0HXR5jxVjh5pNN6QTyP8wCRuPMemJ3q0f9n3iKaSkcuc6ea59NJn01DCESqzHYBVmqXKulz51Pg2zLU4gSN8czxSKbU6YEKr7oTFcG" + "UUipsdlmiSissjuDIPAJrWJWIbIn5CMLXpkxKw52R8UqPcTsqZMzQg83bw9E5WyBfA7hFsWQbIpkrniFAIS3LlHrdllbZGNweJdgD0EbmdAtXwWlN9gDtOLhlAn4M7FD" + "z47TpfEsbS5EfJSrnkrXldhVaAk9giJZO2V2jBbkT86HmJXKGMcVLMv8xyzpXghaGA9M62jRO668CUslQxQ2webUbU0r01GUdekbQuiVU8ogYwZKYIm0y0FegYeUGIWW" + "4FaFYaQMLQPwsxPdY8qTURMWZk0CiFOqMTJtEk7yK4rMadtr0DsC7npjUX2IKWh6SqSAAOnt9HAVf99vWoMDRVrNcdYQTUxE4bYBorVDRk551IGAB0bkSIQ6wjaygebb" + "UAGs8AEMZKN219QN8MWEAIlO4jceX4DrsjcjEjhorNnCOtVntniOGIWPjJ6wtKrMsw2LvyPesXWPZHXLDvEw0ZT3ET01WsqUxYug1tjiDmhUshfHSAdeuqGAfOvdLFbn" + "M6YWrlbMKea6BoswAiqIwMebilCWYgCzMI5L2stawVspDuNAwH6RiycHT72roCZ8aA3WrCGkKIk3Nddmgm3wZGmFIX83QzJ1Ginj1pT8Ogb9xc5lFsJ6sjXoCsd5zhdK" + "7kOrr9NCY20iB0gtyONYmVTOYSzlAf60OJCn7UMtY6eyoFJt8oNecvie9rQQPa5VX0eBBMcqFQFctMulc2OjPbILteMenOq0K0yB9pfpxDJ0pSbAByAyIb3xljNdOmpF" + "Ft5oT5XHFZATYYD7e6Nl7kq3eCcrxfkVxBD2fi303ap2V9CzpnxpSQZhYppRx3xIyEYzK9jKXby4lc0JQirfPz9C5j8W0OkUEt6iRHMZDW2921qf1JmnS6vEze5aTDco" + "peP03B3kmMNWOjiVO7sS6lVbPJgTVfvgKaWJoG9YqUwDwQhUMuX6Zj0V5s5Ofc8ICcu4B6EcxCJDUdQ4cqrb7aplLA4PenTQQYMth0hBqzYADblowrAZYqxhUyV157Km" + "DPS7CyARf17LdXDzNbcaqaleILwp8vhOGN4pBdFIElpmiN4eEmEWgisUb4KelBuRaFQYo1zv8d1sOE9pOaTx0R412nh1g7KZS2Zu2HTEQfV4cp3WQgluSMAiDWZAuS5r" + "w1Cfcfl1cQ0zr8LN2ih356LGAeozxiCzoifO7t5CFzrTzrvoLWz8OxvPygOzstn6xWseCF0dUvUxwcYsWWtBwxhgTI8kP2Ysjm5cpmFNpMqJipZqJhY0XoZE3LOoht1o" + "grnqy5xgaJywZdqy3FhlzFcEhhSn4u4aar9bakmfR3dG2gtLnbdQFQKkNK32NhCw1FBRE1SYVRQQsVVeM33qK7LHwlKu5Uy3AnSPrJUXgIg5JWNMX5mRNrm4HHDLJUYP" + "rHqy6VFcESv64llnQLrDArSv9Sept77x1tGDisESVpELudujZEQqhQtYOheSd42c8lD1KmDrxLQwNultwAsbfAnYgPQyDgeMLtvRWBO3KI5bGf6pt7G6WOEbvaawfwtr" + "HQBGbn9O9JvOf8MCgURM5YiuBXxxntyw3VGMyCDNWa1ya74mz4qu03mFqswcRUdRf4AhHeVotWPgj8GNAC9aDDyzGoOuXTiknip8GfLPdXqQfZhnExnZPhUQEx7mcTzj" + "4r0NFFXkD4yvuF91rehW5X6d0dLBCNO5vQCyG6ddb2GeLolmoklb6ubsTuF6vc44RaxtyNy93tG10HTc9mL4AGODkKlYKeZTwOUdeXZPth3Heq9A4DmXs9pOTw0VUXMH" + "TCvLwJB4RAvQltvgTnA7BTL2sVf7n1mhCQYiyN9rthU7NsIPDMwuTpAhy4ihfBu9RCYUVKwfkQy9PZlM97MylnBn1YLBQPyX26L6aLwYyWAt2A7CYhgrf0XilTcG5Gs3" + "wpfwWi7TBJYqiwPypz20F4Lf0BwaHnyQkIa55yduqWljAeNw17TeIVAbKLKyvZNDLB68rGf5wm6HUlsVQo03ZS5hgUhMTomm5uN37rFCQQ7lfmc7itwXszk0X4ZebpaB" + "2BPjP3K9uNpcYWlqaaKcIcXSOEc1eOBSI6tYML9Dv7jY8wvrxd9orJLAbaxS7REJ2WbF2gzPdg5vEnnsTXIGaczv9XRGteQZTM2uv1yo9S4une3RqRdVT4i2nsdYMIIC" + "izA6spPr4gdRbd4ufNyX73r9xHFHx5rBBv6CbjeolHYM1cNYFcTAC7xeaDYZ5FcFDGQgQSG8RNOt2WvXw2Od3f9BrLDTkh7k8HbTQlyd06naj6hQHKewfhC14HWFvNc6" + "udsUxYZoKbdUh5ymOTTunJpAB7urduDW903cJHmMCMbCStr7hk915ydnbGXYGxDy0fWSwFOyTsXLaULk7l0VV3bN8I4rYQxBwaNEFHRjvBlop664rE3a6zivJ1XoEwlD" + "1WcuddEVCBf9czIBmqtS3zQiNDO5wYbvCSyc3yx1L1sDdhzUNrIdVETTWL0oPoLotwbfRNiDZ3yVWboAqjvQSdtV89CVMpe7UKOpQCZPpGfjS8jlzpA8SwTuuWhQe2U2" + "yB4uvkfv7RolUiQXRUg2gOveX2dpP5mvPb8Ag75ZoNzurWIwVqQciN4H535FB0dAhKnBsRYd7H3RGtcO3pjpcQtB8xsSg0N9p3DpX4NrwETXmxF7YXSkvb6cqoHU0O6p" + "66qGEcGO2B2Y12OmXN9SgdEREUg7JUd0P6JqwWQrJRhAa7t6zIesPaE1lb9XvV0CfSMIE2FxoVIcegHVjxywhAQFQPvLi8mP0XN54kYTMREhvaIqNBMToe8dWr1qainu" + "OKIpfCPdRH7rVAi3ZfHXdMwbi7cmSMBNnRarc3BsryMmXrYy5xkweImGfWHUzoqHfArX1GyTKMrRDMcSesRbPfh0rjEtAKztLT3vMQUA4YIPQ3hFqDfaOauMII4TLWFi" + "1ZcMEWTh6IWxmlt4e5NAqXeUGZvZkEo4QptdhRAyfpg4BadQ0f7ZxI7ug77f0zRwCTo30dWaRaG41bclXqAWyxGorNmmAjU6HK6WqeIWilcGIFks4MmwGeXYUTrMRrYy" + "otltSBMFOV2wBZaJCcyZMBXUWjt6zadMPL6x7OqS6Rjw8pVp30Q0pS6p82AzFRp8w0rI8VyFil3xnSsbVVtjlr1BtwP62JY9DiBCfuIgXMgQwbccWgCUWPz8jimKViEM" + "QG4fYwPMyIB5Xrt2TTJQK1UFXJpR1mzQRVYfXDP4Oi25usQmnGZ53rrjFMVZsjos42eBvq5PpOLGO2Pvnyl7WTCljAdkme9QtjvDSlg9hWmUh7vDSSsJmPDjjVMJUjAr" + "GrQS1iq7ZJDrgkShtjneiaacHhrfI7QdOQYVR8W4g45DrfCSgDYeHHeW6i2BiHcgQrqqDaLkTo7AlFbls1CWG7AWRmTBitkb0vwfbHP2qytYPSP9KtjiKe0XL4qarIst" + "AaV7zODDa3XNdfk1f8CuSZW3jYqGWWfIUjZkZgk07oiPh4p4IiS8kKpv67anIfHj8KZx02DWS123g5VSqmezlbWtLVwz35Th1Pq7X9hV8Eu4Zzp19o6sV4Pky41pDvQR" + "OTwEwsPdHHwJnVJ87ed5oQVKWTzmDxt5fbJj9ENU8nZIdyukZhAA8Px6U248Jrih13Ooih6xy7iytmxa2a7BSBzpEu0IQUX4zAlT4egcvuHCDnk9sqRYYIe6kyhy089X" + "w3AgFyO7bQ4KCZMTtznkXKiapgAi804MVA3cqke1At5tAZqwjfhkq1Y8SMHTrmXE8kYjlwh27ikWGTvjPOLPXJL6SdIRcUo6ZXLOREYiaBiZXLxMTNFXGZEDEorLuAhG" + "hPSaUOilwgF5yUiOKPEdvIC0tbLY5DqJUGmV1AeYWhkAJ6f0nxJM8RjgPwzBhXbp28V18y9BqvqVOEG4yv9MsZjkeSGX6ngsqQkQJwBplUzMMun7qFW7gpNxXglbQ4XG" + "6GW5kEHlqVHUAqBy7l5ajtcPjcUqOjy731YYw6Wec3ETe41xct19yutbtHulPQGbVee70vlnsJ7Tk4TikpsseB323OjFLr9dTy06kwVL5Iao2zApJTAC2YM383iImpW8" + "KfYY0ZabJgJw8cWaWMTkAd2ZN0K8FBVJeVFlPztxAxmM5BKpms92VgPYF7L0bW5RCussRqkZCAoO24RTqMeojRDe12HJu7UVd16kuaspP03KqlNCoFm2hhYCZfiFM8Yr" + "iGoyTYByoQDXbGaRSpKPS0QSNHanRqfWQWiHnaq2gKrZE2LUzhT56pPhCpr7HwHCI2NoXaPWjktBceG6V5pfdGHm8DYGf2Xsxk01MdgUjfCSi3x9Gd0USDAW8DnZZfUq" + "NRMWIZujrgwSmFT4EwcRbgPUdoGvztB4WyvRsT0cyYJ11cSHvP7ArqeupitXkrAWY12ON634f9Ms0MAsCPlOaNQJvg1lqQnsnG7bV3t4ubawEUPbyrOjWkXQ8ofJyM0E" + "sgZCsOk7gi9IlE7h8pMCeIGEKGSXgYlrlepJ73uK24lP9gRwZjWR4139Y3SKUG6zsx4hnJAD3oHgz3poriDwmOYaRNlBQhF67RhSa37bGmGQOk5un58tLrO2pNrRwHnE" + "65WpxGYq8YEZsDdtCpN43gSqtZoIo66Z02JThpHPx7v6X7NB8YNsNZh1pGq7vUnW2EfumaHqPQE1n45A76P2TVTih1FIeInvGU6GQn1eq2oHJ49e08JMJltgNkrYA6tJ" + "UaGsapUGI1zx3UtOh6MolE9Qe8KvYnRtci6vLjYzH2MTtUsGWZ8eex5Et4TXqCd2W2WVDm38hHANk9wNTKn6uaZWHesGNQDZFeb66K4oWB7vM7KklRr7QbMVX1CfhGp5" + "ZSMzWxO8r4ktIpeBkOfUQcilqLe1kGJEWoFbPNItXHJPpgq5rBYxwUhpZstrwZPu8ReDbDVasmJZ337P5nZ9fVkYe9BvalKCeTEp4GLPcByxT86hpd9qKvji3V9byhHo" + "6hsKqdFjSBzLOpaYDbjXfkahQQxmX6V3ZOgipd3YlQRfC172xoejwPsx2FVutdoyTNvnjzu02SFsqmHm5cZNryXD8llPH23qpcsyJV486ICgKOOTJjPKQrCadj5OxCtH" + "9yBmGax4VyC1p9lYBqdgLy3CKn8Krz0J14c9ihn7PUVjk5mWvh4Z7qIs4BdiQIxEUGn5TAJKiHsBKMDbul94kWw5HQh5Gzu9jNnWUJ70mxEfKJFN2ci0QA9tu1EsCeqb" + "ZXWzf7A1F8eppynqKdPzNx3O5BgN8nYmHwgTSEjIdBEhPLNvhWNxQ3xzCQ0mZMq3f8VRONnGYBjFFAREGj3GyW2iWo6tmWAkCYBw8LAEgHYiyXiNP8fMZILBxyowFHit" + "4DANjLmc3rzhsLWYQy3RbvAukqUCeyk6VVtTrfIRVeYBL1V1Ih81nDHzMiYdZA6CRkrHRJoPijXjQWZtt5LAdNIWIuvTvKGd2ciS78AoLy7mHk90b1HhEJPsaaQA0VI4" + "QJJ2PbBSzfJa0Ke7RgGSmg7jbGn1kaNR7PwpLgwuME749nR0e1tp7TlSUzzigOqqTQDG9CMhFsm1FqnqRk14AvplNYFIpqvVG90bHc1XVpY5HKVG38LpPr1jMKwZdZnk" + "pAWQEvEzyeH9Q6gmu65kGXG9zprRdzdDtFFuvMUXZuxUxj2iFHRpY1nns9Xwk67lZYUf2IUJvGayvxpLIcFqAn4Ciz1DtPGtJZshJGAyqDIQrhNFAXGPlCmFKLQqivu1" + "wNsufmefA8aaUJ2PXys0vZohY3D9LAamJTH9mhrsQbhjg24uYaZEAOWitVq1SUWxt3vP1YaAgMPqefFtQNUMcsWS6yNf2wLYhiZlidLW4AovPw5cnM70uRutNMR7XpK6" + "bdZ2fkCt8BwduEXdRDhsq5BNd26MJrOqhi6F1MAFO24muDZ7nkh5ukvrwplfRy4VJWoDjAc6sNeVT6ait3MFsu1yJc6ZSq0c5cWWDlh9RDFCCpbKNsvL57PkaKTN9SYl" + "DHhrUJY77LFH3tpqEm3awwasnO1CGEenVsT4NZCfsHVojH0fH9RHu2EB7WG6L9Au9IvkDz0q0c2xRWlj1HGW4fvVnHCyaTQT6UXuTANOhrH21RE8e64GVXZLMX4ulEoC" + "jfvwv3fxalXAcak93J2435X5V2ot1z4asI1C4VYRpfwSGtznNB0B5mLgFjMnprjS8ahmKoFg4LeLHEVfQPcwisAKmu4anz7ERX53AGwHHq8sxF3LoovOnt0eq6eQUpT3" + "BrWCIf8vGnOHiJDXQz1avD8hawgn7SdGQ4qnwyeA7VOMxezFfK3XUrQoKCBaLh9LzWEcYoBWJi487mOd4qcfEeiXtcNK0mA4q9rqcPBcGFwvQF5S9uNP0Q2XbezbW5yJ" + "lC9BcZVOTeFAkEE45HyUe4Sah9Q0Q1wdBuxcfqWGftpy5cgxo4s6xbc4rHow6XizsMVUjepzgNvNMo55kWP7imBxyLRopFq9kgzN1qNmAyE43EJyGKSyG0fT8uu6qcKQ" + "A2JUAtMTdwkHpMaFvGuC0QbQ0jYMwDWKfz5bekjHGTwWMvTiBmePBgpbNxKknwTWwVTYnFaLafUuoXfspph6FZ0Tw35dXMRcVK65QoyDaEDMqhRXiQbUdFDA2W7urCit" + "7XFk0f6OCjEd4uXzX1EfRZdh29VW3pX9pgtnzBDQmFN4dadWcNIs6QEtYRceMSPiR1j2YAmGnTJVnQV4kywgi8l2LxVIdQytltz0FWi94Q090UXKtZBOKbgH4fLTiZBm" + "B4KrpBtqwgypppeHp5TR4BFJOfMPMfU8nUzVcyYOHDDDOMJIgyBY7ldin0wLhE55m9KPBu6rRiWZmAnLbR3mjjNnN5Lfi6iWZwNlI3O5yAZYKK5AJRixE11Smgd3OZ6g" + "dWbo9FOgKPnRFu50dAzBpskGBaBgENxsOGjX30Op9Tn69HekRwzgQ8XAW5KpWk6Xc7aA2qN4sWVlPniFX0wcRf10iXbDHl6RvGcEVc4Ieo643DSBp8Y2Q6W3bdKRiXQS" + "sikad35thyJBhWwrKmlK7xX9LkDNQnTrOnvFwp1epZoEWpEYyMk2CdkpLIv9eC7P2Lcdqi55y3AvnugfUmDz3AdRwsW4gW2hv2dU7Rzo2xRuvgPVoqCFawdJ9tMYFOSq" + "GFmgYHuIqTgElYhOeaq31CBtA75ywQQYIvGTF05u4rwlMbm5dQCu5zDQlTUeWn3SZQkSeWmdPNMqKJ96KyMhS9kV0PfPNqFWzAhLDyNh8QQH5WwV2FsXkXo8muzFPfn2" + "yTCbpDvYf2NbFUw7IIrLj9ujEtKX3AtBJmWrmlKwsoxCiL9KBqqn9pWzHYgqkr4b6z4R75AI0hW1AIX3ZdqX4bRQrP1pppKqQdfIwuRFVJVOdUJ64DTYacoewHOiZqF0" + "TN4kYfXJK14PJPukZnGUB4QsxUcAQIiB8z5kmT4u7y48IqmqYoFBeyec46ebmALHgH3WxUYAs9VkfaZxv3X6dm8SJiQy9RZimlChTi6ktXre24PIySvzzNCrXNMbMt2y" + "z8F18Crd6zhVqn8QbM4k0c40JuXJhVmpqnCHObKqJWdhW8KGpBxH1u4a0SE98WikDg21Pli8qkReTAZTbibSP5EiXMkmGiom56p10IjCZYKjWtbETmnCvGhEqvOEBwzJ" + "RZECwhR6t5z0uJCTGRqba0bWGbYyhODgqyIdLU43OZwcbTa2LZJI116uOlIXX4b5qOTG2OGOSRPtkYDA5IP90QXEcDMvBWXrpLAF76jHwxoklgFTq8aLi32aZ7Jk49G4" + "pNHVRW8vgG8AzZwtw9NsAqlBhg96dxyhA0SbTLHT8sfvZKeiT3uGDnjYnsFYXJOFyifnspuO4nMCRstVdRet4NUY5pFC4j9vizSCfiooaV1y6TjzYkoaXtvFp5v6M8Yh" + "vD59wmvNfN8FdQp0YhzMuewhifqwnJtiDHuyxuCcPjW2XrzIerWRJLQ3QHMopK81oMA47kW2GPkUD8lmLUK8kW6kqhbcYfKi8Xnj9gmhyweIHvjpDInoWXc4qp6IUa4g" + "xU0VWRJ6OpgKw57HUuQelTZoh49b23UOP4aenu2UrKq2QUQtKNz117It8di6SUz4uw6YgvgwNtP5ozbkvi2WlxcYwnLFW2u5vxvXbPS7uznTrJNZloRePsThUszZgXjQ" + "nCtaSXsChfj3KHYG1tNgvfsDB9ppI8Uy5Z8t0WmDj1SSKvcyRZWyDysDhcZ7Vt6JjbZHHL7iNSuSauMaHqNnEGj7p6M0Pwp1ExGOAE3fGFWyDgqlN9iWeQwqd0CdFCie" + "HcShJjrqsiy8BzlrynsOxlP2hzcfFZL9IQxWFlBqCVWZA5lyNoRY3bHNmHQWxxDYRZvVD0TNYsP0nDUJFB7up8Q8fqKDyKjF0aosm8qsfBhMx5GQbn8UgWXYg2zzrxS4" + "ntaqKmha1kxt6Rfx47788V8EGyhn8CcRZ7fPbQGhWaZWkF0fq4R3zklSts0IgITXjQihdsMRM9DM3y0ikSsr53qLVNciupUnEe77U6u6soFkeIP1zdceopnfaUp8VrEh" + "tBf0C2gJ36Qj9SmDNwhKoImuXSydeF6J3oQyZZuwQvoCfX3MyM5TOwZ4NDGuKAhGTGzDzoxz1LMqGxY0ioNVSsiQBa7u82fWg0QI3SVi8fdTHZbB5Lt2poun3RpRgk2z" + "KTeeOdJIoizow7EclBtWujO3qYecFFywkm4FwIcyvL1jXUPCVTGTs5qszA2MD6o6fHSx0gOWdKDAaLCAhQY1sUIR2Xeig2hXL8ge438Nzs0bkPRCDo2uOPp8USP1j9yS" + "vrRiLf7XKlSLuzPHuEAabOQuefUfMX8cWZYE1bphkHsBRtYMNTkgL2sPZEf9pv7AutFAgoTU4CRm9wkXqMmSKA8o4K1u4yN5CIGfboTGpovRYXGudBM0tmewTjTQlUgd" + "Or0f5OkXsz72BriRmK1Zg67yTi54Ec6n0fsFCRxeVXg1U8YrfN5eZzYrifIJWtRslVlBUK2JlOjiLaRVhIghM4IwzqYxqtKxivyeRO9upbFv7FPdAFjwTVrHkVtp6qRl" + "Bg7xjE5xE5hWp79SX152herINFnu85M8KKEQP8p9SKzK3l3D4N1m2t5Ur9uUXZEbLkudWHPhsvyCO3AvzTFKw53sC6fhF7LiotfuKaxz0gyo24UCcBwqIGTugnrLVwd8" + "xWoLisWhYcUVf1M1oEuT3AoY6qAmAhcjjBagAMmn0eG5YLl7dKCPfZyjhV2TDcPPXCIB59ADflTPGMNxX2XE0xDuGSV9yxUINaTGKjj6B6XE2j9NMQHaeqkXIacEIGgd" + "5R1pKKulhu59OwLYQkYkNQ4XpSH53AjZfCCLQcP5BPmlNVWQj4BldenBDSxOMyVEjJMyuRnZUsaJS8t9OzD0xDT9fBXjW7MVUS5tqYQZvqABvLjPiYtEjuXfsnmGpHVu" + "b3DXxzwqRvgvqSvLupHEuJ7QKykRVopiBAfC0eoJaifDP5eOQQVCXZTX9kxKNj1liNwMucE118hnarFFdAyJw4pZElOMadlXZgWJrJyINy8UU0jqmVfIwzJnYK40LEnq" + "qQC1z8ZHBPhsw2iGOHU9OK0ZxKcu12R7RBvCiD3xd9x3IfhAWF15ahuHucCYrub3MJqIL3qOPAeQ7bALAhzNrzzaDSl8BGnTKWCzJW0gR8B3kgZ9EzQOanptjrKFVaPF" + "sQXDy8h6dfJH0J79xTaQ669p9TxC44YWIGrShIEypceyvQYNBdl4Wm841dki4U2IdRxLNoHZgTh6W6UYHCSuhUXyUzrJfQLEIS1B58FcyyLH28TH3dt48bDsucEuTpHV" + "guRXXA4psiikXZyWwng20SUvddLEPv7uv1GJrZx07ZQbhWbAO6HaTsvrQ0t9zxyxJKnbtPOcmyztcEwHSwZ5640L5fHbhq6E2N9KxvzLerqSs9mGClIEdctSmCvnrPHZ" + "cSqsEzhEYTxyvuImyelXyYRXw8iudliM5nRirQkq0FsWU1CCNXaZDXp89aa4rHCvPW82yPQuE1TFy0OWFVQqQYq2coJxoeixVhNvF0ysToo9P1pGmT7lav9pAbGb0ZeP" + "YxkgPOJBatYPrbzjg2cpFOgBLjNF4gvXm9kkthr0vBGqaHQiP3k0prEXOVgDcbd1EZHY3RLCWfN1r8wI33MAl0Tttp1m4pxY87uC0uRFsGpo5E1L75XdXWPdzwn6Ptoi" + "QjTvaaNCIXv1JFovgwtFSjkAyOl2IuYLI7waVjc2Y9P9iL50aaxHTn4ylIPbCXYUR1sxINNkyxvbKFLXLnPenDySSB6Kaqpyva2lF4dgK9IEgdCp3HEalQNM4AoI5WUF" + "waz8dtnRV9ENW7VmFjNaQkWALDEoMnF7dJhku7vzQxpdnkECgZboXs5K5jYteKFZTKLf0dp0C9Ixp3HxeiKIMjpydcg8jjVam8v7ddikxj5cjRyGaxKUTMyfwCOSKQlh" + "MSTMKdTISawIMQ5dKslUzVvNZ5Mj3fuWzWYsQfQGp0af7Yjpv2tqAYdvUtcKzWB7N4sRII1HzmxzuydzURtT7CNr3UgPvuRQBwZ6zyQqQ1gmWMbCco6co5SdZw6zLeEI" + "J6AxwoGs7tuuCmrvzb6oG5CFQWlnqMWVgywMEkRL3INMY11CMWSvJvfqUnlk70JdPLUNVGRw2SaFvchUDLI4Z6BnjeT0TYfMQ8WvER52P0EKmM3iwNhfb1JKWXFAoWrJ" + "fAjtH0nqMuZ9EYU2B9pgoEDNE2f9mT4S1qLTaykXz7nJAIEHakHtJXmgxdJeTj5GqnMm3vNBr03rWSQWZJuRejUKOAZJpuUrhSxVZQnKoB1UnD9efLgw9yzn2MgzAvxP" + "lYgu65Xtbb8b84JqANEG9geYlhgmXOWb2Wr2hwEW9PFONw9d304jdcMM0fRXVAkSL81am4XsruoixnoiEAi5FhhoNviGpMV0OzvmjeIGaLUhLUKgwVr9SzWrsMLiISRk" + "L4J2dJvYxG2BKWS0IBkcdESLnfycrMc3cBfoNblnxwEv95RNcwrBDKYW4Xc1cS1htk6a7UFlkWJCHmyQvEfQrQvAhO4Kx6QiaT9mdcgerxv1KsF8yxa8g2i293yLhf9s" + "CSPae0dTUaax9kgxICExaQN1TqDBNQydumeyJrZMuyakrPfg7DSefF49H5FeWt28Z5HqKYRxS2W5hMB336yu1H3lDkooaae0aYYwUk1bFQRigVNrS2XICnBzkFpyofRb" + "4slNQ1sYxB2eqPi86utVpnGGJOMicJyost7TwZCmmPUXpIcSrEqCket54HsrdwzeXULji9XdAQaplheEzmheU0588wnrNEArl5KdNjXcZm3e5rhOheavD7LRvhYw1OZZ" + "uyzmjObnQ4EwONmeX2iTDVWX9FHbVTj4ACQIw8LLPxhom97gUdzWOWazMgEiuDhwUiKlMc3iDAJaOinVHser5U1pFDt5Z2rnrkYqH7xDlHNc0Aca0jIc0A995hiyqZ05" + "dhbkk94yekXwrDd0wEwvafP6M0PPgzZvdSe4caF675vmDpDnJG3aTOQFVzLkpeJHvaLZX0gxLS7eyTtttBMcaCpRnjIYAamsPpgYc5TM8hxSBZEHirAXC9Lsx0zaOQVk" + "ZMUr45ufrXE2Eegjm7uFWtqdGPbC5kWE4klsRwfQvDCRyP7VFw8oLhOEECLKcyfC2B1KrL4gE4dilo3gIO7QRcxpB6hB7LJ9ugQOEQl5HFa50RvG6dcrM34NQWQSnrvJ" + "PRzNNPcgve9Ws7IjkO48zchcYMfH4meq118ZVLy0C4lYMPfmPOnhpHkeX8IVh6tFoLBvV1UDjxUAVkVQtooKEwSguex5jl5mBAc1M5VArTI6F3Df5rdg245BNfg6ZyMR" + "cHYb6hfAIUhIoXDPmocIEb9TxYFdtA2EhWRqUlE5OWuvoQPPulyqJHHvpUSu93YinkxVw317uCs07NtYpvumlD7Yt3yUw4Xtb3eUsqs3coSp1EVJLS50b7UXlBQO36Kg" + "cGotiG4DXEGP496RNYESTN2NQRQNMQ7va9zb8N7IR2nXq4kaWAmdcg2jbuilyDzHYIU4o5qVpnGjO1sMjwQSaGWzErDFp2TdES6IZwhL3VNMdzjAQPAo1TuadvmtPHXs" + "aQmmtWh9lV4oYFeCYU7dv87WE8CsGW1h1hPr7C7kUYDUo6ub4FZ8AryEsxnNwfvRAa6Buh0VZicI1TGoPPHn9e1jaUrUxmSGAx9N1S5jtHpLSwXF8Hc9vCQVE5jIRNaD" + "ADuqCT9jkSwdishoonjyGtiRIYvcybZL7LZfoQPcITvkiqeNsAGOK472jMBBDKMfIkmKiLIILfKtafpRXIvUJI1NWRkogJS83R5VT2NPIMXARJ3c3cr6wcQFS9SxKsMG" + "l9baQqWu6AypCerzRxoENtF9Q0qOgAsWp3LEmyvJs7AMKZvCaXYaWAyBwXj3FSTTbHn7ed7mDJZIzUaeqeKFZhPDvZtK3ZWuzI7BL1rXf26YNSA1EJpRSEIudmDWgSCH" + "ucXfUaRL9cO2DIl0YIpiEdj6tP6Jnuoh95pSvKi4AyPlLaRmFLue1WAaRNPAgZd0Av5mEb74i0lNTUbsHNEx0QblQ1vNsjazsXtp4jilpsxPdW2l1pcMlUjLGcppwwyl" + "EZCM0yA3la0x7Zefz52zcpjIaWi7oDWSF2D7oJ2Oj5mUWNjwPZfr6rjWDIDsGW7DfSDEPJ3NaniPpJ3jgDNWAOxgL9sqoweocHxZ4fXmvobzWHftf1rWQzKd1UkEdYsz" + "v6zryHG72DSr2a94k8BingSHpof731IRgZdkdKe3bLDjJENQmwJDxWsqOwAuQqhgvZRfUYhPMPyiH0uP4PsJqdzR5ETx1DwxhLtHAieabptDF2WXuveLQiICfsVOmZiF" + "ghTaT3x0uAc3tlgj9LON6gSznLX3yNuHK9OXahGlgE8JXbsPfsNQqZHX8CT4HlhhWDyS9dKExXHiZpPUdfNTzQUyB4hihkRyDumQfGh1QOaXyxZdB2b486TjFMWop83I" + "4ROn54RivIIaoNlt3IYothpkvIw98MUuid98aNxL991IsmQYBirpPatGLuIDwU3B6wQRmhV6gpSBgGeyb2QNIFLHN5yGC8UEtv19hBrAe9K9hmH6Oa40J5sf32M4n5aU" + "usvBeI63UaLCcRWIR3okJuZnN9VUTxVeCEYHj0HQpgdK7JhhjqTeqJ1IHIlBjzqda5sMPgtEpRpGjkujgr7yrPDJrCPPBOYDT4O78mMmZingaMukU4XOtVk2TM44JrRR" + "WxyLYvmHyXkHmlPr8ndqtOR2nCEmAGvxixCShUhpsjXdV5Kyho6u7eFiS32HnpMgMF6LXNBwtzMEfbYW7k2GrhcLVgSXyavdVQ84JplceoWIohxuxRZPQEMU2NISAasb" + "hJEIXGutTDwAOYFwRFip2vx3frvrR8i0dqfws0vBa6u02wEs83uTNfySIHnFaMDcgTGMJoX0brQdm3X8QlVZob3O2E4CLZsypb9uBOzRF0FxAzGqJHaK8zGEM6xzg9NO" + "jrmQJmUtnq22Oamjk1JxAv3AYo9hWqf8mRioL2jktOMzQBWWvGLVzmSSkfGkv2viASCw85MD6LJtOQEhRSvIqkxU5eXJkarik7gSp54DoWFlkB6777ppO9FGO1LsiQdj" + "PAHEe4ugQCChaFoNKpcK6M69TEEly8OstpsaRCrS8hAdrmsf2A81GU8CPVbCQAOCm8ZBk37JtbOhlCVHCPYMXfq5oXdMIQELuXuJgSOTX7bxYgXIogQKgBpRgRuIXwij" + "AqxJcLmmEwxATfdlJkgCkwE4dvvmskmAqdBt6IOMoFcgmVKQ98p8BzzOAH4TD1mCrBUrXgSJvm1WBtmgFRjtQB3dPNRsGSIssEZzK5jm54V4GMvdIFlfDjvhc3nfByiK" + "oD9p1QN3sooRVGlrB9005zAtk3yk4ww8bvzF6cNo8oLMBpfCc5X0Z2IaCzIISSc1hpvpQzWAkLSAYDsMqd93iBi3IOcWMSYJaY7aMG72Wtt5JVdvaFx8UCEZein0aoI6" + "G02QnWSwIFCFgZtX2Uu3cCxAuOH7GVE0FUejBhlj5QWxKKykCnAh39lie3em8OU7hXyT9EYs2bKNSLtRUE2ZOW2df1dmuLAh2p7yNBix2cbg0qBgB8M6DMDyEuYato6v" + "Bg8h57gfxVKL3vZqvBXcxWeHsVv3ybHb5cugbmqOnmIY9LZY2qt0c0ZGWB4Uj6wNWPYUx2d26MK1Uw8yMyVvfa1CeIqQya4zDlFw4WQSGBD6kr2JDAOCyYfMELu4NYIk" + "Im1LOusY5GkIzcJ0ukSVppL96G6JkUJiX921QAyoYzZwsxyNP7hx6lgHHFKOoGS7oqkJES4Pc8xa2jp8sPmz5EU6O3022qM99Td974AMrREgCH36TzeNhT30evp0nXf1" + "lVeSTRN5T69XRhdsl2a8SeypEOI4mMCV4T2LC82xTbPuDqU0qhiVdpLsGbHjT3T8eJxCnAuwqYh8cv4umxB2ImB00r70S3F3Y1Fxv2tK7esiX3QxMgtE2B69okJCnfg1" + "Xj5bliKiUdKvdPSCpdWFE2X0I3acJ641mRv4J16XY2Eel8bcEIC2iV56gJ65Zuhm5qsXwWkwfIPNjbqSI8O5DKPSZQo7mYZI8FE6VtPkISou8PTAywx1BtW7vWAO2Kxl" + "iSiNWWRoMm9JOc1vCw2zcrhDJTwDMykNoGZDJuNsRpUBOtu4f2T5H2Gu4lPexy8NqjabTF2PX9ciFau3tMwIXLQ4EJkBO9RXt2rcyR7RC8NPVRMLg1BeljYLnk5oLCoP" + "z31StebrfkclR0Qw77Qf7351VIKHhruGE18XJyaeele4WydrWyGKM3uLQlwtwPX6aVOy9LOs1IR8thrFybL4HE18UE1MMZU54NEIc7csDbC5aRGT1iMoHZ24PGSJWKiH" + "aITR7TzgHMk9eBuGaTgEQcbSC40M7aX67gKufy46QK9e3K0VPOykggTpPTodlqlWpohav5H2ut0Gk0CJhiDgkQywTbHkRiuY3kIiiJR6w0coWepgQjrFNPOOPv7sMu9X" + "0bYmSwMAb9ptmMtguGgpRCzMCNfl8XAiIfTphNcRbB73rwCGdqvWMDKIRCzH0KFmSnu6SNJloKVwKRGzPqeFHJxJPLY6Aunsp4ok1M5z6s3lZiRdDFuoMzbSbLyxH2gK" + "eJg0NUmN3XRZAdhdllM6LBgYoKrh8rVT6MrblmvEa2ZfZ5Nf11sd5cRF5JFx6L95spjKPseRItiBObhy1ej3JPQGyN2TPjKmEml0EaeCS7Q6pjAq9hoPFESYdDXYsHti" + "CcO45m6SnswJOagwussDoCR8OyaqNMWN6wcqhKJ3wTWqkhN6pKnS3ivdjATA87IGo6H3EFJ37wDujeJYvB4b8VYCUXm1NrAqRfEtIcIqK2ikPYsCPj1eDYD6jfitAIEs" + "DIqZSdgfy4Fc5L9bh9fc6mnhBgKmkraLs7bG4tuV2IyP9GF81c0l0tAKvMb890BkkTOOHIIylSVDMyOeWzMowIM99lx7vJowTSEi9idXZMfsIWdTJvBRlzTHNioA9Azc" + "09r2bIfcSIJE8cNeMdfIgG5NxZ1RB4rt5Q2g2HqegJA7GRKUiS1aQKx7LNhVeUrwgughToe32KjnT1WnRc7sfdTxftLepWKSS6ShDlEH0D1JPh6oQcN2YMkE4Ldrb350" + "dUbtFInWXmtzOI2alCfQcy9PcBb6CuhuZuIVpbokV7tMJTCkXbLi1jZ2EIG3kzTlk6jBFCFcDRvKv9wWOV0XTU2A5ATFK9GoGAiy734Zi3wJ2ycomnSvWmQcs4TQ9uUj" + "WneWDGv5IiEqPxsHdTXK7DxSCzdbpuBAbkqzDKJa4KugIz4cSY5kGV3gUJLi6RpjamZXJvsW3BqKfC1BiQSLCey0ezVZfqsBswXyMc9Jah3kIgUp3Swv7EfS7DGrESvF" + "pOqSvhSyZIJEwTeqKC6o9caKIuaPHd4eBnCYTgo7EaYojz8g5WWnm2bDVrFKjoLwj9SjhGyRDKQDPD2p21hM5GrNfLMEC6qm7x0uhO62WMyuN8pd058nHkm7YHOXH3pJ" + "HtWhcACLHpLrWIbIVvep5PSn5KLUoZI5R1QjTX1kkrEm9iYKmaRWxbkiD8rLpyQJWsYA112nlacWSszCHf9zk4e2sjENqtPBZA7Qhe49qU5J7W3jaUUK3GNioDjscDS3" + "OZlAUNszMJDM2DYcZZRYM0f3MdQ5ltYdwMifZ362yqPdI872deOKl2lVXd1biT0zvR4JHY7ptlgPnk6XeDjJkaIFP1sVXrCmugACqCNbvTihHKE6X7Khtk5yE9AnLEy7" + "7xXY443yWwpiaznk4LCdfNoAZ3Pt5f2dSYwHobmHGGCHFsJhtUzeHUzFRMH4QOZbG9nyqVA209G6p1UCsGoHFuFPOmKJSi6GuAMbV3T51qGIWpslKIJaVUmgfT02fRzg" + "z7bcJokfJ9gRIGoJuK1npfPJGHAJYPSE19cKb5WUNWb5sQaKPO77xkzHs4xxCsfYiNMirHo9whRX35ffvOBx07ECq7jEYas1CpJL4f1DEdWogilu02LsrdVNlk3SPe5T" + "IJhp8VPBbvHzwx5IRJ8Fk1D0PnkyN8SLz6E3G7ysq1JSfbE4Kibc7GngjnCh6EAAHYhV4FV9ta9NU8YmbcBnsE5uujhRYoaefOJeLqWDiueoXrwzju24WfkvhjclmqIH" + "qxr83uY1orBxUaG13PGwFos81KuSGsK3cLgca32uwecM0qZBlGoh0ew1opSdutFO5sFgVkJMMvTL3DMa7iInRDngCPmtskUiWf7k6NkzaRgHbxn3ArFULRLZHK1Pf8AR" + "41p5IzPV0dqL9Uyslbj7Lu8jRepBYxZgRtbmrZTvRVz2Hf7eEiqqr4iYFAVR1NjBVtlX0eurB8pN0mkIiHOzYEkPG11zUaXbGsUASrnfnad5vdkBOO7V6hH6Ppmk5nRb" + "l5qRtVWQVHBA6Tl4052QnfM0ZoCArWhzaW7R8pCJz82txSAmVmjjO9uURvMBXz5B7xUxXMuMpw3gRb8Xb4izMjmPLGj3lXjC6rGqiR1ncQT7wUszJwY8ULA1gyHmorYL" + "46biaWPLIGcyZXrxXRaAWSt0eOLzXRO8oEIyaMcKqcEI3LrIUDslHJBbyBn9OxM4k0FHIiSXWco6TXMGDl7O9movxhXoMe6EfuJbwID7ozjZcBDdl6Yd1UloBsow2KgH" + "PXHcMTOtCgxNuPyubaoEfZvptc5VN66YX6ZXiudbRs48oMkAWy6wnxFi6a5FbUbvevXyx5PLfZzHDNRz3KD28oZ9mFSNw02zo3ntIP0VOygW54YBy12hjbJrqB8jBuLL" + "NBxqwLvZE6aBpqzMEO5ha557R2NfEsx7ZOiXPlczR0clEo9I9IHWfgkRZNXElAz1zU4gquUe6IjhLALmlHA8xr4rRSX8gQ3rVcS7xt64N9SNpvtzc0saJcfPINksdtIz" + "bcs1xgXNAUbjMCNJk7iPYxBvDemLmbbNHBikDXmfO0f9dhmIULNpXJA92tMJhgysLSC6pFNNFhqKRYaQ4hZcZp8oGnsc5Gi3IRKitF8Z8emloPcN7B5pI3MSvUSXTD5O" + "rJhZlv2BqtLUr2MwupOFVvLhXGbBQDpXrGJqjsvO11OROdDaV9IPEmOqhPT9T5POmy4i7G03mEcYWBQUHQdLnhZCpzonFitpT6nyjIVkqv4e71ZYUD7d0yW1s7bceXey" + "px1ieWtA1jMZBNCigGOi6zrn4yaMg448oKWdokqlmHbyPjtmFv15xicZrZoOWg44d9ZEvOvmo72S8PcQIgzX0Ru9G94HppiF6seGlIzxS5OYwgEHyPAmU0NPaoXKY6PI" + "PB3UD43x7W7TCABn5tv3peX7aeislqW7JbfGoJXbLbjEjd18UTQCXRelhsztDlTfBcDurtOA0R1PZ9UN5FEVPYnLTc8kMqSLb9hZn87gRsFYieSRw2MaKJquyapwwspv" + "5pPASLq0SUyf3jcsUxdYNo7JXfENRDGGS6ggN8ozFOcrcjZv5bUEaA8IhVmqGprp3rxAbBiQOniNgTUMUTeymDuinUcqlqduTDovSYGn2Z0DAorlQB93mXrDwhaQOdqQ" + "w7CfTOVfawz6861qWY0p4RaRUOTZ1vSIffJ2YPA9u1TGr8u7LipVBh7V0M403obLX9hbqW2Vr0nPeTobxQAtfJyjKCAQlJbinaKnESYumf8wbC46jV8Gj9nmekFCSacQ" + "1L5hxRoJYYDPWaHz9RuKX3qwguC9ug5vSVSgJpBLwalHKyQ2NdSnMZmLG9HKHkoOrpaTkXUnlHkmOOEKXuECg5cConzSTaYL7QKGgRSnS60JABDWULFJRpVZiYI4RUDU" + "NeQopH4oirBeSlmtkTw0dIU6ke9CpgMyWt76kAlMWdL8zXhMrA8hVrNscUakFMaigl89nzQoCULxF1OE8yMwCWelDYF6ZH5ozVk7DDf1v9iIfwaayWxTbtuzn02oBtPl" + "c38YmPEn5wD5GNuMJW5iHtrCsl3rMTFOh4GsuhOe2KF5MYd6i7sia5leRta5W2ZC9z1cVNFX8FYUe1YdDAUVd9Y8z6UbdRogOqLF1t9pLNG8PhXZf5Q18a83CNBcqVit" + "s6PdggM1aVp9YZzJmlba3CPBjOjS1FqBnf8E25bAhB2SzyGJ0tU36dYJKsLHPq6o521MtcPdu3dLujg1rEjAJ3JM4LQfBZH2Ohir6c2Erf8aroN36wVnZQhgCBiScUv3" + "cWUekhM6kAXh7Vho11ztcZNXwi67lzOkdqvAo9Hsj8ry8bVYRimaTxRNnReSjGEET1FQRMy0yMHPVavXxEkJJx9dDixwsC0FgpjB4bJpERSUOFuyDaUDnUfmyRVsySdZ" + "Irhs6rxhVNTNsPqww4jaa1N0GhVqp1ZejeRL7WVlrSYiP8CgGEVLXm12o8u0ceJ4XG2W5gEMFvoC9W3zgH4GcZ20AWfbFqL5x5TlIFxUdxJI0lLkntAzf5JytscNSQle" + "hR2gmN8gKHEbXMYmS7vX2JWIKyfENjilpBZpBHwuxdkhqCNZYtcHdQwkDzDz4agcZ7moLkZPEB9jf6HIhETmQ8CtuF24TEtwAG82zitBl9jQaAL7zeNb3ZffQWKAsT5z" + "ytI6keFnF7bxtHsbuIcQHLdbsMBUiYPJk8nWUKFATeMB3CrssazFohsRcCeHbNfP8Xw6HiyuRYE2xz2Bi8TaThrWZZz4HFYFgGZzUeDWLDvwiAlz96cLd879pN2rtBAv" + "YOZGtbSpzXI9o1uzujump3A6ejUNLYZnf5q1ZkJmQhc8P6NucMMidGdwHJDDfPmqz2wHivN6FYJTkjklDFog5rjK1PXELoN5EYIAau4fkzXSQ5h4zG2e3CF6AH4mOIxV" + "G05aJ01KnbEci8bLTcfkNniWuTWsIZvtkb7OSNTkhTm3M81uF5zoDgLflPFASQBJySXxkNTnhzOZdq7us2wrUDZkreGG3h78W5weBRjtqRMNOdQT5JRfqeqL4wtwubVN" + "Rdx36ER6KEEXFodal6wLoCZsfgD3ywpMCUVfrFpVts3t53ADnMX7jUOqbmdG3SCth9fmgB9oMjLErKm263O0F7HB0Ic2tXbamQShfaeHsAJHwtrNj0YLJhApbYzODnfm" + "JCsK3dfPQ6g0Ar00R5a2eZ21k6jb1pf8qj4DUyToexKpARA0v8nKxux1CFP83cE1mDjSu9DJgqM5lei12ugaoSEDOHE5UX3MCrx0OOlHTH2m6j2Nt6vLBbc5lmSRBMUy" + "YzLsbzu5dKRJa5t01QKn6Rjj3PW4W2krViPJyJ2RbMzecpVV9UFULPoDv82mwgTnZvh6iyu0PuTwzffc9iir2KTRGStTeM3YF8wBpEYy9ymtrm6ka1ML6STUR8T5Vhru" + "38ZsmAqZgqVdNFm6E9njDlsTUsaTkVtGZX5Dy0OVHI0PjOlvwxzmn93frhx3aSG2qI4JtJhcOiAnFneEDNgSHtDv6EFHbvX6Z0WgKwnOHVO0DOpiXzuzDXMY8ZsTnBkL" + "AADtITRAZYmyeCz8sZScUHoE3lrcgqomTKqlPyR93SSp6yc8gY7EfF6f63UwhLf5lnKCGmhnYArYFnGhJToNQ1xVyBZlhnTW1kWhEqWHqqNtNKKjiNU39EOJ5U0av8bo" + "lnHXYSJXsESVdn27bDF1nn0UzqJsxmJEXEgSmihVHdaQ9VUPZk5hFw5Grvfj6v0fFj1bdb2B4Pnukq1jCFuovFQGuCfqHeSW8FPA88H6DXqoG3pljEXOsMrpqAL5qMLI" + "958cijCHNz2zUVHamaMfmelU2F12vvqS2B5IeDOGHh18EL8hxTAArCOpb5QnvFA3JliwsR7fpAnBV1L9FCxXebWGTJnNUgHUkgsZCXBj4UASvNCcQv66PhnBK1y408qv" + "vUaYSMdZjRYpvz74RH9rcbyS6NXWSGo1vEty6OBHyElKNQT0r5SqxOp54J6tNqq8fqCs7f9WQkctJGklNpBLT8anIepqhyTWi671ZR25GcftKr3NJSdGRvNWtxh8tuff" + "75Cd4YkUdZxaLiMWLwlR30tFCifvgmsgmLiMEDSj3UgkaGgngcam9gKKum0kQ5kez6tEcfCgCF0S3BPqPbTKffStXFBqAedKWzGxXjy6OExhYCS2MQVUIF22I8H0uSnn" + "yZ3XpB10aUFBzNtgcLAszFG5QN6QjMrptv5lN34B8ZaJblrzNCCpnhOUSoqvOp5a6jXWPQDW667GMl8uwfwjc2bVTwo4BVye8OqztpxxPLoBS4fu6FBVVbWLJ3qnQ7Py" + "RhwVbVfxIVXhHhIZr7jBsiTkEAm5k98vAh5JBjQ4QUZ8vkmv4t04Csmx6GCxP174eSLKpjvnMZtpfcrb1JZeK9qKIVqroyS2J4XTwznOZlDgFpePdEgaEkt3qjDOxBjq" + "hEazZBLj1Z17B6sVBukQKdAgMBBRHR53cbALMTc3ybSLSZFJ256Gue2cFhVdcI8McwoafISqxtrtcWkydIunvGRTkFMax4uJTpGuaFz3p5SjEg11TZi9zbBLXZvTa0Rd" + "eLGmDfMGnVBOzgD2HicAserdFV520iHlmyhDQ0YrvGwoAH3fNd3xTR8TGU3EEHugMS5bxB3l1fnmGBjjOunSIqGCEDnxeGk37Dp6ySD7u32r2xiMSocELEOhtJbWRPuN" + "N9hjQO9BJOhsftTN5mMiQKuj99h2YJ3uYXO7GI5knSfx3qT6ORAeMYb1MBpEQWnn9xYaJaqccNhrTj2RE7DJw4keietODBWZpZntRKHjBja18F91sOo5HxKIHtnenWHM" + "MDDhxqJHLcepg12UmJsKS5SBjpLYFpHR4o7U3b6UbFbwTzBV1KgJAEEuNrdUyuTaBmkPH7UDlrNT6gTxvE8oKzlEwZp0L4mp4HSnJfWwYd8LvQDe1jmzdGWS3LSDjCwO" + "KS6wrCm2cKBAWAuj6dtVSPn4MGqv6NqKM9dTzzTC97HzDV8LVxdhh6NxkkGvJb455CWSy5wrCzwzo1ht2iTgC7yOw3fP22EhLQfJjXBKaJv4i1P0WQfh5P31YV0KlBnJ" + "LEIl0ShW4xDPx610ionaFtDQxQH5wi7P41nn7Hj9i4TGXmGiDRK14n9Fzc8FxHKzTJEHMv5MuTiIyLMywjd4sZmNVXSkTpzWgHdIdCS3Qk2Gbn3GvzxgX6r7iXBs0VVj" + "7wAkW3U4ye7RIORjx4c874hSTg3gRqXXQzuc20cZ9rwFSQRDT7kMpBZVHw029Qe2VtJNb0skNybIRddC6JmhKxkINoyFmAATXF1Wkv2Di3wEH0N5PEPVlKnHggxbfDFz" + "Ge7xaec1mlwFLnmvp7ovABi1nq5AIdBEWam9osM7pWFNjzCmKe6L9VbFlnrtoj1xOmsf814ODblG7jRWPsr5Q2ii7fsrQEEJDgGpUWFax2IohTSGprZV5BPVO1Q6HTlF" + "HnMzAtSycPJGMPgkeG708Tch4sckTczpZ3JAkIxVIUpENwMq1YnnyEjsnQyf8D0Uq7tJv8uLCIOp0eH3Vwb8NkFDYxEgq9Ze7qVMuq3a4Svr5J0ks8l6KQ3a74YCQf2m" + "oO76ZXyy5SJDEAmhnFg0E3oGGwF2WOfRksekLvzV0S6G0nSdZqb8CoLprApDuSs10Ig70dDxDahcRU7tuk31WKAs0NIvmyjuhtQodBxr3L99PHoVrrRyD937q3i2B6eO" + "DRoZLUX45t572VPYLc7EOQdW3U4CuVLMw23N2vjTlvB6lovNT7wfmS21t3k8e0zwlrT5PrYsMOvgyx5NcKuEWG9vLkKd54istexwoCXcfCxcAFC6Id2lcAvt1jH2PzkB" + "a1DcioPpyOlar1ECiigcNJpfunZ0CiPjEnfzAS8d5JEISQZc2Bh589ESkDGjAtz35t9X9BRWWSwsFQoUYk6ZYZasylAVCpAO5xNPtnmim0r8tjVOsw5EsSZOVJJ5oxoX" + "uKdPUzwr9LRa0bxomJ6NDPknDQjvMFQLsNc3xsHfx6kSM92ZjL8XuyJLfiUZ8vKIzVT25OHh72S5MCckKqduYyixSO33lQWglIETBi89ETYdMAu8CzUaVLS2VoIgmmzl" + "vqWGqNfxqNemLmTSqPnHMrGprE9XFZKUHPt0ILe6zoW9ktJYR9CVzEEfI4RTeZYi3SZxxUi3NNYElsU3xuX6nAGEKYORm3pl4U9n6GPWEILLSXJlEtKllUk60o6C36EB" + "863eXxwjTfYCeSDJbzoe3BWr13Q0bfb9aPoVcDF9XGpfMAIyIiI4XS9VSoYOckdgStHbMvIIvXkFEjbUsvqOWWLSKgtYUEwLMfxIu2RKhhvB6lg3BEPturG6nM6Kd9UK" + "980kkAVrFCEd6m996GxflGV5Elx9KVKIOulUahISXPNhAcM7WVBing0kmFt036sVbDHxdKNpJQWJlgcd5t6Ke2GD2D36j2wtD00hZ7mfYzlv08ulEddFRzcOcJ72WpdV" + "T5Rq0OthDAKflKg18qaqDHXtq6r85aMbylzcpFBVVcxrVptBdbeNHFRfpB32mZKT8C9vnupEgAZpCa1tkdgwYJf2zEacAyAlavONqjYIPXFTGdwyn7DWlRDJZju0amfn" + "he7uesmjLQlR5mzvOgSTaP4eSR9xkYL1n98EY19BryiaAJWGw9vcClMhZovGijmfifsWkRTo90tDDUg0TWSLmJj3tl3aId5VDcEe3bc0VzAsXXKHpakuMsJ16VUnRwlb" + "W4EBrBJtjURUaBRY1smH4YdmfFqCbbrGYVoOXCdbtrUkbOXBed9VYiaCnZSlGIQ3FMXQy9Yl7eJnkXzvKq1aGZiEJW4pqpYUcZDrsikpBKzDYTMKn3o8jT1yPfxiCj1Q" + "8vZXCV1jpnipET3ILZWc5knPxy8SewihydbfnhnwBWNEv9a5wXOcHAxsYiBgWDWTdczoMw3h8ux1u7jWPP84sDrJOjs9l2edYz7fEvcvcObdtZgeNVjCHYVwMhbegSiu" + "YHgqTrmlJqqEyeI7jm1Tp7KeWRJAgT0MgBq3Qy34IzePwKE195454MJoAA13s50chd0y5wGtc7S1hDImPc1XWqtnvjk5SVR4RfQzk8avFUhKlp1PgFoYcp9i381CVeUc" + "zQY1YQBDOEuL310sZ9xNtlqvYSyHzCFY8wIA5Iw2RIgGpcUYeFCTwJkD85xpqr0hQz3PAtvYJT2Uf3zd4yHDii7ZmekFVEb6fdHbZrhA2gYzPVVxkoHtC0YSyhif1v8w" + "h3ZISaDtt783c9XIbdO0jv1YdNjn5DcBlCqHYqfzw3F1KOZI4dppOQhH3k0XVsGfMgyDeaK6wwMdRFYOfqHZ4r6J99rL6es8lm7xbgiQN0PjTXamuWCXwBLZlTbW6E2D" + "HvEIyPh9ob1XkgRjMbsNtDCM1UzwJn87S7Yh6PFshTh3EBk9Zidu1Om4dFcO2TqkpINYIYcOYrbcsXUxoE3QTzyvyXrRQs3tVli0UYPcXJY3OJMZztfP3XWozJmlalEj" + "gfNDXx53F844LRzl4BbYYnjHhp0r7sHWQ2etUxGIHMUWak4uWVCXXnsy5IpFEK4uQlcIExvXH6AGth24iBGBLK05Vub7xUvJEKTDvAc63PBGZrFS3kiCciTQxhz1PNHX" + "NYIu1ClyDpgSnZLF6wFjyMepBr9Ka9BPVc5Sg7iLXxSV48etBMdupaJtzMUpizuBHWUR5puXJzo1pS9rddycs56bHWXK1n9R98QWZYV4z0u1qEIVYvsOUMp0xDMlcG5L" + "gCWocCGpS0KPbSQ7xCezkkeogZJmMfxJOTWbrE4jM9cEMS0l0D76uCDBG5nKKtjAE3EizLyHAi1saKiy6gf1GgJsMqiMRkQKQ9JJ70jVfPSPh34W99JywEvtnf5Lto4m" + "7vt1GPwhfKAIEIOtf9dKsKxahtGiADuspZXOJrsyWxoH5apF6PpUHWloZiYaeqzsRPZlIhHbPACpcDPRj76vdu0qMEbOUCOXF3wEvfHPGbr3g5JMyWSR72zIjxJJNCKf" + "0iyXpFuwNgj6rpkjBE4IHCtKzObwx8ij4Ht4KjHGiRx1S224IU2X2Uwb8waHBqDHvD4TOiRnWOKCf0zSWCrjrMYWbao7kQRAChUbNMROWP0XenB3LcfWwkbaXyjb97LN" + "CXZad7fprCZ63qcvGheyVpZham826nHaoKsMP2FX4x3B6IUbvyD8nX05s7BAyj5gZun6GfjtkVsbYmjtegkRJjtcTSeFl0IVSIAGBYaewt1qaQ4ucjBnYX0yXQv0yzWe" + "IgPxbG9WtwGGuJjKrGMOawrZ9W2S8qWac05pOhoXNWRrX5cwfPR9J0ORqBpPIdeBvS6yLIoyvWV96ZGwiXIWtf8118F5DOAr2ni3B1MFTH6EsesN4hRsTopQ0WtcMzq0" + "XKn1NiOwtOEJpg7xmeh3GwLaBJesdELkqAfyVT0sWMVW5PGny3X4VWm6yVpUBkeSKqn53aTGJOKTBcFvxmFZY0P9nDdIi97JkuPa97CiR4HOpC0DySeuoEJFqFOSkOBF" + "An5JcKp8RreGGMYcdzW5WGrzS5s8nSoMI5NEjeY0fhnThIZCojsq4hZCZMm2GruVHe82NIUuTyZ8Ve4jzMmwVRqjsn8e1QU6TyTnCChciN55Yt5blIVSfbxopdEu8Xvy" + "ryLhav0FFGNeJAX5Kbv1UkR9X2jvJaVE7RXWI5ebBB3pV0cRNyOnEjZPRXsjJ6D5tw3qTXblVyzso7z85K9kCkmeyfwqGVxlnnp126i6V5LPJftBQTspOrpxqoXZfV9U" + "U82F479Q6XMNLtkOYFJcrTFptfE5t56mVT93ikv6U23FDDkVXAQyIJjycCfPU8gPb3lAOs4TU9wqpT2MqYg7VuHHCoDjrDXKx6oF5TOFOOm7L4IVKuonbLk7hQe8CoBi" + "bh8KN1Gig8umrfYq7i91i3NfqJ5YO9vtFgPAMoOptX5r8UhDvKrPvIaL5AcSAUeA3A0YmIPE7qMGQtIS6R46087c0qcQZOs8AHRoP48nQ828uvXqG8p8uBKTyS1Hhz8e" + "3C7Z1Cqldsx51ZBrXBXHcqiUrMEnPabXGONJbGm0SUEmfapBfErMiIGks1emQrOZ2foELARxXjBK9YTBc9DMqveVRn6u1xSHlxPDIPQE0GDHddMjMG6UkjDiM7Fa3PLu" + "3cdYezBDAFJfiPFOBbaUwTaAnT4zXZUj9Hjdu86rNUefQVLpL7o6qNm4jfG4MtZ6dqdWCkF26idiWk6L0rnfypoBkQuyDkt92ylsGPTraw5yIDdooNU2zLYa1KX0nFCT" + "oRwtHmgggsM7da3PbIpNYocRPqiPIf7kxS8AA1XAcc7kSmMbKGqxx4J2eGbZSY3YHgWjPOzqBbrxp5s5NUS0M72SogDGttT9DTnVV1gcuoZcDqJogyN5yw7sugVRN6bp" + "INTWN8tshBwPAiZAGv5Cc1ubphjxi6zkLBdBVKm2ZO9dTlYlUnPCOFYuOZPQLaZmZTwauDYwHWtX28HRfSzmXyVPO4CkDdE8cxD7doI93cglJ970bGBumHAU8E4UW9mD" + "20MXtb8TOTI2N2hYvCd3e8J2nPOCZ075mgGzQ2UWxvZOfTpV5H9pBwbfgkpQAEBXqcbB1pjTGZfDQCfrj5s1dGSF75uBjZEhryl3ze8TX763AGbJ0s3F4g7SDrGKfWXw" + "SrfMoMiScgpEBZO9FmjG6rbUCaN35hqsAFttUDSUhzt3YXhVW7JOOeWn7v8omY094t5cBRKiwMQCd1ozm8XzTOAJSAHiUAHvXVGE9btcR7wxeUsNdeGIAncrkwazcOun" + "YGBtMhfNAU3ZLRBbJfxwb1Bqe4hy7lWmRTZGL5D5UaxtJWGLoxTJfRSbkXdyL0hVu1BWMBWvTwCdOdxdxSPuGK8LhHJA8a7xuB5yjJiek7n52OIEr6Un6mknvEGMMwPJ" + "z0cOrxUd1iikOy6Pa7PR1dkXTS3xka87St7HAs5DeeM6lNY024gp9PYWCHD1BQ6OpUoKB5BpzO293lJLLNB3a1uhqUfg56OhTdv9mzQHejr0jrvuSa05qk1hBLcMgarp" + "hbyMNgDX7mANgEz4lls1rqTQXD6oSx9iFmCZCAp4YcluCfvMnV8FPdTVdKHWDdiJ5uFfaC2knUQmrhXix5jWTJZXH5lsCbWF84NMotvIzBJX85YAc1IrEhLlhWpa9wgC" + "ogCGmOAzRuIeZLiZVcfxDSq7g8FXjIfODCNciK8ABsvTVF5XcjjGqVH2QSnV9VEcp0OtDPsPn7F43CwlCHMes3rtcPxkZ99II6JKNwQ3qerK4MKQHIHb5U6BDl29l1jl" + "lR8m1AerOxahmYRfXjwIgWIX51x8nX6z7rE3FMpCiexpy90exwixTp4XSViNosAwv5tsR9UZO13rSP51ufU5imXHYRsAGo6tD2zhsmI7YPxkaQdykzTIrsBZjKBCk4YK" + "ZMZObu9bUZsLcX4IMp9KM3s0Q671DROI4RQk9xDZ8adXfpGmmrbWG7TPf1llGXnJaIeFZJ3UFSiYJKCJ1jRpONliKeTNI35yAIBwceYpRsLaIiiINvqRNnfePbWevQw7" + "H1EN0cglTAwhc3mvzRgTuSZ1TiJneuRJylI34LzWTyiY5tCWN5JBNoHml0XbodE7iq5OuMQYk72mfcRqeeaMlWsQs6A8h5fMhVN4iARYgUayVqkcllvtlCP8nK2UTBkQ" + "oYRBQu3LAoq0si9AU0WsumS6oS5qku9drUhXGvjYC1kbKz1pKvcckAXS6n38cyvXNNY7P65vjAQevbPpSxrftWAXxEq2kvC84QeMdH2XJsbjh5OqFtUojCM28gNF0w19" + "8C3RG9EL76YVtADiDWblRdj8rspNk8t3bPahHM8sfiPtEMRazJ6iiJe3IdEEoQfiGRvcEOADZB3Xycu02YYvas92NOiGZr0aKNfvah01C1sWQvX1mIKeKcsw5IYCL20n" + "OxWlRqL03SPlVIXPCqCibbkVtzqGznRBaLjLu1K5sa2B2RQh1x4jE4pTYDm6JG4flctWeuxLbOmvgti3fJU8bj0EblvQqlow0mRBwlxxAzRBbpdP2NQyM0bucJDTBtAd" + "EYDJFx5sDE5mUh03KYrwwZ3JDiO5tmLDkvkh6AeEMcCrBPY4LBWe7JKfVB88RHvFL3wMdbiWqv7dX2IbGb0vU3SeQEUvZcDQFolmnyGoDzW5OVMhi3NJ56sWFIK0iL35" + "aaGZCg9KgHH44BqOSV8gcx8IWfnV0D89AsmEkEz5fQrYR9x8Ymp9afp4ZIwX2I6y2q5xbKkPwepqtiM93iX0CUFni7ycDvU76aerYOYS41UJRvhaN3gnkkYfQI1G4y6S" + "e02kGJRyh7O6TTjf2jyDqnHB3IiZ4CuAMsJTZlGkBP5NZSNT6Ltty2YhnBo8fga8qeHBdXvAR1XTaZ7L00L0SuMIInf0hna0IkETo1XetIM23y2ZuOXg5yFVa0AQjLe6" + "jVdPHRHk92DY1GusLJITBs9B58xDcrZ3sDJO8dFCKamvoNj06ERxR4TzCRySudNOEdKyjxWf0cfxxl9OctYpzaMEwvvxBKOEqldMCxjht6lnG74gWk7dkX9mBL7tvwm8" + "CjEKzlTBdLgL8GhUTX95wLcVK7wiTUazcOSmNW35bVYFiBnQuDBlrJGjEtE27hwafGPVlCRK2ZY8x1JH1LDvk9uWFVF5u5YaJeQoLpCL2FI4SZP2pADdNBKNqUQBA8E5" + "hdQ8JT8ZeOLNGFQpzyDvoCIT8CihhvAgaLnzWbmNdaoZtjMQZwoIbksfodHUXNoinjdOMdZHzs6TmL0x5sOOaxPR28Hp8YpsBChXkPVMggI8unKJVJ0lNxPk6LUGNJYQ" + "ixeR3qDCL3EdqHjK2IhL4Gc4ErbRtK5ImD8d7TITHIFIFWLv2fOKg3SkjaoodMHlbD0an2r5un4oX5HmfWDwSvUMStUriK61yt70EBCjC8e8nGaQYPav5ndPjY4DUNam" + "9BJbzZ2Vfeh4IBsJf2pwrm6LGwyLNpmed6IAddEQYHlZwlg2G0UgxjFtJGGpJhAUiSSnf8CzE61uXzbGuXP3g6HC0ApbKggjcRbFhFEjNkdamo3w5ySWo1FQ2cXzYvwv" + "mCHuBReBBDPVmV0eKbObuRXFQgaBwc8tAjtd7WDNcKIYuvtmAmQtPInx261sfNPNr4xbQ2T4ZJuiBIpErOAjWkLQfCYSF90EVU3BGOt9m1UDxsppz3uNPHB6mjEK15x1" + "1Mh67pSBAED8z5Jc86HK2Y5zVzHa2WMgMA0Q0MY7k3yAG8BecJ7gLri5uizxV4JUP3Z5ffKLEHDYYJcVh5Xu1Ngpgf9sGZhYPHL47995GnxjAJIFTKTT2n1NJbDF6fK2" + "HO1soH2yE5WUoeHq3CqumoGvB6xJwiXjOltr5RrUWQVAFpPjoANNF1XIFsFSyRQ3fOIN1dvxyozzwsj64eXoJSnMcwc92fCeMZjC8lnQz5mvpBv3AtBWUtHcBQEucgra" + "A7f0qTmYAK3HCwMkjl4CNEqR7m3H2Ai7fxRicNwCCKuHi75O67mPzvDxSTYZViG0G6MTyUuYg31EdxpEtOUVtnKMQSaU91iT0WagNpQ6ePb18wvoasWohmxa2KjU6vLU" + "mXETP5eRMtev7MCOC0GC9iUqpC7I6PO9mMKd9uUx7JINSiqSIV0OkMBFGq0t1FaYeYZ2gyWAGyGbdXjABhQLUAfl1H2Q4zWeYoiacOfFemB4q24ZFQKLCXtwlCFePcCq" + "UnGu0rMdl1ixStZxjYzgS8rziGQBSeBqai2I97gZfyK4Vi0m9g6WOPjVji6J54m7ZlSWJhKO7wkL6XKWJ2NmEgbvFwesrBV2nO3oxkSDigmILLVE7F4u8rPaieNIuLu0" + "JDcgZIHYM8wWXiVjpcr78OvuA514njH62CKsfQJj6gqvpHotAL3NPPFeYIoHNuUNM9M0GOLt1YtFiFe5alrKYtCxilcwA1mzQ9cw8Y1trYRXXeoW36faLf5crZmNTU6Q" + "BFwB11V2JTlUVM1h1aobE7SvRFUD4WsOmm2CTUUFR2VpjnfTMKx5DGMXDq1fxftbSpB0rrqfP5hBvVVmVX3WHN3wy4LSEjWZZgmNCkhIXBT7WSY7DMatzQy0VlfATHTK" + "ajAs56tQFOqxlcE0CrDCyimjpD1V2ni2arxHfvEnTcQSKCPVAnOr3HtiHAqhZ4M2O07KNEqycu2OKyHdjqqfQpx6Rms2REufMKkZVtMNeOqvlmoosqwpHQQqzILw6tPL" + "KSIbVL8pt74rJCFdPtvoofGRbOb1viRJAmcPlDRqK7l1fxQATpM43hLLE7zTkzn6wuHjojKYqBJPb1aRQBWNCgkPhXkn3NHmU1sklEQyyvrvDYmNoIvlJ4WOIpD9aOBH" + "7UAQKjznFEZQTwlm0plvmn8U0aC8R1e00PqDutERmgyiBQjViT3JaSN3S4ee8Wec4OErbWwxDllkg8AxLQSHy1xNwuozeGNgs5SPjsy9tY9oPbKF23kdFf38NARgD073" + "Ni20ebKrwS5P2x3ZiDBatmOQYcFis4RLESzP7RsyQy0yZxGjTUbOVCuPCbbKv6xyZrK20drbpxJNptBhAgtuXiKmVH12fZc4j5WRw7ZCoAEd7WxCoLft0mF7iexxBoHw" + "1m4oNzc3GKpeVgtfBkaAF2dQHAnK0ANcvvCAZ6EKhOUZOUa0fbwyDkD5ebuBmqas7Emf7sbnw1GWemgAoc1LtTN9eRjrmoYMer9bt9ooHXuFNgf3ilqqRkoxdRw2bGQN" + "kpcppdpQJ8hqPj5UindN2VMnSRj083g2EWXiUI3gfRh4BjDAg2UAOd260K1tv8c8RK1AYGNNNqi46stWXVezo7JKM7EbDxlApZHCv2iKXnOInqVw94HqTcM9RF9VdKkH" + "0NeKKHBupDp06QLRUfhrzIcNH23z6NgZ2RosX36JhhVki221Oiq2V7RRrqeS4RmaLR0EhvNyXziCdVhn6xZqCiI25TghneXL37ShY8VaABMAL7CAdV2RjotwNA3KSVaP" + "ypCtX0swSmritB8JrpY20jB0fDmCspd5hb2jvQ7ezQgQXh3SVbQRplCIGyIfqRMVVK4gP0FLdy0OquaWwRw7dPAEIsGHScKl3NLMpOxr9l8N5qcnusKIpdr7f7A2csaM" + "v9tW9jLPetRSgU5IvIztR4v3mSTJnEACDRGgiuqxB50pT00Xyr8M1LQ4OxNoBHbnDMsnCq5blcudGG2BMa4WWiI42CUzLlHEfV4A3hUA6ycEp0AimhOWWMArIYtYJmCr" + "EyUcmXKRYmM20Yp5Q12MoqeoRnF2wbN8jolJcbuFQPjAYoCUQICtzLkHtZ4SPZ45zopnOrrMcGLN3XwALVvzQtUHxSGmAR5tNLcUCK7siEr0DyIiGnk8sg78xEz8QrY9" + "LnVNgg2chPB3GgsroAfKGOxKZzQGUreeAzjhJTpMOXubRUzuSC8g5CLpSBwaW5gQW6jK30FZpj8GSeRZnTFvkOG0XhLKzSQcYK0xy8mIeNXtqqlwmIJjwsZgH2jBFxeM" + "7gcpguK2dAjuTqPHES2TuWr9qbqcuOfWtAHm9WCSbZdYr45xAmlQoR2WG0PSzkPmuqnZPYkJDbaE7l6WXFyWPSp8ifoMaboGJZKtOG18kUYtWisHTqqtUm60oH8pNzow" + "ecqVy7j7TbSwGcxF8d6Qb1DolS9d1rUAzsTkeBZckmwF53GhanArFfh6Beu0Maa03JMhr0wZ2yKjDGQC0aFXmhXQpeun5dXERRVEpm4GlKVyUUSzUiPxEM7krylQ0qOx" + "SN5MFuusezHsakPHpJEWOQDWpraeR1McGkNIRp362CfyR4gCgkZKZTETmSRSknmY3rdUAXOkWEYbGZgGrWNQsdGQrUBwKnYqaUSBlUa2xE1is95H95eUeyX18J6JUdEO" + "QkWjlNe5rpJpaEnXDfqnzKjAjD41YvgTNBhh6OFp2gzSxp3WTModvlaLnUTSWs0nYSs9yYOCH4wRGlbm06YyxW72nnqUjQTyweMl9bO59mLjCbwpUEj89JPTlDpOmhxj" + "kvgXib8Ds0QSfXRP00A2vSRVPvuwuKrUly10uE0kIWZU8I79cYfmOfxvaJcqsgqBUnof7SEGF7qpRjqnEqSZvC9aj7qgL41ha3S5Xtfnc31KQTLOCRiOzMUzN5nPTZBV" + "FuJi0vyi3Tr2V91Ln9rs6qAxaANylvS4XvWzuW43Jf5VqlePq5jrgtrpj0xlwrS1tgGlUn8S9ZPLDXidPRJ7vO4GpPz8l3UtdaFzyQt14crsMV8BZRORtCIzwASaitss" + "trhuzemnfzQroo9hhfNCpETpRq0ztuBd4IsXo0ig9YVQmAcNWfWx2xFYpbRta1uAcA8Hpv85T73ccoF5Bh1ks2qUOVIQAQ2we30eUThACqT0KED4YL8srHKTLCvjK8NN" + "sA3vJB1a27PMdUrwWxnnuJenMBmkGVyWackl5H5pvVUCJ77gvc3B8bLo0rd7E9pUc6K82ltxpyVrjQiffCuUdpoKM1QhgAe0qjUn58X3Bu8tH6bQUZCt2LziMmkBxYfP" + "q5DZfy6cBhfztr32lIuJQLJEMCoPcGZxZltBFvspylhF3cp2IpmwUY8xSY9f7pJCvB7OXGbz7VHJslrkdwq2MF73ecshUVnVQGvDo4Mq5LbaQtfxdEgH2CbeuP2bK0fW" + "fhdqDiZg5hU2DqTBH1msD6SFaeK4Fmsbo0VWGR6erx43nYymKhJRj70WIcoe5HTgQRIU049xHCmZsJ9fxQBT0DePEyKWaugpWXEJmeFtb2qyYLVl9hOCsgFBYrbDkVpn" + "ePTavCKiCu7NSqGzrhvnRHAtzfyCdUafKAm2yiMWQrKRuOEwdQWcEiWLRffzyPV6wFi14swEzbyw3cb5HHbrEOo0FsxPEjJUu7gEFaFVCouXodp7yenAoAS06kqRdciY" + "D7vzmNNiSGsPl2BhTJQGmpJC8jC7gFdcAXzo1OYyjtzYtH835mSRHml8d3YnEnsib16gzIpI1WKGJaMmqgIJ4w6UG5gV4FlngQyFsCajyp9qmL0iGYa8xW2CuOgRRXUg" + "Sm9645NmY3NGM2q9u6HKLBIVKYb7TUMRaUOXqnXURPzTUy8wz9IjYclZFh26I82rIbIo0FpUJynoVpXEiLKUScJJiNtUFVRCyOE2DAUPWgQ013A4RcIbcbA5J6fIaXVL" + "gahqrdDNUX5VSLNeQJakc1xlCogWkqd4ROiSesuHFgexWdZCn2NItk3uqxa2DdR4lAZEaT6PMFsaA0206YgI604Cwz6SsrIdqzjHlmW86I7LhqFSAE36pJUQJzGYXzoe" + "dpZwyHMATQgSonvONx2gVH87ifeAvDmtIIUr0jixaB2LHbJZ9GaVD7F5a6dkb1vndmzesNwrKU9uaaBzzJVJQCvKfRGwHQP2hiw09fbCr0hGmt7OrqCbZxbtMXMXiYTc" + "AsbYHIc6Ct3n1fduK5pB5Aa1wKPoU6sNe6DM9qxgQn05Y2UjsVTuo1KCWVOz2qccHOnyNzOyyAJmvQe8OPKYEhZjJbkarTYAnHN439tg6H3dzAgpXYSKb1mpuZ46dM2a" + "Hp2QCe6wtvOCnjJN0iHtBkhmbivCVU5ki3r0h4NxHx79GYzhztGD3tTLXIRVUv7unXFDTzik1MAICX7HR0GtjsECso10pqpFEXrGM2XHVKfyoRmLxTgftF8RBDb0w3YK" + "Ut19orlyPmZAKIEzs2Co2rgWx4KKIBL74YFby3OiYBjkZ3NBS17NoQZwZPzRsiSt3DtIFoUoyT1eBIZBPVjCVo5SMBDtZfHivSdNRBXaPZdR4NqQFcKxFTd73m0qTwUm" + "ibvSPTG2qLhRKHSPxLIhcSb67cEDX8Iw9jlxOk9L1Sjl7dBmVXKaBrxGCBAhffLyUdg0kGUpzOAPCNobQXfdybYYVd7xqu6itIDburLPXeNY49787H4ZpaHcPCqFL8JE" + "9iosWeknyQq1zDmxte9Try9xDliTJ95MRCCJcPLhE90czVDdGM7Ceix3kqkrrMoM16IVotiOb3299uEV2qioyNTN5XUhhiLK6gWsv6n23StSaggynPpLPo0e3PJcGLyi" + "ufRCsGGOH9H4XjH5ZLj2llZjWUEBxok7CmLV8VylrSw6UdPMztvqzYdQa2QhEepRGcJepQ98GejBrjmNVqn9RcOTQb0B0qF6jfXNkHqK2GcboQe2vMO56hgdtDSo3DsR" + "6jQJOTwqNFqJrxEsZZC9L8Fo96cxr4bXyrTtRn6joblxORZwtCn3vwRQvM5oyDZfdtxr04EzvKtftxQUgM3TRAReZi4ucmQ0fJF9zIKA6oefNleXySOn3FDtZSXuVpsM" + "Gxa27zETEiwSF0mEVtmafHq44Xeo3PgHu2mbBYz4laEYgNe04f72MGyvhXH8sRyLEGvSZCsmtg0iVW3iRoGvlxnnNVhpyDydTtwy7ifAoo7B6eI6YhKGe5ju9kQlCjSx" + "sdaJXCxLjcf66vNL2wevmXYghY05Cl3f60yn6ihEZJkj19SPqUB2olYScF7EbEy1ZifogTamkyeDovLrm21UMBi1phJpe96hnjiHyNGgKSje4K3G1AJvzzMCn37CPeHj" + "jxD0EPKOuTNg8bonMXfZByezGKruOsNDHKFBI6rvOaczDIf8FiKvTnwktnhMAcj50nOOt5IFK8olEoYwwBnzyOIoxiyCkuhXqj4GDtvXOmXeKGmjjJ7oGWNe55lPedxd" + "38c3Kq1emQwygQuMhLYxBMSsbhaVm9yv06duKAs3A7aPjPjYieKhtisNw3JFyzrKOq04u7kI0Ve34XCUU5WHcMlYk82llTYULeC5YG0MAzcJICQLX66h2f6R1j6CDij0" + "uRWArg4Gm89UXxhLKDy3oUUK5X7jInO3Z4l2Gbx0MhvuFktHGm7jRvuJwYTae3vAYbBraBMMUEY9SPmlZZHpu1Hkf6HBF8cE3rCqerUrPnUCA1H0Ie0sQBlJ9Y9y4Pur" + "U4vxcJto6zQTAeAkDsnufCwNyuHc6kOtnzlq859s3NeljjwTBe1PfATvogKY0UyE1CIysCT54JJY57DR5PhawPJq3iOC5y7Qpk6zeJyGlztXpVgFb5SJ9r5QtJHnbkE9" + "vP1fMKzemdVLSjA82ZFtGfS4DCe4YTIY9xmtSQZeHDSeV2sd2C7QwP3xkCF8FIDwVmFfzEEcgl7hA3hkPuXvT5mbQ0kbSJ9d8HUxkTeQiE37gpHRiV1mwYphX0vzxSF9" + "bMuPmGcDUEVYWavPMaAtjdAIndRn1T5Ex8jBIGSDxVU926YfZAEDAaFCQ7tbJ8RTjFfDmzzsmwNnJSDe3UinmcDznRnUkHLapuKp1GPRC1NsRxft37ujX3u8CcQVDBn4" + "qskD4n1JwOfH9yTFTF3jGzA0I77TV1JRSyCX4J3q1FRxTgdpo3hur2oLHYOuhxvDR1ZMqr8qHYkFSW9X1fUdCY1BuuPMMCPfupO1ZBNaQYsuSjKbXpJTBjhaZBqIj0MX" + "Ijetz3M0NlSZ8VO1ODDi0tn28gdptfhhiP1zLVlJNElz8gc38GvF0q9j5lCxiUWaX17qoPGHVZH6oxiTa9nN49UXvDsiaSJgbCZoZnzZpBvsXKhGemNBXCwdiuMxSL5x" + "FXVet4TQYEED9i9ptgCuXtdcBbcpRbe05HLew1I1pEcDVC5Wc3IQHgURrdYuvbKTqhR905apXUzBc79qrn8Z5Le8ASVlysA9uQqLFwVU9vHBtps2N9SwTNQph7l4WNSy" + "CtD3fW3nzP7vsgUb2SlTXeu9UXQHkjtEqBso1BfZwTpo20xWByqF9sJ568vl4i7qj6Y8oTsq5RHhTY9sscDllyl0O5Ef2lkbk1iEQRarLwCyhO39DSnRQlYVdNo1iGBu" + "Qq12tnScBaE7yEnfH1udHEjoGQrNaBqRUUIjH3VtgxIJQMWrWbdIYPpCm5gDzpsIQgBSdfHj7BwGf7tXQh841mKGFDguDUMP2OnJkkeqdOVt8pGk5Zw9SY4USccOimwb" + "EIauwjisqr1FRDeLOTGqHKkzbpu5e4rcMnoUgcHgfXZd0iIMStxWm42Rc4eM5kzBbj2bRtSeDa0cw6WfBfLswJtkdhUUdAKUCksZ5IXUAMZPYeXfZ5iwKKeetB0nRSW6" + "R64Bif5JUwdCTXslEIQc1WdIlGrhxGwPsHQhfmr3dfAtcMBA1e4bXqH6EfOGhC7YjjxCysadI7e5SUSHKlYmtqjHd3Ji1usEKZIYwEoIgu1Q8sx109yina3BWqkZrAtX" + "vJfE5NP3A8lT4MxzSanGbGpk0WekmDEqTY0A5hWrYdIbmlBsHoLHN5OyXEnRRr5tOhHYfXzQDjmp7zw9hTuyqxPPOwvwVwoCEF54pAOt9D3DEesQSfSOmxoxDJpZ4O68" + "C423Pk1TdJp4clgCrfV5Url4LxlHhXLJNCEzPd6usSoqHssNHMmEclNdu0wyrCzFzN9T7Dz42g6hO1afu84mvAks23zf5fFhZSRl0comoHZGJcbmQ1Kkc4tEuCUO5oWR" + "JfohwnmXGcPoj98AUNwRLUzsyD05z49QVY5pwmAsws82G3rkmWHd2g3JLrbFmX1oeVOEnx3lb16ywoNPqp2t2LfhVqq1mUq3MT9QYOoaCn6wVgzGbAGTAFLYerq0KF4N" + "6uLwMZEmrLPZF8yGBlE0N3rk4Uc6Imcq6gjBsyyn4ufRlrIwHrVXFY5omNg0y0TMhavkHSiu2Ku7oSmqt4CZTXt3amxhg3EHIvE419M7DMmxdxEWEHwvULkrxyJxJaPc" + "Myzo7I3MlgaHxmdjdygiUWzIE2ewa7hhweJomeZTumRWRK3I38fkGPUP5dSWwYWMatKFbZWhDAyiCY1bXkLNKzRmj4zusnMcjmzcItQpZx3Rpsn9J7l5o7tA4XuQzie0" + "O6IrM65C0x37fFSJpKVdgkArwhRLly17zOhTLtytb8gzGWeOoc7C9ErR3aPOMqsYaZzn5V4MfmfmcwzozRzh5NcRUvCYie5naS53ruUvEUVV3MqkQzCmYH2XIpuMT7gc" + "rCIuJISrcbmzH4OKr3073eT15Vz9VcyYhebWh7jbgzXDjbaNKyUzi6fGao7NZDTYSuIdCE3Nr8YHKXtmWTcFNkv9t85hzhHA9nXJ1snpPBMepwFn9b1VYNVGi0itNo2g" + "CE9EiDoApbNlVvS8KO8SCWWn0QwPNQtBwZgCjAqHNFETzshr16njxIKvYC5mgsUZduPMIJlFOWaab1yKZH2pj5Um41Gg956uZKwH3ZayWPWE5cv7QSIosGQRDEijeljL" + "nBSVMxTvOfMLXrjIe2bPiyqgjwsfFedwEEyiHWEYsSwks2p0jADqfrLGbe8w3o1TCI4CnKB46PmDeAFY3brHIt4QpGG1Sc8ia1znFxU5XqXygbUQjeWsKsXG7LTrL0Jk" + "5tOgiMLO6xrgJb5BuGsslBeVp21jBhOJdco2BGNKYEkHKwPekPn7rlfVzmSRMSPVdvXNfvrmxjoM6CFETNtI2zeTuX0J1m5F7FjrP3M9cVhcFMg3xQNddmOegl6UX8p5" + "xdSBSHVSqircHhbkhckd8Cy6VExxrKnMjXP7kkeX4Z7OLbousZXVGnu2IbR8E4pklhwcZHIaDO6OUFyHD05UZUjzCKoXEu81OleXVZTgAkYVJyzr1O0DUQblYA1ij2AM" + "zNPY5GncsBKfMBsvHxuzeBVurtLrvfeAYryuTRdwSgD9MW61AXC746rSILF7aMRnJJ8ho5qzZZbBVRx5IcF2GpdpSQqDrGBQUTAarAluIwoPCYyb6aJrbbtjpe6UmH0t" + "RXdfLoFkYu6OD7jynmGCPb4A24g8rqpRXlFUVcxcN8dm37bctHhHcIrFY6UKrZqABFaQHPQzReOkHiABOxXba8x1mKyDQPWUYe3iPFXuAIejPAW0wM7MiRkODFsr0nnP" + "wQZn6YrNNLlmq04y9x9aBZNIhGX1XlhQ96aYObh1DFipCcaQznB9qgE12YVHl8jcvRZjSCMQdEgBArhLP6UomJUr6n8rMXGVK8O7zSNQzgWzCg6KYolPgREmmCATOlmx" + "x1GkzazPBvuviO3PGEoANE3OJOia2hpmBZoL8gPVZ4XASSosAfw0f4lMWoB7nwY7t1v3uqix9Rp6gOxsXP7aK12vnCZRg2COeazeg7MUgfRJVMdTyIOja2gD1RVKH8EQ" + "YM4MevSXTpPzbRHRgys9P1Ni5xNlNJYq3Ic8MV1HygpfMHkwy9Q99fLm8zVJqencwvSsXjKLhJ0yZjgWILAMuv4ihuVQ9OzPJeENydu46bS6xqyl7n6gGQJSP6VSN9jS" + "mUULi0gMnWyBT3xCHHabqpeSqPIVCR25TcdMdHWyGBor3WJfhMw3HN05uPiuo6aWvYdpolxfVFpaE5gT3pDVz8KQKd6TrS5FQ4ljd3mbSWVVGCMjq84Y1BZ6qbg0zDIo" + "oNfZdOWQ2MGzosleFO68dQ5x2EuauEQcM7i3OQbIKrgmpQQQLgAu30QeZeX7M1IBV9P7ceLVcKaSfjUQEpMqYGGP7hhmewFfZOXuDfHMoEpXykoPXDnbiiatbz5JqPKt" + "ZHAuMXT64VqUMCYob50Vg53nRdOt5ciUrjR6pqqDBCOgEwc51fr3R0SaHo2wYBYxzHBz5PxAbr4OqUHjyN1JmOd3ZqZWbK1dTBrtgTXHDEQMFohndqQRYtS5QzYfvmVd" + "S0fh4h1hwIlf88RGxeEdO41lQOVP35P0ybM6kmRJUUMFbvv15d7yptFKIVNrJ7litI0tWvDVBPeEJmwbCPwADuK5P6cz2WEcCI48R8liEQwjBKCeo5aC89UzSh8CrtAI" + "WpraFWZ0okBBwyZ9dQUXXtdbqUIBiKiL436vFmE59aIUegtPeKTJZVHeLocXeA8VFPVhdNvKknFJdU6jzk9WQJDCjhusVcJcQrHmd16vLgpkT0D6WKIEPldIxmBrPzrH" + "b3OFB7ZmLnDsp1WMXVrNXivh11woH0AIQAfvlOvu3hbqZRWG2UDitie36JPFqtMFDRnJMkyB59RTeUBAS1iqi8Zun1KU728EZgjqJcxnX5nKXOfDWo0YOukYEou2f32A" + "vC2fKPqSgHgf4WuKmRjn0cENkZBsxQrJusN7BH37Q4dVUGijIS5I3UihZhIuNAEhlrcYAm1IXfHN1RccBpd8AQ5hBytUvJxQsmfkGILlB6K1V5J590IARfXajP9h3zew" + "FHqV8XUwo61wQSp3qB0ULBhf3ajIOix6N05cfSOOb8HhkBbmmiQ3pqs0J9tE5Ionr5O0lXlR6Af3PcqnSHi2kJB5XADd8BpjyZmjjQx73nrnI95Tx7OgFXjPRoMByXQx" + "BRBrhkVBwFY06gco2b1pbPJVIJBP00yetN896LETdAIE0AXHPLeW9AIHx4HoVd3chMLfP4dS4Lip8qVRiPKKsTYCleDmx2T5IbFLeJnsaw5Yr220opVdcPkg7M5ALBVm" + "iLxBwLXddkP8wb5utSSItKqBFofOCkTZzoRhO1fWA9BvBt0UCoZlS79xCD8wSjMjDSUW3pVXO0zBbBchXsvUzLu8yv2m8izrfkNwprxpBrjFQFsaQXhEmVPPTFJZazzf" + "awzGKqjHXguwC73xg8Y5Lf4wFPqMmTnuxvMBHSVR7AgyMjrqeIpO4GcCHedPWC52RCKMSJWwfngsC2hO7FnsCopF0PL1taPNkROuBg1tD42WZT5G32mSbNfyioI2xTE4" + "ErSQ2jcDwHkSwwFdLuYc4CIHO6zuBHBA4mc6cMOHuobBY8nJwhL3J526hzx9gzUNTU7vgUdW5zUNNAjcxkN3jF9XgnBShzeU8zg1ZLdGfLrsNjL8Z27vbMG3KphyxzCX" + "kgnNFB7hptipG721qwytqDsx7pMi10UFPWoWKMqfpM8wgpb16TXfvy2HH7VDwjNESzaQkmwjeu3NCdVnNajrGW1rIX0H0KhV4PgKG9eptvrzQ2pbwPzD6XBEh7MD8SlF" + "Ptv0aIezSdAPIckcKaFj5LjHdWZx86WFuVcKYsIajlazquF9rCeCC1bfZbKrjMeiJnp8RHheNUy0NnNNB3ClGekyFWtQCzYUCo96xB1o21u5bP7CFoqHevARgjApes4d" + "gM9WgZuHOD7qNWfYux7GorTTotOhg9hWrvRfbHrcD0Niu7iVvWgHVn3v26Rvu6B05jUGTXxfcxGLpyC2ZSKS8lQFpD7T6gmH2z9ysbbptfAdiwUZe6T3dtCaTSI8YKJE" + "HabEWVTYta5yji9Hqm8oWAT7f5W8T8HOZ80FEZWixkSVeYSKBJbEBTQEueR30G6dsLOqDLIDWqkq5BqCyr84hT0S2h5scY56j8LsWtQXHZ25BmtefeHZaUaq5jUQLXvs" + "2DGzQQpwQRiosJtRBFjOiu5wFbfcDo5mZtDboDYanJYOgyaVAKb9Sh7whEt0Ss4FVBHaUxiE9wHc2TO8Z7L1TZRUqDeUBEegIzikCHqJNEgpNuSIHD6LwQmkKPrO9M47" + "MHXdCMxuSajkGt37YGlS9WuKtv7p8gFOnImv3GXzmyleLo9fe1IiTaiGGhKU1NWNLN3pOeUzC9E63iwsHkQEPWM0CIOYyjgE3GTR6mHknc24lo0a0p8gjtDHedk8vZ5p" + "4BNHCX7Ea7paxayYDZbIUx2hA3TUXkxYivIJVCFF7drV2XMmAYSa5H88JsLA6YRZCsltAS9iXYdaonAlN4WGaz1E0m4afC06JsV0uOzfiVKsSCFSf2IXyQQlWTEqnrSu" + "nqlMMuSx8Lq4GDVWAsTATI8316L7lKV5RW9CJB1GvVM5vwrHq5L20WHs8Bvpx3wXQkK8FFwF5L8iXG1MOfS3tbXGBld8EfzF8kB9E6kk4Ehzml15oDnDQceTWxOkjKIo" + "OD83EnQKmuHvul57p1bsT8n61enTKYucBytlzBmi7mRsQ1PbjZDMvg9TjaFTMcnvKBOjq9sv6o50FXmrhQIdHkTUSxAZ0YszWEhxawDWetiT2Hnpl2DkG6AiEC23N8bc" + "YPCV4jdGnVKm7lkHpkU9gzNqNACF57y8vlsWRRZtop18ymQwxNUB0TfFo8IX01YjYWWGGPdZ4d9aR3V3FL43a40XWTf5EGYetVxENlvgmptILWp4WSShdJHFuhtSBqFZ" + "NG9LL5E6eaTcJUy5CetUFwF1ZLBIaM7NqnC1HzDyfbUE6YwZoE3SotSaIwjVfTtcQibn62LodQtVfqkwtnbiTP4ZDDUXrsFakLKKdQtDyl97KJbcgIy7AzatcJbOtv6k" + "TaB3RWYoEa0hVJ9vFbXjTNOWZfQYQffbHvSsIk5wvD7aPJH4GQjII2sB4nMTnPcG8u59HuzITQZkXdOqXOiWK5JGHUVcezyr1pCNQSfCSzjixkbb0q93KYjevgU4URQx" + "40sWaAZ0zItTAhIXMp9I1ZBXXyCiIMbZB8yW62xOXEXeHdoab1J4W98a6TxabU3oYCZcHbpFPPgUYlOOpsfXtLTbmbny7JOm5WAdn9oNB0PslpU6PZ0afwFVUw6QnBAI" + "7ugkUPAg05Q6k6Eo2vKbUceHyTctDxTb8jCAqjnQAjcsdjFdyIrXfxRuPoYlW3hbKNY7owubNosFBnkFTREPWDSaLrUc8mq9XdqRYxkMLAMSur6aal6yCK6BeeNPHdeo" + "KK8CoLNDyioVAhp2hsL5bfjBHKDQCN3ItoK3TD7ALfZlQgF2cQLnvkQdaHqVwMtwkGpIOEUZw0zcDmOVrLcYx5UmptfwkMCbQxrpFlhju1Nry2Ci9bKuRoMeuOeneV1O" + "oKsrFPXQiUcpuhOhNhlvHbnvgzvwxk4tt9TvBzw0fL9vXbDnq9uM6YtKeTW7rRrrs23cwjPJzz3vEhZxC6NkdM71Rc4nOwbrpDdxGwkZ3jSY3Jylo9Lbf0pviXcuJ3g4" + "1rzmp6gP6NEyp2rscQFE4nCr6CaQVIgjhVlsQOFOtjsvQb34oUDIBwGxDtXLPNh0ErUUoIAKxVmcJTLD2XKtme1h38QFqWEfMASip8mWjoSpwhWUT1P89iEoSeNCA6iU" + "X1Onx5LE43j9HoPlZKvYoFA0vErGTMaiNik4pmxurReM6dZIH0THe7boPk4nyih1Jss45PZ0dE5iabinKnwZjVWbyNMFWfrfYPqSbH0No1zeh1uaS2lnCiEAlyGRftQs" + "NPPSFAhPBLFeSpJYiqKeQzE1opvSoiocKki7YY3QrKCddLObN6lO4W7s1mIRF1GYJcrEY9GchljXqlJznDmZ0udaNJCUfAUcynENT8KplC4dLGUPskVd4T9MC4Y5wJ9r" + "ogZz0tPeCqnkC1gEXGSJQsMoqeI5Aa2Z0rsczLr1acHWNjahd6hwmRLcmnKycJAQlqsPUdrylLZaFMcYa6YXUh6kqYG5GEHXd7UAGwNrnm1zeHAUvcHrYOSotYDpV9dC" + "32BgvAPXdjs2G9apgOjZYC8ykWYZtXWuh0YwTpd6HBgqgeCD4MmveJqyLybFQFzdKjl4h8Gzp2m7jpmEgC3TsEEycTWwGdQf8zXEjZ3GKRLzfjrmGlOUmQuMGHmedZxB" + "pPOeCLVaVkzqnz1jP40lEjHzAqhKqKXw4qcv2l5vMAvJ0MJ7PhySWs21rQO6kZcg0uGVSa3Ykl9OQmdiUmO40n5UEx3Gc2WgfVb5nsGae7nkzpFLgrRgo6rZ0ER3bY0R" + "zCjgQx5cJTsZyuw5HasdBKjq6W3t8EYIuDNs9VWoTillo1u8qQca5hztc0oTGv5mTAcxp5mHHn2WKMK8S21OPPBaZRYFMfHj2gfU8omqVLxDzmRPWg3xZLzEqsh1QMGg" + "XQBYlP6BYpl3Yf5Y0cyBJyaz59m8OfL7tySHrJULklZkil5tMAHXWU5H1XMFmC3PvMy1qpRxDZVRvO0oHnIw58Y9pwiLInYnbOCU9400wjr6DeN5mgqzIbUqAkGfHEx5" + "Sy1qz1ZdPIyevxJyRFBqEC6Bi72ICXJh8anlXX253KJv3DQ4QTzBSqSvtmU6VYpduhhXET9x949txeo0IAXcZnHirY5ngkCIygciuFRoTTUHkEKS9kf4fS2nEyaDG09J" + "6K1r7QZCXpoVR8MttHqCPPN03dmvQSBeAMhmwIEDPM38iIqN3qC0hkCMZDrZhDiX7j5fvhoJ9kLjUPIMcu8tKpnJKB3Lv8STTSUdIULglxuDVNl0vg6m8IFUZbHXtl2D" + "J1DFEvqE73HIWFZoeJzVggpWmrDCMuhACkbnGqNGL0lUL3TUvoGBs5gVG57E2LyQZUnRPZYAo0RiOtjR1mR7CLvPphvjOVwkKNj6vibfQOhEjBG5nbkATzHomZocpL5Y" + "cHBZpNeFHh7nP48ahKBzQn49I6HJEaSPWfcfsTX6VF6lKTwQ1Z4dIKLl6lFmwwRMoaOHz0W9QaKuYNOxzD0ntW9xn8mN5sosSFZ7cMdalkIbxS12A02OteW0I7u6ZKGC" + "tmDYAk6d7juw7JdNfJiXpGQYEUkDK1373amxtzWmEu9ftz433ZtFUknfzTVuACgAenpfcVRx6oyLJoclkAk0LLKgE9tyrXjJZzZJWw70YXxn8t3D0jPx88aLYhEA8TfL" + "joAZvqcxBQ8e03pbggZFLqZAIw5ewXQhBgUDgjqsWtKELUeO9pvq6XJJhsZp2eiCahKpZKqxpU0sYBSciW1Co3eCmVLX7NvQYgNZ8epoj4OplLPqYO2oBUXdxTLXpZeG" + "gpcw1f0gsVUgreThXaZjFYcEaF3cTlqRPwayG7d2niO7l4fJ9tYLBPoOhlu0hpwwNMFf5nCccgVa10DCxFUXtG5D6jhBr5DYZcJRwZf3hdI8sFnbmQXigy1pdsmh2sUU" + "R3V09UrxqUwqO31GT7v6wCoRAqmAeNS7WeL8sYztP51JuwOewATVSNNUYU92xVpWgi1gc7zcUSnheJ4lOdTM41Q6Cl47aiTE4WhmbuW1Fgwr7p8SDOYJefMG7qFX9EBZ" + "N4L7tra75NO5qNNAj1l5RqUHvLwfP67vKkAmWdqEQOV2yofT13Hrw3bJrqB9Sh17AJD3jvSOst3sKj4UNkSJRVGJLky6t6XJyPfu9SgCqcybD8GH3A4tcotpKmUyeWaV" + "3Yb49gsjlVWNr118iAFYAtu8LuYIpv0tCDlNmoGnaOr7MYg6AhACoQGhC61W5fh0Rl8EmUYeQwr5n6FsRGANqCUlTzABzvhJeZU8R4yPzK1EKYpsNbuB0OAfjWQ4QfXo" + "3PYVwMTyxFiMTEPF7JExpQ0A0kiHocZCzBZNee4Wbcosp0V9cqlB5X2Mizv87MVqN5qeAlNv8bF3tkUTT9ny25jiWz3piarNZZriHt2Jjwr6drhDmCyAMtOzfTpWgbIo" + "4O2QBp55VldQdVAIfZaruZfYsfT0m3klv0ijQQrSXfmEonDPr9LSi5ex1k0Pt2I42sW7tO3PfHI2ess8NhKTFwaeKbhhJH4cjYWuxETfAT1hcFDp9gFVNv61hZIpPC7B" + "6Re48nr4bugJvEqjP8MgmDwCuP4wPv8whciQ8xnbDANV9atiIp6nILVVPHy6iy8aeNJ5cU8vGHKEfglaW1PF3BhNBoDtbhi2ygT7nRWncGwlpXnrPsnwBuEk1XC1d9wE" + "XSkcpTLsb3jUCod6ahdBtMBWg30E0TmP7daELf3o8SWrOXUHoCad3y9q1El10fCCDfzw7uamJ00EXaLHDNaIOckLowZUttmM1TXTyrk8V330gmOIs3pS6zF2fMgbXfdk" + "Rd7m3ocqHWKxCINsXgZlt7a5LsoulsUktOELiWnNvLJ86qo3BmYk0Ok0gGqnU4WHvVMsiCU3gEmxOxenUWlHM6JSzrk6iW0PYDjEwQvMlT4Msom4GSLDQV2oxpbvWpIg" + "xLQjt8WS6pKhDMdSMXMYproxprmKdxHdsTooFhDuIHtNZQCbqhSvAZLZ7YaVo0JMWVZHMi76eAAjPI6h1BJOmMcuywnhIlMPcSScLtu5NvwQI854m3BUSmnxwlTPrknt" + "AVoDe7cEI45aGbgaXW7ZPPSJ0lYc2OvyaTKJob2OBh8zdnA3ERIBgRXC4cNImoSvxzMBiO7BDf5zroBqceOXxtaEc7y4D7GrekVAEwIjH7DMkg7AXdBrAXKdr97HIj"; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.h b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.h new file mode 100644 index 0000000000..faaabae2d3 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.h @@ -0,0 +1,19 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +namespace TestImpact +{ + // Large text blob in the form of a string literal so the app does not require the Az FileIO and Application environment + extern const char* const LongText; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessMain.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessMain.cpp new file mode 100644 index 0000000000..5c46ee525e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessMain.cpp @@ -0,0 +1,19 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "TestImpactTestProcess.h" + +int main(int argc, char* argv[]) +{ + TestImpact::TestProcess process(argc, argv); + return process.MainFunc(); +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/testimpactframework_testprocess_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/testimpactframework_testprocess_files.cmake new file mode 100644 index 0000000000..a06f1fa706 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/testimpactframework_testprocess_files.cmake @@ -0,0 +1,18 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Source/TestImpactTestProcessMain.cpp + Source/TestImpactTestProcess.cpp + Source/TestImpactTestProcess.h + Source/TestImpactTestProcessLargeText.cpp + Source/TestImpactTestProcessLargeText.h +) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/CMakeLists.txt new file mode 100644 index 0000000000..8298bb7123 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/CMakeLists.txt new file mode 100644 index 0000000000..7748581284 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/CMakeLists.txt @@ -0,0 +1,28 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +################################################################################ +# Tests +################################################################################ + +ly_add_target( + NAME TestImpact.TestTargetA.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE AZ + FILES_CMAKE + testimpactframework_testtargeta_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTestShared + AZ::AzTest +) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/Tests/TestImpactTestTargetA.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/Tests/TestImpactTestTargetA.cpp new file mode 100644 index 0000000000..9598b88b98 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/Tests/TestImpactTestTargetA.cpp @@ -0,0 +1,73 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace UnitTest +{ + class TestFixture + : public AllocatorsTestFixture + { + }; + + TEST(TestCase, Test1_WillPass) + { + SUCCEED(); + } + + TEST(TestCase, Test2_WillPass) + { + SUCCEED(); + } + + TEST(TestCase, Test3_WillPass) + { + SUCCEED(); + } + + TEST(TestCase, Test4_WillPass) + { + SUCCEED(); + } + + TEST(TestCase, Test5_WillPass) + { + SUCCEED(); + } + + TEST(TestCase, Test6_WillPass) + { + SUCCEED(); + } + + TEST(TestCase, Test7_WillFail) + { + FAIL(); + } + + TEST_F(TestFixture, Test1_WillPass) + { + SUCCEED(); + } + + TEST_F(TestFixture, Test2_WillPass) + { + SUCCEED(); + } + + TEST_F(TestFixture, Test3_WillPass) + { + SUCCEED(); + } +} // namespace UnitTest + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/testimpactframework_testtargeta_tests_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/testimpactframework_testtargeta_tests_files.cmake new file mode 100644 index 0000000000..32c8746c9d --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/testimpactframework_testtargeta_tests_files.cmake @@ -0,0 +1,14 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Tests/TestImpactTestTargetA.cpp +) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/CMakeLists.txt new file mode 100644 index 0000000000..8298bb7123 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/CMakeLists.txt new file mode 100644 index 0000000000..57b55fb9f4 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +################################################################################ +# Tests +################################################################################ + +ly_add_target( + NAME TestImpact.TestTargetB.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE AZ + FILES_CMAKE + testimpactframework_testtargetb_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzTestShared + AZ::AzTest +) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/Tests/TestImpactTestTargetB.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/Tests/TestImpactTestTargetB.cpp new file mode 100644 index 0000000000..be58b4fa17 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/Tests/TestImpactTestTargetB.cpp @@ -0,0 +1,78 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +namespace UnitTest +{ + class TestFixture + : public AllocatorsTestFixture + { + }; + + class TestFixtureWithParams + : public AllocatorsTestFixture + , public ::testing::WithParamInterface> + { + }; + + TEST(TestCase, Test1_WillPass) + { + SUCCEED(); + } + + TEST(TestCase, Test2_WillPass) + { + SUCCEED(); + } + + TEST(TestCase, Test3_WillPass) + { + SUCCEED(); + } + + TEST_F(TestFixture, Test1_WillPass) + { + SUCCEED(); + } + + TEST_P(TestFixtureWithParams, Test1_WillPass) + { + SUCCEED(); + } + + TEST_P(TestFixtureWithParams, Test2_WillPass) + { + SUCCEED(); + } + + INSTANTIATE_TEST_CASE_P( + PermutationA, + TestFixtureWithParams, + ::testing::Combine( + ::testing::Values(1, 2, 4), + ::testing::Values(3, 5, 7), + ::testing::Values(-0.0f, 0.0f, 1.0f) + )); + + INSTANTIATE_TEST_CASE_P( + , + TestFixtureWithParams, + ::testing::Combine( + ::testing::Values(8, 16, 32), + ::testing::Values(9, 13, 17), + ::testing::Values(-10.0f, 0.05f, 10.0f) + )); +} // namespace UnitTest + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/testimpactframework_testtargetb_tests_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/testimpactframework_testtargetb_tests_files.cmake new file mode 100644 index 0000000000..949074a5af --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/testimpactframework_testtargetb_tests_files.cmake @@ -0,0 +1,14 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Tests/TestImpactTestTargetB.cpp +) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/CMakeLists.txt new file mode 100644 index 0000000000..8298bb7123 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/CMakeLists.txt new file mode 100644 index 0000000000..47862f80df --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +################################################################################ +# Tests +################################################################################ + +ly_add_target( + NAME TestImpact.TestTargetC.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE AZ + FILES_CMAKE + testimpactframework_testtargetc_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzTestShared + AZ::AzTest +) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/Tests/TestImpactTestTargetC.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/Tests/TestImpactTestTargetC.cpp new file mode 100644 index 0000000000..48f0ffe9dc --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/Tests/TestImpactTestTargetC.cpp @@ -0,0 +1,63 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +namespace UnitTest +{ + class TestFixture + : public AllocatorsTestFixture + { + }; + + template + class TestFixtureWithTypes + : public AllocatorsTestFixture + { + }; + + using TestTypes = testing::Types; + TYPED_TEST_CASE(TestFixtureWithTypes, TestTypes); + + TEST_F(TestFixture, Test1_WillPass) + { + SUCCEED(); + } + + TEST_F(TestFixture, Test2_WillPass) + { + SUCCEED(); + } + + TYPED_TEST(TestFixtureWithTypes, Test1_WillPass) + { + SUCCEED(); + } + + TYPED_TEST(TestFixtureWithTypes, Test2_WillPass) + { + SUCCEED(); + } + + TYPED_TEST(TestFixtureWithTypes, Test3_WillPass) + { + SUCCEED(); + } + + TYPED_TEST(TestFixtureWithTypes, Test4_WillPass) + { + SUCCEED(); + } +} // namespace UnitTest + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/testimpactframework_testtargetc_tests_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/testimpactframework_testtargetc_tests_files.cmake new file mode 100644 index 0000000000..fa5b4f6e9a --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/testimpactframework_testtargetc_tests_files.cmake @@ -0,0 +1,14 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Tests/TestImpactTestTargetC.cpp +) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/CMakeLists.txt new file mode 100644 index 0000000000..8298bb7123 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/CMakeLists.txt new file mode 100644 index 0000000000..bf821ac0d6 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +################################################################################ +# Tests +################################################################################ + +ly_add_target( + NAME TestImpact.TestTargetD.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE AZ + FILES_CMAKE + testimpactframework_testtargetd_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzTestShared + AZ::AzTest +) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/Tests/TestImpactTestTargetD.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/Tests/TestImpactTestTargetD.cpp new file mode 100644 index 0000000000..bd27b51fd9 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/Tests/TestImpactTestTargetD.cpp @@ -0,0 +1,188 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +namespace UnitTest +{ + class TestFixture1 + : public AllocatorsTestFixture + { + }; + + class DISABLED_TestFixture2 + : public AllocatorsTestFixture + { + }; + + template + class TestFixtureWithTypes1 + : public AllocatorsTestFixture + { + }; + + template + class DISABLED_TestFixtureWithTypes2 + : public AllocatorsTestFixture + { + }; + + class TestFixtureWithParams1 + : public AllocatorsTestFixture + , public ::testing::WithParamInterface> + { + }; + + class DISABLED_TestFixtureWithParams2 + : public AllocatorsTestFixture + , public ::testing::WithParamInterface> + { + }; + + using TestTypes = testing::Types; + TYPED_TEST_CASE(TestFixtureWithTypes1, TestTypes); + TYPED_TEST_CASE(DISABLED_TestFixtureWithTypes2, TestTypes); + + TEST(TestCase, Test1_WillPass) + { + SUCCEED(); + } + + TEST(TestCase, DISABLED_Test2_WillPass) + { + SUCCEED(); + } + + TEST(TestCase, Test3_WillPass) + { + SUCCEED(); + } + + TEST(TestCase, Test4_WillPass) + { + SUCCEED(); + } + + TEST(TestCase, Test5_WillPass) + { + SUCCEED(); + } + + TEST_F(TestFixture1, Test1_WillPass) + { + SUCCEED(); + } + + TEST_F(TestFixture1, Test2_WillPass) + { + SUCCEED(); + } + + TEST_F(DISABLED_TestFixture2, Test1_WillPass) + { + SUCCEED(); + } + + TEST_F(DISABLED_TestFixture2, Test2_WillPass) + { + SUCCEED(); + } + + TEST_P(TestFixtureWithParams1, Test1_WillPass) + { + SUCCEED(); + } + + TEST_P(TestFixtureWithParams1, DISABLED_Test2_WillPass) + { + SUCCEED(); + } + + TEST_P(DISABLED_TestFixtureWithParams2, Test1_WillPass) + { + SUCCEED(); + } + + TEST_P(DISABLED_TestFixtureWithParams2, DISABLED_Test2_WillPass) + { + SUCCEED(); + } + + INSTANTIATE_TEST_CASE_P( + PermutationA, + TestFixtureWithParams1, + ::testing::Combine( + ::testing::Values(1, 2, 4), + ::testing::Values(3, 5, 7), + ::testing::Values(-0.0f, 0.0f, 1.0f) + )); + + INSTANTIATE_TEST_CASE_P( + , + TestFixtureWithParams1, + ::testing::Combine( + ::testing::Values(8, 16, 32), + ::testing::Values(9, 13, 17), + ::testing::Values(-10.0f, 0.05f, 10.0f) + )); + + INSTANTIATE_TEST_CASE_P( + PermutationA, + DISABLED_TestFixtureWithParams2, + ::testing::Combine( + ::testing::Values(1, 2, 4), + ::testing::Values(3, 5, 7), + ::testing::Values(-0.0f, 0.0f, 1.0f) + )); + + INSTANTIATE_TEST_CASE_P( + , + DISABLED_TestFixtureWithParams2, + ::testing::Combine( + ::testing::Values(8, 16, 32), + ::testing::Values(9, 13, 17), + ::testing::Values(-10.0f, 0.05f, 10.0f) + )); + + TYPED_TEST(TestFixtureWithTypes1, Test1_WillPass) + { + SUCCEED(); + } + + TYPED_TEST(TestFixtureWithTypes1, DISABLED_Test2_WillPass) + { + SUCCEED(); + } + + TYPED_TEST(TestFixtureWithTypes1, Test3_WillPass) + { + SUCCEED(); + } + + TYPED_TEST(DISABLED_TestFixtureWithTypes2, Test1_WillPass) + { + SUCCEED(); + } + + TYPED_TEST(DISABLED_TestFixtureWithTypes2, DISABLED_Test2_WillPass) + { + SUCCEED(); + } + + TYPED_TEST(DISABLED_TestFixtureWithTypes2, Test3_WillPass) + { + SUCCEED(); + } +} // namespace UnitTest + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/testimpactframework_testtargetd_tests_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/testimpactframework_testtargetd_tests_files.cmake new file mode 100644 index 0000000000..ebb5a422c7 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/testimpactframework_testtargetd_tests_files.cmake @@ -0,0 +1,14 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Tests/TestImpactTestTargetD.cpp +) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake index 74c7c84dcc..fd65a16e39 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake +++ b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake @@ -10,5 +10,20 @@ # set(FILES - Source/Dummy.cpp + Include/TestImpactFramework/TestImpactException.h + Include/TestImpactFramework/TestImpactFrameworkPath.h + Include/TestImpactFramework/TestImpactCallback.h + Source/TestImpactException.cpp + Source/TestImpactFrameworkPath.cpp + Source/Process/TestImpactProcess.cpp + Source/Process/TestImpactProcess.h + Source/Process/TestImpactProcessException.h + Source/Process/TestImpactProcessInfo.cpp + Source/Process/TestImpactProcessInfo.h + Source/Process/TestImpactProcessLauncher.h + Source/Process/JobRunner/TestImpactProcessJob.h + Source/Process/JobRunner/TestImpactProcessJobInfo.h + Source/Process/JobRunner/TestImpactProcessJobRunner.h + Source/Process/Scheduler/TestImpactProcessScheduler.cpp + Source/Process/Scheduler/TestImpactProcessScheduler.h ) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake new file mode 100644 index 0000000000..61059dc543 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake @@ -0,0 +1,22 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Tests/Process/TestImpactProcessSchedulerTest.cpp + Tests/Process/TestImpactProcessTest.cpp + Tests/TestImpactExceptionTest.cpp + Tests/TestImpactFrameworkPathTest.cpp + Tests/TestImpactProcessSchedulerTest.cpp + Tests/TestImpactProcessTest.cpp + Tests/TestImpactProcessTestShared.cpp + Tests/TestImpactProcessTestShared.h + Tests/TestImpactTestMain.cpp +) From 0ebe6c6079f391b53765a580fa0869585f9adb61 Mon Sep 17 00:00:00 2001 From: igarri Date: Thu, 22 Apr 2021 13:19:39 +0100 Subject: [PATCH 002/233] Setup Table model and Table view --- .../AzToolsFramework/AssetBrowserTableModel.h | 36 ++ .../AssetBrowser/AssetBrowserTableModel.cpp | 37 ++ .../AssetBrowser/AssetBrowserTableModel.h | 36 ++ .../Entries/AssetBrowserEntry.cpp | 5 +- .../AssetBrowser/Entries/AssetBrowserEntry.h | 2 + .../Entries/FolderAssetBrowserEntry.cpp | 1 + .../Entries/RootAssetBrowserEntry.cpp | 3 + .../Views/AssetBrowserTableView.cpp | 65 +++ .../Views/AssetBrowserTableView.h | 56 +++ .../AssetBrowser/Views/EntryDelegate.cpp | 44 +-- .../aztoolsframework_files.cmake | 4 + .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 17 + .../AzAssetBrowser/AzAssetBrowserWindow.h | 3 + .../AzAssetBrowser/AzAssetBrowserWindow.ui | 369 ++++++++++-------- 14 files changed, 492 insertions(+), 186 deletions(-) create mode 100644 Code/Framework/AzToolsFramework/AssetBrowserTableModel.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h diff --git a/Code/Framework/AzToolsFramework/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AssetBrowserTableModel.h new file mode 100644 index 0000000000..4e65b3921c --- /dev/null +++ b/Code/Framework/AzToolsFramework/AssetBrowserTableModel.h @@ -0,0 +1,36 @@ +#pragma once +#if !defined(Q_MOC_RUN) +#include +#include +#include + +#include +#include +#include + +AZ_PUSH_DISABLE_WARNING( + 4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...' +#include +#include +#include +#endif +AZ_POP_DISABLE_WARNING +namespace AzToolsFramework +{ + namespace AssetBrowser + { + class AssetBrowserTableModel + : public QSortFilterProxyModel + { + Q_OBJECT + public: + AZ_CLASS_ALLOCATOR(AssetBrowserTableModel, AZ::SystemAllocator, 0); + explicit AssetBrowserTableModel(QObject* parent = nullptr); + + QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + }; + } +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp new file mode 100644 index 0000000000..a6e5cde6d8 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -0,0 +1,37 @@ +#include "AssetBrowserTableModel.h" +namespace AzToolsFramework +{ + namespace AssetBrowser + { + AssetBrowserTableModel::AssetBrowserTableModel(QObject* parent /* = nullptr */) + : QSortFilterProxyModel(parent) + { + } + QModelIndex AssetBrowserTableModel::mapToSource(const QModelIndex& proxyIndex) const + { + AZ_UNUSED(proxyIndex); + return QModelIndex(); + } + QModelIndex AssetBrowserTableModel::mapFromSource(const QModelIndex& sourceIndex) const + { + AZ_UNUSED(sourceIndex); + return QModelIndex(); + } + QModelIndex AssetBrowserTableModel::index(int row, int column, const QModelIndex& parent) const + { + AZ_UNUSED(row); + AZ_UNUSED(column); + AZ_UNUSED(parent); + + return QModelIndex(); + } + QVariant AssetBrowserTableModel::data(const QModelIndex& index, int role) const + { + AZ_UNUSED(index); + AZ_UNUSED(role); + + return QVariant(); + } + } // namespace AssetBrowser +} // namespace AzToolsFramework +#include "AssetBrowser/moc_AssetBrowserTableModel.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h new file mode 100644 index 0000000000..4e65b3921c --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -0,0 +1,36 @@ +#pragma once +#if !defined(Q_MOC_RUN) +#include +#include +#include + +#include +#include +#include + +AZ_PUSH_DISABLE_WARNING( + 4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...' +#include +#include +#include +#endif +AZ_POP_DISABLE_WARNING +namespace AzToolsFramework +{ + namespace AssetBrowser + { + class AssetBrowserTableModel + : public QSortFilterProxyModel + { + Q_OBJECT + public: + AZ_CLASS_ALLOCATOR(AssetBrowserTableModel, AZ::SystemAllocator, 0); + explicit AssetBrowserTableModel(QObject* parent = nullptr); + + QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + }; + } +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.cpp index 85d5eaecea..8ee8f44137 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.cpp @@ -46,6 +46,7 @@ namespace AzToolsFramework const char* AssetBrowserEntry::m_columnNames[] = { "Name", + "Path", "Source ID", "Fingerprint", "Guid", @@ -128,6 +129,8 @@ namespace AzToolsFramework return QString::fromUtf8(m_name.c_str()); case Column::DisplayName: return m_displayName; + case Column::Path: + return m_displayPath; default: return QVariant(); } @@ -283,4 +286,4 @@ namespace AzToolsFramework } // namespace AssetBrowser } // namespace AzToolsFramework -#include "AssetBrowser/Entries/moc_AssetBrowserEntry.cpp" \ No newline at end of file +#include "AssetBrowser/Entries/moc_AssetBrowserEntry.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h index 6f0eceeeea..ac5470a136 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h @@ -68,6 +68,7 @@ namespace AzToolsFramework enum class Column { Name, + Path, SourceID, Fingerprint, Guid, @@ -135,6 +136,7 @@ namespace AzToolsFramework protected: AZStd::string m_name; QString m_displayName; + QString m_displayPath; AZStd::string m_relativePath; AZStd::string m_fullPath; AZStd::vector m_children; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.cpp index 88b26ad5ee..0312edbc51 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.cpp @@ -43,6 +43,7 @@ namespace AzToolsFramework void FolderAssetBrowserEntry::UpdateChildPaths(AssetBrowserEntry* child) const { child->m_relativePath = m_relativePath + AZ_CORRECT_DATABASE_SEPARATOR + child->m_name; + child->m_displayPath = QString::fromUtf8(child->m_relativePath.c_str()); child->m_fullPath = m_fullPath + AZ_CORRECT_DATABASE_SEPARATOR + child->m_name; AssetBrowserEntry::UpdateChildPaths(child); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.cpp index ada961711a..2ee71c8719 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.cpp @@ -292,6 +292,9 @@ namespace AzToolsFramework product->m_assetType = productWithUuidDatabaseEntry.second.m_assetType; product->m_assetType.ToString(product->m_assetTypeString); AZ::Data::AssetCatalogRequestBus::BroadcastResult(product->m_relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, assetId); + QString displayPath = QString::fromUtf8(product->m_relativePath.c_str()); + displayPath.remove(QString("/" + QString::fromUtf8(product->m_name.c_str()))); + product->m_displayPath = displayPath; EntryCache::GetInstance()->m_productAssetIdMap[assetId] = product; if (needsAdd) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp new file mode 100644 index 0000000000..ee86f372db --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -0,0 +1,65 @@ +#include "AssetBrowserTableView.h" + +#pragma optimize("", off) +namespace AzToolsFramework +{ + namespace AssetBrowser + { + AssetBrowserTableView::AssetBrowserTableView(QWidget* parent) + : QTableView(parent) + { + AssetBrowserViewRequestBus::Handler::BusConnect(); + AssetBrowserComponentNotificationBus::Handler::BusConnect(); + } + AssetBrowserTableView::~AssetBrowserTableView() + { + AssetBrowserViewRequestBus::Handler::BusDisconnect(); + AssetBrowserComponentNotificationBus::Handler::BusDisconnect(); + } + void AssetBrowserTableView::setModel(QAbstractItemModel* model) + { + //m_assetBrowserSortFilterProxyModel = qobject_cast(model); + //AZ_Assert(m_assetBrowserSortFilterProxyModel, "Expecting AssetBrowserTableFilterModel"); + //m_assetBrowserModel = qobject_cast(m_assetBrowserSortFilterProxyModel->sourceModel()); + QTableView::setModel(model); + } + void AssetBrowserTableView::SetName(const QString& name) + { + m_name = name; + bool isAssetBrowserComponentReady = false; + AssetBrowserComponentRequestBus::BroadcastResult(isAssetBrowserComponentReady, &AssetBrowserComponentRequests::AreEntriesReady); + if (isAssetBrowserComponentReady) + { + OnAssetBrowserComponentReady(); + } + } + AZStd::vector AssetBrowserTableView::GetSelectedAssets() const + { + return AZStd::vector(); + } + + void AssetBrowserTableView::SelectProduct(AZ::Data::AssetId assetID) + { + AZ_UNUSED(assetID); + } + + void AssetBrowserTableView::SelectFileAtPath(const AZStd::string& assetPath) + { + AZ_UNUSED(assetPath); + } + + void AssetBrowserTableView::ClearFilter() + { + } + + void AssetBrowserTableView::Update() + { + } + + //void AssetBrowserTableView::OnAssetBrowserComponentReady() + //{ + //} + } // namespace AssetBrowser +} // namespace AzToolsFramework +#pragma optimize("", on) +#include "AssetBrowser/Views/moc_AssetBrowserTableView.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h new file mode 100644 index 0000000000..04881aa782 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -0,0 +1,56 @@ +#pragma once +#if !defined(Q_MOC_RUN) +#include +#include +#include + +#include +//#include + +#include +#include +#include +#endif + +namespace AzToolsFramework +{ + namespace AssetBrowser + { + class AssetBrowserEntry; + class AssetBrowserModel; + class AssetBrowserFilterModel; + class EntryDelegate; + + class AssetBrowserTableView + : public QTableView + , public AssetBrowserViewRequestBus::Handler + , public AssetBrowserComponentNotificationBus::Handler + { + Q_OBJECT + public: + explicit AssetBrowserTableView(QWidget* parent = nullptr); + ~AssetBrowserTableView() override; + + void setModel(QAbstractItemModel *model) override; + void SetName(const QString& name); + + AZStd::vector GetSelectedAssets() const; + + ////////////////////////////////////////////////////////////////////////// + // AssetBrowserViewRequestBus + virtual void SelectProduct(AZ::Data::AssetId assetID) override; + virtual void SelectFileAtPath(const AZStd::string& assetPath) override; + virtual void ClearFilter() override; + virtual void Update() override; + + ////////////////////////////////////////////////////////////////////////// + // AssetBrowserComponentNotificationBus + //void OnAssetBrowserComponentReady() override; + ////////////////////////////////////////////////////////////////////////// + private: + QString m_name; + + + }; + } // namespace AssetBrowser +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index abc290a406..42e161cf37 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -74,34 +74,34 @@ namespace AzToolsFramework QPoint iconTopLeft(remainingRect.x(), remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2)); auto sourceEntry = azrtti_cast(entry); - - int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey()); - QPalette actualPalette(option.palette); - - if (sourceEntry) + if (index.column() == static_cast(AssetBrowserEntry::Column::Name)) { - if (m_showSourceControl) + int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey()); + if (sourceEntry) { - DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey()); + if (m_showSourceControl) + { + DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey()); + } + // sources with no children should be greyed out. + if (sourceEntry->GetChildCount() == 0) + { + isEnabled = false; // draw in disabled style. + actualPalette.setCurrentColorGroup(QPalette::Disabled); + } } - // sources with no children should be greyed out. - if (sourceEntry->GetChildCount() == 0) - { - isEnabled = false; // draw in disabled style. - actualPalette.setCurrentColorGroup(QPalette::Disabled); - } - } - remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail - remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing. + remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail + remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing. + } + QString displayString = qvariant_cast(index.data(index.column())); - style->drawItemText(painter, - remainingRect, - option.displayAlignment, - actualPalette, - isEnabled, - entry->GetDisplayName(), + style->drawItemText( + painter, remainingRect, option.displayAlignment, actualPalette, isEnabled, + index.column() == static_cast(AssetBrowserEntry::Column::Name) + ? qvariant_cast(entry->data(static_cast(AssetBrowserEntry::Column::Name))) + : qvariant_cast(entry->data(static_cast(AssetBrowserEntry::Column::Path))), isSelected ? QPalette::HighlightedText : QPalette::Text); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 2939bb44ad..51ca1a3fdb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -545,6 +545,8 @@ set(FILES AssetBrowser/AssetBrowserEntry.h AssetBrowser/AssetBrowserFilterModel.cpp AssetBrowser/AssetBrowserFilterModel.h + AssetBrowser/AssetBrowserTableModel.cpp + AssetBrowser/AssetBrowserTableModel.h AssetBrowser/AssetBrowserModel.cpp AssetBrowser/AssetBrowserModel.h AssetBrowser/AssetEntryChange.h @@ -555,6 +557,8 @@ set(FILES AssetBrowser/EBusFindAssetTypeByName.h AssetBrowser/Views/AssetBrowserTreeView.cpp AssetBrowser/Views/AssetBrowserTreeView.h + AssetBrowser/Views/AssetBrowserTableView.cpp + AssetBrowser/Views/AssetBrowserTableView.h AssetBrowser/Views/EntryDelegate.cpp AssetBrowser/Views/EntryDelegate.h AssetBrowser/Views/AssetBrowserFolderWidget.cpp diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 0b23739bb8..733044746e 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include // AzQtComponents @@ -66,6 +67,7 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) : QWidget(parent) , m_ui(new Ui::AzAssetBrowserWindowClass()) , m_filterModel(new AzToolsFramework::AssetBrowser::AssetBrowserFilterModel(parent)) + , m_tableModel(new AzToolsFramework::AssetBrowser::AssetBrowserTableModel(parent)) { m_ui->setupUi(this); m_ui->m_searchWidget->Setup(true, true); @@ -76,7 +78,12 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_filterModel->setSourceModel(m_assetBrowserModel); m_filterModel->SetFilter(m_ui->m_searchWidget->GetFilter()); + m_tableModel->setSourceModel(m_filterModel.data()); + m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); + m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data()); + + m_ui->m_assetBrowserTableViewWidget->setVisible(false); connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), &AssetBrowserFilterModel::filterUpdatedSlot); @@ -92,7 +99,11 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, &SearchWidget::ClearStringFilter); connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter); + m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); + m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main"); + + connect(m_ui->m_viewSwitcherCheckBox, &QCheckBox::stateChanged, this, &AzAssetBrowserWindow::SwitchDisplayView); } AzAssetBrowserWindow::~AzAssetBrowserWindow() @@ -220,4 +231,10 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& } +void AzAssetBrowserWindow::SwitchDisplayView(const int state) +{ + m_ui->m_assetBrowserTableViewWidget->setVisible(state); + m_ui->m_assetBrowserTreeViewWidget->setVisible(!state); +} + #include diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h index 263712dfe2..1a5604b98f 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h @@ -29,6 +29,7 @@ namespace AzToolsFramework namespace AssetBrowser { class AssetBrowserFilterModel; + class AssetBrowserTableModel; class AssetBrowserModel; } } @@ -53,6 +54,7 @@ private: QScopedPointer m_ui; QScopedPointer m_filterModel; + QScopedPointer m_tableModel; AzToolsFramework::AssetBrowser::AssetBrowserModel* m_assetBrowserModel; void UpdatePreview() const; @@ -60,6 +62,7 @@ private: private Q_SLOTS: void SelectionChangedSlot(const QItemSelection& selected, const QItemSelection& deselected) const; void DoubleClickedItem(const QModelIndex& element); + void SwitchDisplayView(const int state); }; extern const char* AZ_ASSET_BROWSER_PREVIEW_NAME; diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui index d6a6b4b8df..d9e42ef906 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui @@ -1,168 +1,211 @@ - AzAssetBrowserWindowClass - - - - 0 - 0 - 691 - 554 - - - - Asset Browser - - - - 0 - - - - - - 1 - 1 - - - - true - - - - - 0 - 0 - 671 - 534 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - 0 - 0 - - - - + AzAssetBrowserWindowClass + + + + 0 + 0 + 691 + 554 + + + + Asset Browser + + + + 0 + + + + + + 1 + 1 + + + + true + + + + + 0 + 0 + 671 + 534 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + + + + + Switch View + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + false + + + + + 0 + 0 + + + + vertical-align: top + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed + + + QAbstractItemView::DropOnly + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + QAbstractItemView::ScrollPerPixel + + + false + + + true + + + + + + + + 1 + 0 + + + + QAbstractItemView::DragOnly + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + + + - - - - - - 0 - 0 - - - - Qt::Horizontal - - - false - - - - - 0 - 0 - - - - vertical-align: top - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 1 - 0 - - - - QAbstractItemView::DragOnly - - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - - - - - - - AzToolsFramework::AssetBrowser::SearchWidget - QWidget -
AzToolsFramework/AssetBrowser/Search/SearchWidget.h
- 1 -
- - AzToolsFramework::AssetBrowser::AssetBrowserTreeView - QTreeView -
AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h
-
- - AzToolsFramework::AssetBrowser::PreviewerFrame - QFrame -
AzToolsFramework/AssetBrowser/Previewer/PreviewerFrame.h
- 1 -
-
- - + + + AzToolsFramework::AssetBrowser::SearchWidget + QWidget +
AzToolsFramework/AssetBrowser/Search/SearchWidget.h
+ 1 +
+ + AzToolsFramework::AssetBrowser::AssetBrowserTreeView + QTreeView +
AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h
+
+ + AzToolsFramework::AssetBrowser::PreviewerFrame + QFrame +
AzToolsFramework/AssetBrowser/Previewer/PreviewerFrame.h
+ 1 +
+ + AzToolsFramework::AssetBrowser::AssetBrowserTableView + QTableView +
AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h
+
+
+ +
From b7c2495911484fa0e22a57e77534c3fffdb678d0 Mon Sep 17 00:00:00 2001 From: igarri Date: Wed, 28 Apr 2021 09:38:43 +0100 Subject: [PATCH 003/233] Working base --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 2 + .../AssetBrowser/AssetBrowserFilterModel.h | 1 + .../AssetBrowser/AssetBrowserTableModel.cpp | 92 ++++++++++++++++--- .../AssetBrowser/AssetBrowserTableModel.h | 18 +++- .../Views/AssetBrowserTableView.cpp | 74 +++++++++++++-- .../Views/AssetBrowserTableView.h | 17 +++- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 4 + 7 files changed, 183 insertions(+), 25 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index 9c60b6cd35..42498eb76e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -62,6 +62,7 @@ namespace AzToolsFramework invalidateFilter(); m_invalidateFilter = false; } + Q_EMIT entriesUpdated(); } bool AssetBrowserFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const @@ -162,6 +163,7 @@ namespace AzToolsFramework { m_alreadyRecomputingFilters = false; FilterUpdatedSlotImmediate(); + //beginInsertRows() } ); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h index f9423b2534..6aa1a08e45 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h @@ -53,6 +53,7 @@ namespace AzToolsFramework Q_SIGNALS: void filterChanged(); + void entriesUpdated(); ////////////////////////////////////////////////////////////////////////// //QSortFilterProxyModel diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index a6e5cde6d8..e7a49b2d99 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -6,31 +6,97 @@ namespace AzToolsFramework AssetBrowserTableModel::AssetBrowserTableModel(QObject* parent /* = nullptr */) : QSortFilterProxyModel(parent) { + m_showColumn.insert(static_cast(AssetBrowserEntry::Column::DisplayName)); + m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); } QModelIndex AssetBrowserTableModel::mapToSource(const QModelIndex& proxyIndex) const { - AZ_UNUSED(proxyIndex); - return QModelIndex(); + Q_ASSERT(!proxyIndex.isValid() || proxyIndex.model() == this); + if (!proxyIndex.isValid()) + { + return QModelIndex(); + } + return m_indexMap[proxyIndex.row()]; } QModelIndex AssetBrowserTableModel::mapFromSource(const QModelIndex& sourceIndex) const { - AZ_UNUSED(sourceIndex); - return QModelIndex(); + Q_ASSERT(!sourceIndex.isValid() || sourceIndex.model() == sourceModel()); + if (!sourceIndex.isValid()) + { + return QModelIndex(); + } + return createIndex(m_rowMap[sourceIndex], sourceIndex.column(), sourceIndex.internalPointer()); } - QModelIndex AssetBrowserTableModel::index(int row, int column, const QModelIndex& parent) const + //QModelIndex AssetBrowserTableModel::index(int row, int column, const QModelIndex& parent) const + //{ + + // //return parent.isValid() ? QModelIndex() : createIndex(row, column , m_indexMap[row].internalPointer()); + // if (!parent.isValid()) + // { + // QModelIndex(); + // } + // return createIndex(row, column, m_indexMap[row].internalPointer()); + //} + QVariant AssetBrowserTableModel::data(const QModelIndex& index, int role) const { - AZ_UNUSED(row); - AZ_UNUSED(column); - AZ_UNUSED(parent); + //AZ_UNUSED(role); + auto sourceIndex = mapToSource(index); + if (!sourceIndex.isValid()) + return QVariant(); + + AssetBrowserEntry* entry = GetAssetEntry(sourceIndex); // static_cast(sourceIndex.internalPointer()); + if (entry == nullptr) + { + AZ_Assert(false, "ERROR - index internal pointer not pointing to an AssetEntry. Tree provided by the AssetBrowser invalid?"); + return Qt::PartiallyChecked; + } + + return sourceIndex.data(role); //return entry->data(index.column()); + //return QVariant::fromValue(entry); - return QModelIndex(); } - QVariant AssetBrowserTableModel::data(const QModelIndex& index, int role) const + bool AssetBrowserTableModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const + { + AZ_UNUSED(source_row); + AZ_UNUSED(source_parent); + return true; + } + bool AssetBrowserTableModel::filterAcceptsColumn(int source_column, const QModelIndex&) const { - AZ_UNUSED(index); - AZ_UNUSED(role); + return m_showColumn.find(source_column) != m_showColumn.end(); + } + int AssetBrowserTableModel::BuildMap(const QAbstractItemModel* model, const QModelIndex& parent, int row) + { + int rows = model ? model->rowCount(parent) : 0; + for (int i = 0; i < rows; ++i) + { + auto index = model->index(i, 0, parent); - return QVariant(); + m_rowMap[index] = row; + m_indexMap[row] = index; + row = row + 1; + if (model->hasChildren(index)) + { + row = BuildMap(model, index, row); + } + } + return row; + } + AssetBrowserEntry* AssetBrowserTableModel::GetAssetEntry(QModelIndex index) const + { + if (index.isValid()) + { + return static_cast(index.internalPointer()); + } + else + { + AZ_Error("AssetBrowser", false, "Invalid Source Index provided to GetAssetEntry."); + return nullptr; + } + } + void AssetBrowserTableModel::UpdateMap() + { + BuildMap(sourceModel()); } } // namespace AssetBrowser } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index 4e65b3921c..ef87ad8512 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -29,8 +29,24 @@ namespace AzToolsFramework QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; - QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + //QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + public Q_SLOTS: + void UpdateMap(); + + protected: + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; + bool filterAcceptsColumn(int source_column, const QModelIndex& /*source_parent*/) const override; + + private: + int BuildMap(const QAbstractItemModel* model, const QModelIndex& parent = QModelIndex(), int row = 0); + AssetBrowserEntry* GetAssetEntry(QModelIndex index) const; + + private: + AZStd::fixed_unordered_set(AssetBrowserEntry::Column::Count)> m_showColumn; + QMap m_indexMap; + QMap m_rowMap; }; } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp index ee86f372db..927deb0039 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -1,5 +1,34 @@ -#include "AssetBrowserTableView.h" +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +AZ_PUSH_DISABLE_WARNING( + 4244 4251 4800, "-Wunknown-warning-option") // conversion from 'int' to 'float', possible loss of data, needs to have dll-interface to + // be used by clients of class 'QFlags::Int': forcing value to bool + // 'true' or 'false' (performance warning) +#include +#include +#include +#include +#include +#include +#include +AZ_POP_DISABLE_WARNING #pragma optimize("", off) namespace AzToolsFramework { @@ -7,7 +36,19 @@ namespace AzToolsFramework { AssetBrowserTableView::AssetBrowserTableView(QWidget* parent) : QTableView(parent) + , m_delegate(new EntryDelegate(this)) + { + setSortingEnabled(true); + setItemDelegate(m_delegate); + // header()->hide(); + setContextMenuPolicy(Qt::CustomContextMenu); + + setMouseTracking(true); + + connect(this, &QTableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu); + //connect(m_scTimer, &QTimer::timeout, this, &AssetBrowserTreeView::OnUpdateSCThumbnailsList); + AssetBrowserViewRequestBus::Handler::BusConnect(); AssetBrowserComponentNotificationBus::Handler::BusConnect(); } @@ -18,9 +59,9 @@ namespace AzToolsFramework } void AssetBrowserTableView::setModel(QAbstractItemModel* model) { - //m_assetBrowserSortFilterProxyModel = qobject_cast(model); - //AZ_Assert(m_assetBrowserSortFilterProxyModel, "Expecting AssetBrowserTableFilterModel"); - //m_assetBrowserModel = qobject_cast(m_assetBrowserSortFilterProxyModel->sourceModel()); + m_sourceModel = qobject_cast(model); + AZ_Assert(m_sourceModel, "Expecting AssetBrowserTableModel"); + m_sourceFilterModel = qobject_cast(m_sourceModel->sourceModel()); QTableView::setModel(model); } void AssetBrowserTableView::SetName(const QString& name) @@ -53,12 +94,31 @@ namespace AzToolsFramework } void AssetBrowserTableView::Update() + { + update(); + } + + void AssetBrowserTableView::OnAssetBrowserComponentReady() { } - //void AssetBrowserTableView::OnAssetBrowserComponentReady() - //{ - //} + void AssetBrowserTableView::OnContextMenu(const QPoint& point) + { + AZ_UNUSED(point); + + auto selectedAssets = GetSelectedAssets(); + if (selectedAssets.size() != 1) + { + return; + } + + QMenu menu(this); + AssetBrowserInteractionNotificationBus::Broadcast(&AssetBrowserInteractionNotificationBus::Events::AddContextMenuActions, this, &menu, selectedAssets); + if (!menu.isEmpty()) + { + menu.exec(QCursor::pos()); + } + } } // namespace AssetBrowser } // namespace AzToolsFramework #pragma optimize("", on) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h index 04881aa782..bec24cca9b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -5,7 +5,7 @@ #include #include -//#include +#include #include #include @@ -17,7 +17,7 @@ namespace AzToolsFramework namespace AssetBrowser { class AssetBrowserEntry; - class AssetBrowserModel; + class AssetBrowserTableModel; class AssetBrowserFilterModel; class EntryDelegate; @@ -45,11 +45,20 @@ namespace AzToolsFramework ////////////////////////////////////////////////////////////////////////// // AssetBrowserComponentNotificationBus - //void OnAssetBrowserComponentReady() override; + void OnAssetBrowserComponentReady() override; ////////////////////////////////////////////////////////////////////////// + + private Q_SLOTS: + void OnContextMenu(const QPoint& point); + + //! Get all visible source entries and place them in a queue to update their source control status + //void OnUpdateSCThumbnailsList(); + private: QString m_name; - + QPointer m_sourceFilterModel = nullptr; + QPointer m_sourceModel = nullptr; + EntryDelegate* m_delegate = nullptr; }; } // namespace AssetBrowser diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 733044746e..d67ee0d0ca 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -78,13 +78,17 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_filterModel->setSourceModel(m_assetBrowserModel); m_filterModel->SetFilter(m_ui->m_searchWidget->GetFilter()); + m_tableModel->setFilterRole(Qt::DisplayRole); m_tableModel->setSourceModel(m_filterModel.data()); + //m_tableModel->setSourceModel(m_assetBrowserModel); m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data()); m_ui->m_assetBrowserTableViewWidget->setVisible(false); + connect(m_filterModel.data(), &AssetBrowserFilterModel::entriesUpdated, m_tableModel.data(), &AssetBrowserTableModel::UpdateMap); + connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), &AssetBrowserFilterModel::filterUpdatedSlot); connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, this, [this]() From db630794c8badee93b3bb2549defa733ce7d48b6 Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 28 Apr 2021 14:18:40 +0100 Subject: [PATCH 004/233] Update TIAF artifact file formats and test target enumeration. --- cmake/TestImpactFramework/EnumeratedTests.in | 48 ++++++++------- .../LYTestImpactFramework.cmake | 58 +++++++++---------- .../SourceToTargetMapping.in | 27 +++++---- 3 files changed, 72 insertions(+), 61 deletions(-) diff --git a/cmake/TestImpactFramework/EnumeratedTests.in b/cmake/TestImpactFramework/EnumeratedTests.in index acd33fb661..02eb648f5f 100644 --- a/cmake/TestImpactFramework/EnumeratedTests.in +++ b/cmake/TestImpactFramework/EnumeratedTests.in @@ -1,23 +1,31 @@ -#Lumberyard enumerated tests -[google] -[google.test] -tests = [ -${google_tests} -] -[google.benchmark] -tests = [ +{ + "google": { + "benchmark": { + "tests": [ ${google_benchmarks} -] -[python] -[python.test] -tests = [ -${python_tests} -] -[python.editor] -tests = [ + ] + }, + "test": { + "tests": [ +${google_tests} + ] + } + }, + "python": { + "editor": { + "tests": [ ${python_editor_tests} -] -[unknown] -tests = [ + ] + }, + "test": { + "tests": [ +${python_tests} + ] + } + }, + "unknown": { + "tests": [ ${unknown_tests} -] \ No newline at end of file + ] + } +} \ No newline at end of file diff --git a/cmake/TestImpactFramework/LYTestImpactFramework.cmake b/cmake/TestImpactFramework/LYTestImpactFramework.cmake index c10c5bf637..c08c64db80 100644 --- a/cmake/TestImpactFramework/LYTestImpactFramework.cmake +++ b/cmake/TestImpactFramework/LYTestImpactFramework.cmake @@ -25,7 +25,7 @@ set(LY_TEST_IMPACT_WORKING_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/TestImpactFram set(LY_TEST_IMPACT_TEMP_DIR "${LY_TEST_IMPACT_WORKING_DIR}/Temp") # Directory for static artifacts produced as part of the build system generation process -set(LY_TEST_IMPACT_ARTIFACT_DIR "${LY_TEST_IMPACT_WORKING_DIR}/Artefact") +set(LY_TEST_IMPACT_ARTIFACT_DIR "${LY_TEST_IMPACT_WORKING_DIR}/Artifact") # Directory for source to build target mappings set(LY_TEST_IMPACT_SOURCE_TARGET_MAPPING_DIR "${LY_TEST_IMPACT_ARTIFACT_DIR}/Mapping") @@ -77,28 +77,28 @@ function(ly_test_impact_rebase_files_to_repo_root INPUT_FILES OUTPUT_FILES RELAT set(${OUTPUT_FILES} ${rebased_files} PARENT_SCOPE) endfunction() -#! ly_test_impact_get_target_type_string: gets the target type string (either executable, dynalib or unknown) for the specified target. +#! ly_test_impact_get_test_launch_method: gets the launch method (either standalone or testrunner) for the specified target. # # \arg:TARGET_NAME name of the target -# \arg:TARGET_TYPE the type string for the specified target -function(ly_test_impact_get_target_type_string TARGET_NAME TARGET_TYPE) - # Get the test impact framework-friendly target type string +# \arg:LAUNCH_METHOD the type string for the specified target +function(ly_test_impact_get_test_launch_method TARGET_NAME LAUNCH_METHOD) + # Get the test impact framework-friendly launch method string get_target_property(target_type ${TARGET_NAME} TYPE) if("${target_type}" STREQUAL "SHARED_LIBRARY" OR "${target_type}" STREQUAL "MODULE_LIBRARY") - set(${TARGET_TYPE} "dynlib" PARENT_SCOPE) + set(${LAUNCH_METHOD} "test_runner" PARENT_SCOPE) elseif("${target_type}" STREQUAL "EXECUTABLE") - set(${TARGET_TYPE} "executable" PARENT_SCOPE) + set(${LAUNCH_METHOD} "stand_alone" PARENT_SCOPE) else() - set(${TARGET_TYPE} "unknown" PARENT_SCOPE) + message(FATAL_ERROR "Cannot deduce test target launch method for the target type ${target_type}") endif() endfunction() #! ly_test_impact_extract_google_test: explodes a composite google test string into namespace, test and suite components. # # \arg:COMPOSITE_TEST test in the form 'namespace::test' -# \arg:TEST_QUALIFER qualifier for the test (namespace) +# \arg:TEST_NAMESPACE namespace for the test # \arg:TEST_NAME name of test -function(ly_test_impact_extract_google_test COMPOSITE_TEST TEST_QUALIFER TEST_NAME) +function(ly_test_impact_extract_google_test COMPOSITE_TEST TEST_NAMESPACE TEST_NAME) get_property(test_components GLOBAL PROPERTY LY_ALL_TESTS_${COMPOSITE_TEST}_TEST_NAME) # Namespace and test are mandetiry string(REPLACE "::" ";" test_components ${test_components}) @@ -107,19 +107,19 @@ function(ly_test_impact_extract_google_test COMPOSITE_TEST TEST_QUALIFER TEST_NA message(FATAL_ERROR "The test ${test_components} appears to have been specified without a namespace, i.e.:\ly_add_googletest/benchmark(NAME ${test_components})\nInstead of (perhaps):\ly_add_googletest/benchmark(NAME Gem::${test_components})\nPlease add the missing namespace before proceeding.") endif() - list(GET test_components 0 test_qualifier) + list(GET test_components 0 test_namespace) list(GET test_components 1 test_name) - set(${TEST_QUALIFER} ${test_qualifier} PARENT_SCOPE) + set(${TEST_NAMESPACE} ${test_namespace} PARENT_SCOPE) set(${TEST_NAME} ${test_name} PARENT_SCOPE) endfunction() #! ly_test_impact_extract_python_test: explodes a composite python test string into filename, namespace, test and suite components. # # \arg:COMPOSITE_TEST test in form 'namespace::test' or 'test' -# \arg:TEST_QUALIFER qualifier for the test (optional) +# \arg:TEST_NAMESPACE namespace for the test (optional) # \arg:TEST_NAME name of test # \arg:TEST_FILE the Python script path for this test -function(ly_test_impact_extract_python_test COMPOSITE_TEST TEST_QUALIFER TEST_NAME TEST_FILE) +function(ly_test_impact_extract_python_test COMPOSITE_TEST TEST_NAMESPACE TEST_NAME TEST_FILE) get_property(test_components GLOBAL PROPERTY LY_ALL_TESTS_${COMPOSITE_TEST}_TEST_NAME) get_property(test_file GLOBAL PROPERTY LY_ALL_TESTS_${COMPOSITE_TEST}_SCRIPT_PATH) @@ -127,10 +127,10 @@ function(ly_test_impact_extract_python_test COMPOSITE_TEST TEST_QUALIFER TEST_NA string(REPLACE "::" ";" test_components ${test_components}) list(LENGTH test_components num_test_components) if(num_test_components GREATER 1) - list(GET test_components 0 test_qualifier) + list(GET test_components 0 test_namespace) list(GET test_components 1 test_name) else() - set(test_qualifier "") + set(test_namespace "") set(test_name ${test_components}) endif() @@ -141,7 +141,7 @@ function(ly_test_impact_extract_python_test COMPOSITE_TEST TEST_QUALIFER TEST_NA ${LY_ROOT_FOLDER} ) - set(${TEST_QUALIFER} ${test_qualifier} PARENT_SCOPE) + set(${TEST_NAMESPACE} ${test_namespace} PARENT_SCOPE) set(${TEST_NAME} ${test_name} PARENT_SCOPE) set(${TEST_FILE} ${test_file} PARENT_SCOPE) endfunction() @@ -166,24 +166,24 @@ function(ly_test_impact_write_test_enumeration_file TEST_ENUMERATION_TEMPLATE_FI get_property(test_suite GLOBAL PROPERTY LY_ALL_TESTS_${test}_TEST_SUITE) if("${test_type}" STREQUAL "pytest") # Python tests - ly_test_impact_extract_python_test(${test} test_qualifier test_name test_file) - list(APPEND python_tests "{ name = \"${test_name}\", qualifier = \"${test_qualifier}\", suite = \"${test_suite}\", path = \"${test_file}\" }") + ly_test_impact_extract_python_test(${test} test_namespace test_name test_file) + list(APPEND python_tests " { \"name\": \"${test_name}\", \"namespace\": \"${test_namespace}\", \"suite\": \"${test_suite}\", \"path\": \"${test_file}\" }") elseif("${test_type}" STREQUAL "pytest_editor") # Python editor tests - ly_test_impact_extract_python_test(${test} test_qualifier test_name test_file) - list(APPEND python_editor_tests "{ name = \"${test_name}\", qualifier = \"${test_qualifier}\", suite = \"${test_suite}\", path = \"${test_file}\" }") + ly_test_impact_extract_python_test(${test} test_namespace test_name test_file) + list(APPEND python_editor_tests " { \"name\": \"${test_name}\", \"namespace\": \"${test_namespace}\", \"suite\": \"${test_suite}\", \"path\": \"${test_file}\" }") elseif("${test_type}" STREQUAL "googletest") # Google tests - ly_test_impact_extract_google_test(${test} test_qualifier test_name) - ly_test_impact_get_target_type_string(${test_name} target_type) - list(APPEND google_tests "{ name = \"${test_name}\", qualifier = \"${test_qualifier}\", suite = \"${test_suite}\", build_type = \"${target_type}\" }") + ly_test_impact_extract_google_test(${test} test_namespace test_name) + ly_test_impact_get_test_launch_method(${test_name} launch_method) + list(APPEND google_tests " { \"name\": \"${test_name}\", \"namespace\": \"${test_namespace}\", \"suite\": \"${test_suite}\", \"launch_method\": \"${launch_method}\" }") elseif("${test_type}" STREQUAL "googlebenchmark") # Google benchmarks - ly_test_impact_extract_google_test(${test} test_qualifier test_name) - list(APPEND google_benchmarks "{ name = \"${test_name}\", qualifier = \"${test_qualifier}\", suite = \"${test_suite}\" }") + ly_test_impact_extract_google_test(${test} test_namespace test_name) + list(APPEND google_benchmarks " { \"name\": \"${test_name}\", \"namespace\": \"${test_namespace}\", \"suite\": \"${test_suite}\" }") else() message("${test} is of unknown type (TEST_LIBRARY property is empty)") - list(APPEND unknown_tests "{ name = \"${test}\" }") + list(APPEND unknown_tests " { \"name\": \"${test}\" }") endif() endforeach() @@ -279,7 +279,7 @@ function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE PERSISTENT_DATA_D if(NOT LY_TEST_IMPACT_INSTRUMENTATION_BIN) message(FATAL_ERROR "No test impact framework instrumentation binary was specified, please provide the path with option LY_TEST_IMPACT_INSTRUMENTATION_BIN") endif() - set(instrumentation_bin ${LY_TEST_IMPACT_INSTRUMENTATION_BIN}) + file(TO_CMAKE_PATH ${LY_TEST_IMPACT_INSTRUMENTATION_BIN} instrumentation_bin) # test impact framework working dir ly_test_impact_rebase_file_to_repo_root( @@ -323,7 +323,7 @@ function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE PERSISTENT_DATA_D ${LY_ROOT_FOLDER} ) - # Bild dependency artifact dir + # Build dependency artifact dir ly_test_impact_rebase_file_to_repo_root( "${LY_TEST_IMPACT_TARGET_DEPENDENCY_DIR}" target_dependency_dir diff --git a/cmake/TestImpactFramework/SourceToTargetMapping.in b/cmake/TestImpactFramework/SourceToTargetMapping.in index c94c52a145..da837ed398 100644 --- a/cmake/TestImpactFramework/SourceToTargetMapping.in +++ b/cmake/TestImpactFramework/SourceToTargetMapping.in @@ -1,15 +1,18 @@ -#Lumberyard source to target mapping -[target] -name = "${target_name}" -output_name = "${target_output_name}" -path = "${target_path}" -[sources] -input = [ +{ + "sources": { + "input": [ ${autogen_input_files} -] -output = [ + ], + "output": [ ${autogen_output_files} -] -static = [ + ], + "static": [ ${static_sources} -] \ No newline at end of file + ] + }, + "target": { + "name": "${target_name}", + "output_name": "${target_output_name}", + "path": "${target_path}" + } +} \ No newline at end of file From b9dc7639b4182cbdace284ed4226c0c179ffec52 Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 28 Apr 2021 17:15:48 +0100 Subject: [PATCH 005/233] Add dynamic dependency map. --- .../Frontend/Console/Code/CMakeLists.txt | 2 +- .../Artifact/Dynamic/TestImpactCoverage.h | 9 +- .../TestImpactChangeDependencyList.cpp | 41 ++ .../TestImpactChangeDependencyList.h | 41 ++ .../TestImpactDependencyException.h | 26 ++ .../TestImpactDynamicDependencyMap.cpp | 414 ++++++++++++++++++ .../TestImpactDynamicDependencyMap.h | 120 +++++ .../TestImpactSourceCoveringTestsList.cpp | 58 +++ .../TestImpactSourceCoveringTestsList.h | 53 +++ .../Test/Run/TestImpactTestCoverage.cpp | 4 +- .../Source/Test/Run/TestImpactTestCoverage.h | 4 +- .../Tests/Test/TestImpactTestCoverageTest.cpp | 12 +- .../testimpactframework_runtime_files.cmake | 68 ++- ...timpactframework_runtime_tests_files.cmake | 24 +- 14 files changed, 853 insertions(+), 23 deletions(-) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactChangeDependencyList.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactChangeDependencyList.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDependencyException.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/Code/CMakeLists.txt index 7a043a30ca..da2c707cb8 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Code/CMakeLists.txt +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Code/CMakeLists.txt @@ -20,4 +20,4 @@ ly_add_target( BUILD_DEPENDENCIES PRIVATE AZ::TestImpact.Runtime.Static -) +) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactCoverage.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactCoverage.h index 3c5b5353a7..54b78d682e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactCoverage.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactCoverage.h @@ -12,9 +12,8 @@ #pragma once -#include +#include #include -#include namespace TestImpact { @@ -28,14 +27,14 @@ namespace TestImpact //! Coverage information about a particular source file. struct SourceCoverage { - AZ::IO::Path m_path; //!< Source file path. - AZStd::optional> m_coverage; //!< Source file line coverage (empty if source level coverage only). + AZStd::string m_path; //!< Source file path. + AZStd::vector m_coverage; //!< Source file line coverage (empty if source level coverage only). }; //! Coverage information about a particular module (executable, shared library). struct ModuleCoverage { - AZ::IO::Path m_path; //!< Module path. + AZStd::string m_path; //!< Module path. AZStd::vector m_sources; //!< Sources of this module that are covered. }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactChangeDependencyList.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactChangeDependencyList.cpp new file mode 100644 index 0000000000..53e71d3a30 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactChangeDependencyList.cpp @@ -0,0 +1,41 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + ChangeDependencyList::ChangeDependencyList( + AZStd::vector&& createSourceDependencies, + AZStd::vector&& updateSourceDependencies, + AZStd::vector&& deleteSourceDependencies) + : m_createSourceDependencies(AZStd::move(createSourceDependencies)) + , m_updateSourceDependencies(AZStd::move(updateSourceDependencies)) + , m_deleteSourceDependencies(AZStd::move(deleteSourceDependencies)) + { + } + + const AZStd::vector& ChangeDependencyList::GetCreateSourceDependencies() const + { + return m_createSourceDependencies; + } + + const AZStd::vector& ChangeDependencyList::GetUpdateSourceDependencies() const + { + return m_updateSourceDependencies; + } + + const AZStd::vector& ChangeDependencyList::GetDeleteSourceDependencies() const + { + return m_deleteSourceDependencies; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactChangeDependencyList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactChangeDependencyList.h new file mode 100644 index 0000000000..bf8b0f558d --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactChangeDependencyList.h @@ -0,0 +1,41 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Representation of a change list where all CRUD sources have been resolved to source dependencies from the dynamic dependency map. + class ChangeDependencyList + { + public: + ChangeDependencyList( + AZStd::vector&& createSourceDependencies, + AZStd::vector&& updateSourceDependencies, + AZStd::vector&& deleteSourceDependencies); + + //! Gets the sources dependencies of the created source files from the change list. + const AZStd::vector& GetCreateSourceDependencies() const; + + //! Gets the sources dependencies of the updated source files from the change list. + const AZStd::vector& GetUpdateSourceDependencies() const; + + //! Gets the sources dependencies of the deleted source files from the change list. + const AZStd::vector& GetDeleteSourceDependencies() const; + private: + AZStd::vector m_createSourceDependencies; + AZStd::vector m_updateSourceDependencies; + AZStd::vector m_deleteSourceDependencies; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDependencyException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDependencyException.h new file mode 100644 index 0000000000..e256fa3ccc --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDependencyException.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Exception for dependency related operations. + class DependencyException + : public Exception + { + public: + using Exception::Exception; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp new file mode 100644 index 0000000000..f3edbe2507 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp @@ -0,0 +1,414 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +namespace TestImpact +{ + DynamicDependencyMap::DynamicDependencyMap( + AZStd::vector&& productionTargetDescriptors, + AZStd::vector&& testTargetDescriptors) + : m_productionTargets(AZStd::move(productionTargetDescriptors)) + , m_testTargets(AZStd::move(testTargetDescriptors)) + { + const auto mapBuildTargetSources = [this](const auto* target) + { + for (const auto& source : target->GetSources().m_staticSources) + { + if (auto mapping = m_sourceDependencyMap.find(source); mapping != m_sourceDependencyMap.end()) + { + // This is an existing entry in the dependency map so update the parent build targets with this target + mapping->second.m_parentTargets.insert(target); + } + else + { + // This is a new entry on the dependency map so create an entry with this parent target and no covering targets + m_sourceDependencyMap.emplace(source, DependencyData{ {target}, {} }); + } + } + + // Populate the autogen input to output mapping with any autogen sources + for (const auto& autogen : target->GetSources().m_autogenSources) + { + for (const auto& output : autogen.m_outputs) + { + m_autogenInputToOutputMap[autogen.m_input].push_back(output); + } + } + }; + + for (const auto& target : m_productionTargets.GetTargets()) + { + mapBuildTargetSources(&target); + } + + for (const auto& target : m_testTargets.GetTargets()) + { + mapBuildTargetSources(&target); + } + } + + size_t DynamicDependencyMap::GetNumTargets() const + { + return m_productionTargets.GetNumTargets() + m_testTargets.GetNumTargets(); + } + + size_t DynamicDependencyMap::GetNumSources() const + { + return m_sourceDependencyMap.size(); + } + + const BuildTarget* DynamicDependencyMap::GetBuildTarget(const AZStd::string& name) const + { + const BuildTarget* buildTarget = nullptr; + AZStd::visit([&buildTarget](auto&& target) + { + if constexpr (IsProductionTarget || IsTestTarget) + { + buildTarget = target; + } + + }, GetTarget(name)); + + return buildTarget; + } + + const BuildTarget* DynamicDependencyMap::GetBuildTargetOrThrow(const AZStd::string& name) const + { + const BuildTarget* buildTarget = nullptr; + AZStd::visit([&buildTarget](auto&& target) + { + if constexpr (IsProductionTarget || IsTestTarget) + { + buildTarget = target; + } + }, GetTargetOrThrow(name)); + + return buildTarget; + } + + AZStd::variant DynamicDependencyMap::GetTarget(const AZStd::string& name) const + { + if (auto testTarget = m_testTargets.GetTarget(name); testTarget != nullptr) + { + return testTarget; + } + else if (auto productionTarget = m_productionTargets.GetTarget(name); productionTarget != nullptr) + { + return productionTarget; + } + + return AZStd::monostate{}; + } + + AZStd::variant DynamicDependencyMap::GetTargetOrThrow(const AZStd::string& name) const + { + AZStd::variant buildTarget; + AZStd::visit([&buildTarget, &name](auto&& target) + { + if constexpr (IsProductionTarget || IsTestTarget) + { + buildTarget = target; + } + else + { + throw(TargetException(AZStd::string::format("Couldn't find target %s", name.c_str()).c_str())); + } + }, GetTarget(name)); + + return buildTarget; + } + + void DynamicDependencyMap::ReplaceSourceCoverage(const SourceCoveringTestsList& sourceCoverageDelta) + { + for (const auto& sourceCoverage : sourceCoverageDelta.GetCoverage()) + { + // Autogen input files are not compiled sources and thus supplying coverage data for them makes no sense + AZ_TestImpact_Eval( + m_autogenInputToOutputMap.find(sourceCoverage.GetPath()) == m_autogenInputToOutputMap.end(), + DependencyException, AZStd::string::format("Couldn't replace source coverage for %s, source file is an autogen input file", + sourceCoverage.GetPath().c_str()).c_str()); + + auto [it, inserted] = m_sourceDependencyMap.insert(sourceCoverage.GetPath()); + auto& [key, sourceDependency] = *it; + + // Clear any existing coverage for the delta + sourceDependency.m_coveringTestTargets.clear(); + + // Update the dependency with any new coverage data + for (const auto& unresolvedTestTarget : sourceCoverage.GetCoveringTestTargets()) + { + const TestTarget* testTarget = m_testTargets.GetTarget(unresolvedTestTarget); + if (testTarget) + { + // Source to covering test target mapping + sourceDependency.m_coveringTestTargets.insert(testTarget); + + // Build target to covering test target mapping + for (const auto& parentTarget : sourceDependency.m_parentTargets) + { + m_buildTargetCoverage[parentTarget.GetBuildTarget()].insert(testTarget); + } + } + else + { + AZ_Warning("ReplaceSourceCoverage", false, AZStd::string::format("Test target %s exists in the coverage data " + "but has since been removed from the build system", unresolvedTestTarget.c_str()).c_str()); + } + } + + // If the new coverage data results in a parentless and coverageless entry, consider it a dead entry and remove accordingly + if (sourceDependency.m_coveringTestTargets.empty() && sourceDependency.m_parentTargets.empty()) + { + m_sourceDependencyMap.erase(it); + } + } + } + + void DynamicDependencyMap::ClearSourceCoverage(const AZStd::vector& paths) + { + for (const auto& path : paths) + { + if (const auto outputSources = m_autogenInputToOutputMap.find(path); outputSources != m_autogenInputToOutputMap.end()) + { + // Clearing the coverage data of an autogen input source instead clears the coverage data of its output sources + for (const auto& outputSource : outputSources->second) + { + ReplaceSourceCoverage(SourceCoveringTestsList({ SourceCoveringTests(outputSource, { }) })); + } + } + else + { + ReplaceSourceCoverage(SourceCoveringTestsList({ SourceCoveringTests(path, { }) })); + } + } + } + + const ProductionTargetList& DynamicDependencyMap::GetProductionTargetList() const + { + return m_productionTargets; + } + + const TestTargetList& DynamicDependencyMap::GetTestTargetList() const + { + return m_testTargets; + } + + AZStd::vector DynamicDependencyMap::GetCoveringTestTargetsForProductionTarget(const ProductionTarget& productionTarget) const + { + AZStd::vector coveringTestTargets; + if (const auto coverage = m_buildTargetCoverage.find(&productionTarget); coverage != m_buildTargetCoverage.end()) + { + coveringTestTargets.reserve(coverage->second.size()); + AZStd::copy(coverage->second.begin(), coverage->second.end(), AZStd::back_inserter(coveringTestTargets)); + } + + return coveringTestTargets; + } + + AZStd::optional DynamicDependencyMap::GetSourceDependency(const AZStd::string& path) const + { + AZStd::unordered_set parentTargets; + AZStd::unordered_set coveringTestTargets; + + const auto getSourceDependency = [&parentTargets, &coveringTestTargets, this](const AZStd::string& path) + { + const auto sourceDependency = m_sourceDependencyMap.find(path); + if (sourceDependency != m_sourceDependencyMap.end()) + { + for (const auto& parentTarget : sourceDependency->second.m_parentTargets) + { + parentTargets.insert(parentTarget); + } + + for (const auto& testTarget : sourceDependency->second.m_coveringTestTargets) + { + coveringTestTargets.insert(testTarget); + } + } + }; + + if (const auto outputSources = m_autogenInputToOutputMap.find(path); outputSources != m_autogenInputToOutputMap.end()) + { + // Consolidate the parentage and coverage of each of the autogen input file's generated output files + for (const auto& outputSource : outputSources->second) + { + getSourceDependency(outputSource); + } + } + else + { + getSourceDependency(path); + } + + if (!parentTargets.empty() || !coveringTestTargets.empty()) + { + return SourceDependency(path, DependencyData{ AZStd::move(parentTargets), AZStd::move(coveringTestTargets) }); + } + + return AZStd::nullopt; + } + + SourceDependency DynamicDependencyMap::GetSourceDependencyOrThrow(const AZStd::string& path) const + { + auto sourceDependency = GetSourceDependency(path); + AZ_TestImpact_Eval(sourceDependency.has_value(), DependencyException, AZStd::string::format("Couldn't find source %s", path.c_str()).c_str()); + return sourceDependency.value(); + } + + SourceCoveringTestsList DynamicDependencyMap::ExportSourceCoverage() const + { + AZStd::vector coverage; + for (const auto& [path, dependency] : m_sourceDependencyMap) + { + AZStd::vector souceCoveringTests; + for (const auto& testTarget : dependency.m_coveringTestTargets) + { + souceCoveringTests.push_back(testTarget->GetName()); + } + + coverage.push_back(SourceCoveringTests(path, AZStd::move(souceCoveringTests))); + } + + return coverage; + } + + AZStd::vector DynamicDependencyMap::GetOrphanSourceFiles() const + { + AZStd::vector orphans; + for (const auto& [source, dependency] : m_sourceDependencyMap) + { + if (dependency.m_parentTargets.empty()) + { + orphans.push_back(source); + } + } + + return orphans; + } + + ChangeDependencyList DynamicDependencyMap::ApplyAndResoveChangeList(const ChangeList& changeList) + { + AZStd::vector createDependencies; + AZStd::vector updateDependencies; + AZStd::vector deleteDependencies; + + // Keep track of the coverage to delete as a post step rather than deleting it in situ so that erroneous change lists + // do not corrupt the dynamic dependency map + AZStd::vector coverageToDelete; + + // Create operations + for (const auto& createdFile : changeList.m_createdFiles) + { + auto sourceDependency = GetSourceDependency(createdFile); + if (sourceDependency.has_value()) + { + if (sourceDependency->GetNumParentTargets()) + { + if (sourceDependency->GetNumCoveringTestTargets()) + { + const AZStd::string msg = AZStd::string::format("The newly-created file %s belongs to a build target yet " + "still has coverage data in the source covering test list implying that a delete CRUD operation has been " + "missed, thus the integrity of the source covering test list has been compromised", createdFile.c_str()); + AZ_Error("File Creation", false, msg.c_str()); + throw DependencyException(msg); + } + else + { + createDependencies.emplace_back(AZStd::move(*sourceDependency)); + } + } + else + { + if (sourceDependency->GetNumCoveringTestTargets()) + { + const AZStd::string msg = AZStd::string::format("The newly-created file %s does not belong to a build target " + "yet still has coverage data in the source covering test list, implying that a delete CRUD operation has been " + "missed, thus the integrity of the source covering test list has been compromised", createdFile.c_str()); + AZ_Error("File Creation", false, msg.c_str()); + throw DependencyException(msg); + } + } + } + } + + // Update operations + for (const auto& updatedFile : changeList.m_updatedFiles) + { + auto sourceDependency = GetSourceDependency(updatedFile); + if (sourceDependency.has_value()) + { + if (sourceDependency->GetNumParentTargets()) + { + updateDependencies.emplace_back(AZStd::move(*sourceDependency)); + } + else + { + if (sourceDependency->GetNumCoveringTestTargets()) + { + AZ_Warning( + "File Update", false, AZStd::string::format("Source file %s is potentially an orphan (used by build targets " + "without explicitly being added to the build system, e.g. an include directive pulling in a header from the " + "repository). Running the covering tests for this file with instrumentation will confirm whether or nor this " + "is the case", updatedFile.c_str()).c_str()); + + updateDependencies.emplace_back(AZStd::move(*sourceDependency)); + coverageToDelete.push_back(updatedFile); + } + } + } + } + + // Delete operations + for (const auto& deletedFile : changeList.m_deletedFiles) + { + auto sourceDependency = GetSourceDependency(deletedFile); + if (sourceDependency.has_value()) + { + if (sourceDependency->GetNumParentTargets()) + { + if (sourceDependency->GetNumCoveringTestTargets()) + { + const AZStd::string msg = AZStd::string::format("The deleted file %s still belongs to a build target and still " + "has coverage data in the source covering test list, implying that the integrity of both the source to target " + "mappings and the source covering test list has been compromised", deletedFile.c_str()); + AZ_Error("File Delete", false, msg.c_str()); + throw DependencyException(msg); + } + else + { + const AZStd::string msg = AZStd::string::format("The deleted file %s still belongs to a build target implying " + "that the integrity of the source to target mappings has been compromised", deletedFile.c_str()); + AZ_Error("File Delete", false, msg.c_str()); + throw DependencyException(msg); + } + } + else + { + if (sourceDependency->GetNumCoveringTestTargets()) + { + deleteDependencies.emplace_back(AZStd::move(*sourceDependency)); + coverageToDelete.push_back(deletedFile); + } + } + } + } + + if (!coverageToDelete.empty()) + { + ClearSourceCoverage(coverageToDelete); + } + + return ChangeDependencyList(AZStd::move(createDependencies), AZStd::move(updateDependencies), AZStd::move(deleteDependencies)); + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h new file mode 100644 index 0000000000..0057fdc264 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h @@ -0,0 +1,120 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace TestImpact +{ + //! Representation of the repository source tree and its relation to the build targets and coverage data. + class DynamicDependencyMap + { + public: + //! Constructs the dependency map with entries for each build target's source files with empty test coverage data. + DynamicDependencyMap( + AZStd::vector&& productionTargetDescriptors, + AZStd::vector&& testTargetDescriptors); + + //! Gets the total number of production and test targets in the repository. + size_t GetNumTargets() const; + + //! Gets the total number of unique source files in the repository. + //! @note This includes autogen output sources. + size_t GetNumSources() const; + + //! Attempts to get the specified build target. + //! @param name The name of the build target to get. + //! @returns If found, the pointer to the specified build target, otherwise nullptr. + const BuildTarget* GetBuildTarget(const AZStd::string& name) const; + + //! Attempts to get the specified build target or throw TargetException. + //! @param name The name of the build target to get. + const BuildTarget* GetBuildTargetOrThrow(const AZStd::string& name) const; + + //! Attempts to get the specified target's specialized type. + //! @param name The name of the target to get. + //! @returns If found, the pointer to the specialized target, otherwise AZStd::monostate. + AZStd::variant GetTarget(const AZStd::string& name) const; + + //! Attempts to get the specified target's specialized type or throw TargetException. + //! @param name The name of the target to get. + AZStd::variant GetTargetOrThrow(const AZStd::string& name) const; + + //! Get the list of production targets in the repository. + const ProductionTargetList& GetProductionTargetList() const; + + //! Get the list of test targets in the repository. + const TestTargetList& GetTestTargetList() const; + + //! Gets the test targets covering the specified production target. + //! @param productionTarget The production target to retrieve the covering tests for. + AZStd::vector GetCoveringTestTargetsForProductionTarget(const ProductionTarget& productionTarget) const; + + //! Gets the source dependency for the specified source file. + //! @note Autogen input source dependencies are the consolidated source dependencies of all of their generated output sources. + //! @returns If found, the source dependency information for the specified source file, otherwise empty. + AZStd::optional GetSourceDependency(const AZStd::string& path) const; + + //! Gets the source dependency for the specified source file or throw DependencyException. + SourceDependency GetSourceDependencyOrThrow(const AZStd::string& path) const; + + //! Replaces the source coverage of the specified sources with the specified source coverage. + //! @note The covering targets for the parent test target(s) will not be pruned if those covering targets are removed. + //! @param sourceCoverageDelta The source coverage delta to replace in the dependency map. + void ReplaceSourceCoverage(const SourceCoveringTestsList& sourceCoverageDelta); + + //! Exports the coverage of all sources in the dependency map. + SourceCoveringTestsList ExportSourceCoverage() const; + + //! Gets the list of orphaned source files in the dependency map that have coverage data but belong to no parent build targets. + AZStd::vector GetOrphanSourceFiles() const; + + //! Applies the specified change list to the dynamic dependency map and resolves the change list to a change dependency list + //! containing the updated source dependencies for each source file in the change list. + //! @param changeList The change list to apply and resolve. + //! @returns The change list as resolved to the appropriate source dependencies. + [[nodiscard]] ChangeDependencyList ApplyAndResoveChangeList(const ChangeList& changeList); + + private: + //! Clears the source coverage of the specified sources. + //! @note The covering targets for the parent test target(s) will not be pruned if those covering targets are removed. + void ClearSourceCoverage(const AZStd::vector& paths); + + //! The sorted list of unique production targets in the repository. + ProductionTargetList m_productionTargets; + + //! The sorted list of unique test targets in the repository. + TestTargetList m_testTargets; + + //! The dependency map of sources to their parent build targets and covering test targets. + AZStd::unordered_map m_sourceDependencyMap; + + //! The map of build targets and their covering test targets. + AZStd::unordered_map> m_buildTargetCoverage; + + //! Mapping of autogen input sources to their generated output sources. + AZStd::unordered_map> m_autogenInputToOutputMap; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp new file mode 100644 index 0000000000..57a11e9f69 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp @@ -0,0 +1,58 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include + +namespace TestImpact +{ + SourceCoveringTests::SourceCoveringTests(const AZStd::string& path, AZStd::vector&& coveringTestTargets) + : m_path(path) + , m_coveringTestTargets(AZStd::move(coveringTestTargets)) + { + } + + const AZStd::string& SourceCoveringTests::GetPath() const + { + return m_path; + } + + size_t SourceCoveringTests::GetNumCoveringTestTargets() const + { + return m_coveringTestTargets.size(); + } + + const AZStd::vector& SourceCoveringTests::GetCoveringTestTargets() const + { + return m_coveringTestTargets; + } + + SourceCoveringTestsList::SourceCoveringTestsList(AZStd::vector&& sourceCoveringTests) + : m_coverage(AZStd::move(sourceCoveringTests)) + { + AZStd::sort(m_coverage.begin(), m_coverage.end(), [](const SourceCoveringTests& lhs, const SourceCoveringTests& rhs) + { + return lhs.GetPath() < rhs.GetPath(); + }); + } + + size_t SourceCoveringTestsList::GetNumSources() const + { + return m_coverage.size(); + } + + const AZStd::vector& SourceCoveringTestsList::GetCoverage() const + { + return m_coverage; + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h new file mode 100644 index 0000000000..68b09d9e5a --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h @@ -0,0 +1,53 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Represents the unresolved test target coverage for a given source file. + class SourceCoveringTests + { + public: + SourceCoveringTests(const AZStd::string& path, AZStd::vector&& coveringTestTargets); + + //! Returns the path of this source file. + const AZStd::string& GetPath() const; + + //! Returns the number of unresolved test targets covering this source file. + size_t GetNumCoveringTestTargets() const; + + //! Returns the unresolved test targets covering this source file. + const AZStd::vector& GetCoveringTestTargets() const; + private: + AZStd::string m_path; //!< The path of this source file. + AZStd::vector m_coveringTestTargets; //!< The unresolved test targets that cover this source file. + }; + + //! Sorted collection of source file test coverage. + class SourceCoveringTestsList + { + public: + SourceCoveringTestsList(AZStd::vector&& sourceCoveringTests); + + //! Returns the number of source files in the collection. + size_t GetNumSources() const; + + //! Returns the source file coverages. + const AZStd::vector& GetCoverage() const; + private: + AZStd::vector m_coverage; //!< The collection of source file coverages. + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.cpp index 3a6f5d0b79..0d50dbbf9a 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.cpp @@ -25,7 +25,7 @@ namespace TestImpact for (const auto& sourceCovered : moduleCovered.m_sources) { m_sourcesCovered.emplace_back(sourceCovered.m_path); - if (sourceCovered.m_coverage.has_value()) + if (!sourceCovered.m_coverage.empty()) { m_coverageLevel = CoverageLevel::Line; } @@ -51,7 +51,7 @@ namespace TestImpact return m_modules.size(); } - const AZStd::vector& TestCoverage::GetSourcesCovered() const + const AZStd::vector& TestCoverage::GetSourcesCovered() const { return m_sourcesCovered; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.h index ab92b1bd80..7253a29df0 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.h @@ -38,7 +38,7 @@ namespace TestImpact size_t GetNumModulesCovered() const; //! Returns the sorted set of unique sources covered (empty if no coverage). - const AZStd::vector& GetSourcesCovered() const; + const AZStd::vector& GetSourcesCovered() const; //! Returns the modules covered (empty if no coverage). const AZStd::vector& GetModuleCoverages() const; @@ -48,7 +48,7 @@ namespace TestImpact private: AZStd::vector m_modules; - AZStd::vector m_sourcesCovered; + AZStd::vector m_sourcesCovered; AZStd::optional m_coverageLevel; }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestCoverageTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestCoverageTest.cpp index 538097d11e..c1d7ba3e4b 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestCoverageTest.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestCoverageTest.cpp @@ -19,12 +19,12 @@ namespace UnitTest { namespace { - AZ::IO::Path GenerateSourcePath(AZ::u32 index) + AZStd::string GenerateSourcePath(AZ::u32 index) { return AZStd::string::format("SourceFile%u", index); } - AZ::IO::Path GenerateModulePath(AZ::u32 index) + AZStd::string GenerateModulePath(AZ::u32 index) { return AZStd::string::format("Module%u", index); } @@ -47,7 +47,7 @@ namespace UnitTest sourceCoverage.m_path = GenerateSourcePath(index); if (coverageLevel == TestImpact::CoverageLevel::Line) { - sourceCoverage.m_coverage.emplace(GenerateLineCoverages(index + 1)); + sourceCoverage.m_coverage = GenerateLineCoverages(index + 1); } return sourceCoverage; @@ -152,9 +152,9 @@ namespace UnitTest if (m_coverageLevel == TestImpact::CoverageLevel::Line) { // Expect there to actually be line coverage data if this coverage was procedurally generated with line data - EXPECT_TRUE(sourceCoverage.m_coverage.has_value()); + EXPECT_FALSE(sourceCoverage.m_coverage.empty()); - const AZStd::vector& lineCoverages = sourceCoverage.m_coverage.value(); + const AZStd::vector& lineCoverages = sourceCoverage.m_coverage; // Expect the source's number of lines to match that of the corresponding procedurally generated source EXPECT_EQ(lineCoverages.size(), sourceIndex + 1); @@ -171,7 +171,7 @@ namespace UnitTest else { // Do not expect there to actually be line coverage data if this coverage was not procedurally generated with line data - EXPECT_FALSE(sourceCoverage.m_coverage.has_value()); + EXPECT_TRUE(sourceCoverage.m_coverage.empty()); } } } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake index fd65a16e39..f4128f7002 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake +++ b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake @@ -10,11 +10,37 @@ # set(FILES + Include/TestImpactFramework/TestImpactBitwise.h + Include/TestImpactFramework/TestImpactCallback.h Include/TestImpactFramework/TestImpactException.h Include/TestImpactFramework/TestImpactFrameworkPath.h - Include/TestImpactFramework/TestImpactCallback.h - Source/TestImpactException.cpp - Source/TestImpactFrameworkPath.cpp + Source/Artifact/TestImpactArtifactException.h + Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp + Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h + Source/Artifact/Factory/TestImpactChangeListFactory.cpp + Source/Artifact/Factory/TestImpactChangeListFactory.h + Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.cpp + Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.h + Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp + Source/Artifact/Factory/TestImpactTestRunSuiteFactory.h + Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp + Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.h + Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp + Source/Artifact/Factory/TestImpactModuleCoverageFactory.h + Source/Artifact/Static/TestImpactBuildTargetDescriptor.cpp + Source/Artifact/Static/TestImpactBuildTargetDescriptor.h + Source/Artifact/Static/TestImpactTargetDescriptorCompiler.cpp + Source/Artifact/Static/TestImpactTargetDescriptorCompiler.h + Source/Artifact/Static/TestImpactProductionTargetDescriptor.cpp + Source/Artifact/Static/TestImpactProductionTargetDescriptor.h + Source/Artifact/Static/TestImpactTestTargetMeta.h + Source/Artifact/Static/TestImpactTestTargetDescriptor.cpp + Source/Artifact/Static/TestImpactTestTargetDescriptor.h + Source/Artifact/Dynamic/TestImpactChangelist.h + Source/Artifact/Dynamic/TestImpactTestEnumerationSuite.h + Source/Artifact/Dynamic/TestImpactTestRunSuite.h + Source/Artifact/Dynamic/TestImpactTestSuite.h + Source/Artifact/Dynamic/TestImpactCoverage.h Source/Process/TestImpactProcess.cpp Source/Process/TestImpactProcess.h Source/Process/TestImpactProcessException.h @@ -26,4 +52,40 @@ set(FILES Source/Process/JobRunner/TestImpactProcessJobRunner.h Source/Process/Scheduler/TestImpactProcessScheduler.cpp Source/Process/Scheduler/TestImpactProcessScheduler.h + + Source/Target/TestImpactBuildTarget.cpp + Source/Target/TestImpactBuildTarget.h + Source/Target/TestImpactBuildTargetList.h + Source/Target/TestImpactProductionTarget.cpp + Source/Target/TestImpactProductionTarget.h + Source/Target/TestImpactProductionTargetList.h + Source/Target/TestImpactTargetException.h + Source/Target/TestImpactTestTarget.cpp + Source/Target/TestImpactTestTarget.h + Source/Target/TestImpactTestTargetList.h + Source/Test/Enumeration/TestImpactTestEnumeration.h + Source/Test/Enumeration/TestImpactTestEnumerationException.h + Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp + Source/Test/Enumeration/TestImpactTestEnumerationSerializer.h + Source/Test/Enumeration/TestImpactTestEnumerator.cpp + Source/Test/Enumeration/TestImpactTestEnumerator.h + Source/Test/Run/TestImpactTestRunSerializer.cpp + Source/Test/Run/TestImpactTestRunSerializer.h + Source/Test/Run/TestImpactTestRunner.cpp + Source/Test/Run/TestImpactTestRunner.h + Source/Test/Run/TestImpactInstrumentedTestRunner.cpp + Source/Test/Run/TestImpactInstrumentedTestRunner.h + Source/Test/Run/TestImpactTestRun.cpp + Source/Test/Run/TestImpactTestRun.h + Source/Test/Run/TestImpactTestRunJobData.cpp + Source/Test/Run/TestImpactTestRunJobData.h + Source/Test/Run/TestImpactTestCoverage.cpp + Source/Test/Run/TestImpactTestCoverage.h + Source/Test/Run/TestImpactTestRunException.h + Source/Test/Job/TestImpactTestJobRunner.h + Source/Test/Job/TestImpactTestJobException.h + Source/Test/Job/TestImpactTestJobCommon.h + Source/Test/TestImpactTestSuiteContainer.h + Source/TestImpactException.cpp + Source/TestImpactFrameworkPath.cpp ) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake index 61059dc543..13f788c37b 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake +++ b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake @@ -10,13 +10,29 @@ # set(FILES + Tests/Artifact/TestImpactTargetDescriptorCompilerTest.cpp + Tests/Artifact/TestImpactBuildTargetDescriptorFactoryTest.cpp + Tests/Artifact/TestImpactModuleCoverageFactoryTest.cpp + Tests/Artifact/TestImpactChangeListFactoryTest.cpp + Tests/Artifact/TestImpactTestEnumerationSuiteFactoryTest.cpp + Tests/Artifact/TestImpactTestRunSuiteFactoryTest.cpp + Tests/Artifact/TestImpactTestTargetMetaMapFactoryTest.cpp + Tests/Process/TestImpactProcessSchedulerTest.cpp + Tests/Process/TestImpactProcessTest.cpp + Tests/Target/TestImpactBuildTargetTest.cpp Tests/TestImpactExceptionTest.cpp Tests/TestImpactFrameworkPathTest.cpp - Tests/TestImpactProcessSchedulerTest.cpp - Tests/TestImpactProcessTest.cpp - Tests/TestImpactProcessTestShared.cpp - Tests/TestImpactProcessTestShared.h + Tests/Test/TestImpactTestEnumeratorTest.cpp + Tests/Test/TestImpactTestEumerationSerializerTest.cpp + Tests/Test/TestImpactTestRunSerializerTest.cpp + Tests/Test/TestImpactTestRunnerTest.cpp + Tests/Test/TestImpactInstrumentedTestRunnerTest.cpp + Tests/Test/TestImpactTestCoverageTest.cpp + Tests/TestImpactTestJobRunnerCommon.h Tests/TestImpactTestMain.cpp + Tests/TestImpactTestUtils.cpp + Tests/TestImpactTestUtils.h + ) From 692dd21b892dfc647053734230e26bda3bbfa9b4 Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 28 Apr 2021 17:26:28 +0100 Subject: [PATCH 006/233] Add missing SourceDependency. --- .../Dependency/TestImpactSourceDependency.cpp | 82 ++++++++++++++++ .../Dependency/TestImpactSourceDependency.h | 96 +++++++++++++++++++ .../testimpactframework_runtime_files.cmake | 10 +- 3 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp new file mode 100644 index 0000000000..49d82e341a --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp @@ -0,0 +1,82 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace TestImpact +{ + ParentTarget::ParentTarget(const TestTarget* target) + : m_buildTarget(target) + , m_target(target) + { + } + + ParentTarget::ParentTarget(const ProductionTarget* target) + : m_buildTarget(target) + , m_target(target) + { + } + + bool ParentTarget::operator==(const ParentTarget& other) const + { + return m_buildTarget == other.m_buildTarget; + } + + const BuildTarget* ParentTarget::GetBuildTarget() const + { + return m_buildTarget; + } + + const AZStd::variant& ParentTarget::GetTarget() const + { + return m_target; + } + + SourceDependency::SourceDependency( + const AZStd::string& path, + DependencyData&& dependencyData) + : m_path(path) + , m_dependencyData(AZStd::move(dependencyData)) + { + } + + const AZStd::string& SourceDependency::GetPath() const + { + return m_path; + } + + size_t SourceDependency::GetNumParentTargets() const + { + return m_dependencyData.m_parentTargets.size(); + } + + size_t SourceDependency::GetNumCoveringTestTargets() const + { + return m_dependencyData.m_coveringTestTargets.size(); + } + + const AZStd::unordered_set& SourceDependency::GetParentTargets() const + { + return m_dependencyData.m_parentTargets; + } + + const AZStd::unordered_set& SourceDependency::GetCoveringTestTargets() const + { + return m_dependencyData.m_coveringTestTargets; + } + +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h new file mode 100644 index 0000000000..e6cd89df9d --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h @@ -0,0 +1,96 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace TestImpact +{ + class BuildTarget; + class ProductionTarget; + class TestTarget; + + //! Representation of a source dependency's parent target. + class ParentTarget + { + public: + //! Constructor overload for test target types. + ParentTarget(const TestTarget* target); + + //! Constructor overload for production target types. + ParentTarget(const ProductionTarget* target); + + //! Returns the base build target pointer for this parent. + const BuildTarget* GetBuildTarget() const; + + //! Returns the specialized target pointer for this parent. + const AZStd::variant& GetTarget() const; + + bool operator==(const ParentTarget& other) const; + private: + const BuildTarget* m_buildTarget; //! The base built target pointer for this parent. + AZStd::variant m_target; //! The specialized target pointer for this parent. + }; +} + +namespace AZStd +{ + //! Hash function for ParentTarget types for use in maps and sets + template<> struct hash + { + size_t operator()(const TestImpact::ParentTarget& parentTarget) const noexcept + { + return reinterpret_cast(parentTarget.GetBuildTarget()); + } + }; +} + +namespace TestImpact +{ + struct DependencyData + { + AZStd::unordered_set m_parentTargets; + AZStd::unordered_set m_coveringTestTargets; + }; + + //! Test target coverage and build target dependency information for a given source file in the dynamic dependency map. + class SourceDependency + { + public: + SourceDependency( + const AZStd::string& path, + DependencyData&& dependencyData); + + //! Returns the path of this source file. + const AZStd::string& GetPath() const; + + //! Returns the number of parent build targets this source belongs to. + size_t GetNumParentTargets() const; + + //! Returns the number of test targets covering this source file. + size_t GetNumCoveringTestTargets() const; + + //! Returns the parent targets that this source file belongs to. + const AZStd::unordered_set& GetParentTargets() const; + + //! Returns the test targets covering this source file. + const AZStd::unordered_set& GetCoveringTestTargets() const; + private: + AZStd::string m_path; //!< The path of this source file. + DependencyData m_dependencyData; //!< + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake index f4128f7002..19f3ec23f7 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake +++ b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake @@ -52,7 +52,15 @@ set(FILES Source/Process/JobRunner/TestImpactProcessJobRunner.h Source/Process/Scheduler/TestImpactProcessScheduler.cpp Source/Process/Scheduler/TestImpactProcessScheduler.h - + Source/Dependency/TestImpactDynamicDependencyMap.cpp + Source/Dependency/TestImpactDynamicDependencyMap.h + Source/Dependency/TestImpactChangeDependencyList.cpp + Source/Dependency/TestImpactChangeDependencyList.h + Source/Dependency/TestImpactDependencyException.h + Source/Dependency/TestImpactSourceDependency.h + Source/Dependency/TestImpactSourceDependency.cpp + Source/Dependency/TestImpactSourceCoveringTestsList.h + Source/Dependency/TestImpactSourceCoveringTestsList.cpp Source/Target/TestImpactBuildTarget.cpp Source/Target/TestImpactBuildTarget.h Source/Target/TestImpactBuildTargetList.h From 2b21b70635ef0d78b3af55adcf3c3018d491b840 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 29 Apr 2021 08:28:42 +0100 Subject: [PATCH 007/233] Add test selector and prioritizor. --- .../TestImpactModuleCoverageFactory.cpp | 13 +- .../Static/TestImpactDependencyGraphData.h | 26 ++ .../TestImpactTestSelectorAndPrioritizer.cpp | 224 ++++++++++++++++++ .../TestImpactTestSelectorAndPrioritizer.h | 77 ++++++ .../testimpactframework_runtime_files.cmake | 3 + 5 files changed, 333 insertions(+), 10 deletions(-) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactDependencyGraphData.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.h diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp index 54ba65c762..889f91c600 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp @@ -97,7 +97,7 @@ namespace TestImpact { // Module ModuleCoverage moduleCoverage; - moduleCoverage.m_path = AZ::IO::Path(package_node->first_attribute(Keys[NameKey])->value()); + moduleCoverage.m_path = package_node->first_attribute(Keys[NameKey])->value(); const auto classes_node = package_node->first_node(Keys[ClassesKey]); if (classes_node) @@ -107,13 +107,11 @@ namespace TestImpact { // Source SourceCoverage sourceCoverage; - sourceCoverage.m_path = AZ::IO::Path(pathRoot + class_node->first_attribute(Keys[FileNameKey])->value()); + sourceCoverage.m_path = pathRoot + class_node->first_attribute(Keys[FileNameKey])->value(); const auto lines_node = class_node->first_node(Keys[LinesKey]); if (lines_node) { - AZStd::vector lineCoverage; - // Lines for (auto line_node = lines_node->first_node(); line_node; line_node = line_node->next_sibling()) { @@ -121,12 +119,7 @@ namespace TestImpact const size_t number = AZStd::stol(AZStd::string(line_node->first_attribute(Keys[NumberKey])->value())); const size_t hits = AZStd::stol(AZStd::string(line_node->first_attribute(Keys[HitsKey])->value())); - lineCoverage.emplace_back(LineCoverage{number, hits}); - } - - if (!lineCoverage.empty()) - { - sourceCoverage.m_coverage.emplace(AZStd::move(lineCoverage)); + sourceCoverage.m_coverage.emplace_back(LineCoverage{number, hits}); } } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactDependencyGraphData.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactDependencyGraphData.h new file mode 100644 index 0000000000..ee8ca722b4 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactDependencyGraphData.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + struct DependencyGraphData + { + AZStd::string m_root; + AZStd::vector m_vertexes; + AZStd::vector> m_edges; + }; +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp new file mode 100644 index 0000000000..f8213738e5 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp @@ -0,0 +1,224 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include + +namespace TestImpact +{ + TestSelectorAndPrioritizer::TestSelectorAndPrioritizer(const DynamicDependencyMap* dynamicDependencyMap, DependencyGraphDataMap&& dependencyGraphDataMap) + : m_dynamicDependencyMap(dynamicDependencyMap) + , m_dependencyGraphDataMap(AZStd::move(dependencyGraphDataMap)) + { + } + + AZStd::vector TestSelectorAndPrioritizer::SelectTestTargets(const ChangeDependencyList& changeDependencyList, TestSelectionStrategy testSelectionStrategy) + { + const auto selectedTestTargetAndDependerMap = SelectTestTargets(changeDependencyList); + const auto prioritizedSelectedTests = PrioritizeSelectedTestTargets(selectedTestTargetAndDependerMap, testSelectionStrategy); + return prioritizedSelectedTests; + } + + TestSelectorAndPrioritizer::SelectedTestTargetAndDependerMap TestSelectorAndPrioritizer::SelectTestTargets( + const ChangeDependencyList& changeDependencyList) + { + SelectedTestTargetAndDependerMap selectedTestTargetMap; + + // Create operations + for (const auto& sourceDependency : changeDependencyList.GetCreateSourceDependencies()) + { + for (const auto& parentTarget : sourceDependency.GetParentTargets()) + { + AZStd::visit([&selectedTestTargetMap, this](auto&& target) + { + if constexpr (IsProductionTarget) + { + // Parent Targets: Yes + // Coverage Data : No + // Source Type : Production + // + // Scenario + // 1. The file has been newly created + // 2. This file exists in one or more source to production target mapping artifacts + // 3. There exists no coverage data for this file in the source covering test list + // + // Action + // 1. Select all test targets covering the parent production targets + const auto coverage = m_dynamicDependencyMap->GetCoveringTestTargetsForProductionTarget(*target); + for (const auto* testTarget : coverage) + { + selectedTestTargetMap[testTarget].insert(target); + } + } + else + { + // Parent Targets: Yes + // Coverage Data : No + // Source Type : Test + // + // Scenario + // 1. The file has been newly created + // 2. This file exists in one or more source to test target mapping artifacts + // 3. There exists no coverage data for this file in the source covering test list + // + // Action + // 1. Select all parent test targets + selectedTestTargetMap.insert(target); + } + }, parentTarget.GetTarget()); + } + } + + // Update operations + for (const auto& sourceDependency : changeDependencyList.GetUpdateSourceDependencies()) + { + if (sourceDependency.GetNumParentTargets()) + { + if (sourceDependency.GetNumCoveringTestTargets()) + { + for (const auto& parentTarget : sourceDependency.GetParentTargets()) + { + AZStd::visit([&selectedTestTargetMap, &sourceDependency, this](auto&& target) + { + if constexpr (IsProductionTarget) + { + // Parent Targets: Yes + // Coverage Data : Yes + // Source Type : Production + // + // Scenario + // 1. The existing file has been modified + // 2. This file exists in one or more source to production target mapping artifacts + // 3. There exists coverage data for this file in the source covering test list + // + // Action + // 1. Select all test targets covering this file + for (const auto* testTarget : sourceDependency.GetCoveringTestTargets()) + { + selectedTestTargetMap[testTarget].insert(target); + } + } + else + { + // Parent Targets: Yes + // Coverage Data : Yes + // Source Type : Test + // + // Scenario + // 1. The existing file has been modified + // 2. This file exists in one or more source to test target mapping artifacts + // 3. There exists coverage data for this file in the source covering test list + // + // Action + // 1. Select the parent test targets for this file + selectedTestTargetMap.insert(target); + } + }, parentTarget.GetTarget()); + } + } + else + { + for (const auto& parentTarget : sourceDependency.GetParentTargets()) + { + AZStd::visit([&selectedTestTargetMap, &sourceDependency, this](auto&& target) + { + if constexpr (IsTestTarget) + { + // Parent Targets: Yes + // Coverage Data : No + // Source Type : Test + // + // Scenario + // 1. The existing file has been modified + // 2. This file exists in one or more source to test target mapping artifacts + // 3. There exists no coverage data for this file in the source covering test list + // + // Action + // 1. Select the parent test targets for this file + selectedTestTargetMap.insert(target); + } + }, parentTarget.GetTarget()); + } + } + } + else + { + // Parent Targets: No + // Coverage Data : Yes + // Source Type : Indeterminate + // + // Scenario + // 1. The existing file has been modified + // 2. Either: + // a) This file previously existed in one or more source to target mapping artifacts + // b) This file no longer exists in any source to target mapping artifacts + // c) The coverage data for this file was has yet to be deleted from the source covering test list + // 3. Or: + // a) The file is being used by build targets but has erroneously not been explicitly added to the build + // system (e.g. include directive pulling in a header from the repository that has not been added to + // any build targets due to an oversight) + // + // Action + // 1. Log potential orphaned source file warning + // 2. Select all test targets covering this file + // 3. Delete the existing coverage data from the source covering test list + + for (const auto* testTarget : sourceDependency.GetCoveringTestTargets()) + { + selectedTestTargetMap.insert(testTarget); + } + } + } + + // Delete operations + for (const auto& sourceDependency : changeDependencyList.GetDeleteSourceDependencies()) + { + // Parent Targets: No + // Coverage Data : Yes + // Source Type : Indeterminate + // + // Scenario + // 1. The existing file has been deleted + // 2. This file previously existed in one or more source to target mapping artifacts + // 2. This file does not exist in any source to target mapping artifacts + // 4. The coverage data for this file was has yet to be deleted from the source covering test list + // + // Action + // 1. Select all test targets covering this file + // 2. Delete the existing coverage data from the source covering test list + for (const auto* testTarget : sourceDependency.GetCoveringTestTargets()) + { + selectedTestTargetMap.insert(testTarget); + } + } + + return selectedTestTargetMap; + } + + AZStd::vector TestSelectorAndPrioritizer::PrioritizeSelectedTestTargets( + const SelectedTestTargetAndDependerMap& selectedTestTargetAndDependerMap, + [[maybe_unused]]TestSelectionStrategy testSelectionStrategy) + { + AZStd::vector selectedTestTargets; + + // Prioritization disabled for now + // https://jira.agscollab.com/browse/SPEC-6563 + for (const auto& [testTarget, dependerTargets] : selectedTestTargetAndDependerMap) + { + selectedTestTargets.push_back(testTarget); + } + + return selectedTestTargets; + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.h new file mode 100644 index 0000000000..261e469ee2 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.h @@ -0,0 +1,77 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace TestImpact +{ + class DynamicDependencyMap; + class BuildTarget; + class TestTarget; + + //! Strategy for selecting tests given a set of source changes. + enum class TestSelectionStrategy : bool + { + SelectOnly, //!< Select tests only, do not attempt prioritization of those selected tests. + SelectAndPriotitize //!< Select tests and prioritize according to dependency graph locality of coverer and coveree. + }; + + //! Map of build targets and their dependency graph data. + //! For test targets, the dependency graph data is that of the build targets which the test target depends on. + //! For production targets, the dependency graph is that of the build targets that depend on it (dependers). + //! @note No dependency graph data is not an error, it simple means that the target cannot be prioritized. + using DependencyGraphDataMap = AZStd::unordered_map; + + //! Selects the test targets that cover a given set of changes based on the CRUD rules and optionally prioritizes the test + //! selection according to their locality of their covering production targets in the their dependency graphs. + //! @note the CRUD rules for how tests are selected can be found in the MicroRepo header file. + class TestSelectorAndPrioritizer + { + public: + //! Constructs the test selector and prioritizer for the given dynamic dependency map. + //! @param dynamicDependencyMap The dynamic dependency map representing the repository source tree. + //! @param dependencyGraphDataMap The map of build targets and their dependency graph data for use in test prioritization. + TestSelectorAndPrioritizer(const DynamicDependencyMap* dynamicDependencyMap, DependencyGraphDataMap&& dependencyGraphDataMap); + + //! Select the covering test targets for the given set of source changes and optionally prioritizes said test selection. + //! @param changeDependencyList The resolved list of source dependencies for the CRUD source changes. + //! @param testSelectionStrategy The test selection and prioritization strategy to apply to the given CRUD source changes. + AZStd::vector SelectTestTargets(const ChangeDependencyList& changeDependencyList, TestSelectionStrategy testSelectionStrategy); + + private: + //! Map of selected test targets and the production targets they cover for the given set of source changes. + using SelectedTestTargetAndDependerMap = AZStd::unordered_map>; + + //! Selects the test targets covering the set of source changes in the change dependency list. + //! @param changeDependencyList The change dependency list containing the CRUD source changes to select tests for. + //! @returns The selected tests and their covering production targets for the given set of source changes. + SelectedTestTargetAndDependerMap SelectTestTargets(const ChangeDependencyList& changeDependencyList); + + //! Prioritizes the selected tests according to the specified test selection strategy, + //! @note If no dependency graph data exists for a given test target then that test target still be selected albeit not prioritized. + //! @param selectedTestTargetAndDependerMap The selected tests to prioritize. + //! @param testSelectionStrategy The test selection strategy to prioritize the selected tests. + //! @returns The selected tests either in either arbitrary order or in prioritized with highest priority first. + AZStd::vector PrioritizeSelectedTestTargets( + const SelectedTestTargetAndDependerMap& selectedTestTargetAndDependerMap, TestSelectionStrategy testSelectionStrategy); + + const DynamicDependencyMap* m_dynamicDependencyMap; + DependencyGraphDataMap m_dependencyGraphDataMap; + }; +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake index 19f3ec23f7..da3693433e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake +++ b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake @@ -36,6 +36,7 @@ set(FILES Source/Artifact/Static/TestImpactTestTargetMeta.h Source/Artifact/Static/TestImpactTestTargetDescriptor.cpp Source/Artifact/Static/TestImpactTestTargetDescriptor.h + Source/Artifact/Static/TestImpactDependencyGraphData.h Source/Artifact/Dynamic/TestImpactChangelist.h Source/Artifact/Dynamic/TestImpactTestEnumerationSuite.h Source/Artifact/Dynamic/TestImpactTestRunSuite.h @@ -59,6 +60,8 @@ set(FILES Source/Dependency/TestImpactDependencyException.h Source/Dependency/TestImpactSourceDependency.h Source/Dependency/TestImpactSourceDependency.cpp + Source/Dependency/TestImpactTestSelectorAndPrioritizer.h + Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp Source/Dependency/TestImpactSourceCoveringTestsList.h Source/Dependency/TestImpactSourceCoveringTestsList.cpp Source/Target/TestImpactBuildTarget.cpp From eb5dd7ee47ee3ce0d6c4ab3fdc86fd923fff503e Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 29 Apr 2021 14:51:51 +0100 Subject: [PATCH 008/233] Address PR comments. --- .../Source/Artifact/Static/TestImpactDependencyGraphData.h | 2 +- .../Dependency/TestImpactTestSelectorAndPrioritizer.cpp | 4 ++-- .../Source/Dependency/TestImpactTestSelectorAndPrioritizer.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactDependencyGraphData.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactDependencyGraphData.h index ee8ca722b4..8dae41c579 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactDependencyGraphData.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactDependencyGraphData.h @@ -23,4 +23,4 @@ namespace TestImpact AZStd::vector m_vertexes; AZStd::vector> m_edges; }; -} +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp index f8213738e5..2962e91381 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp @@ -213,7 +213,7 @@ namespace TestImpact AZStd::vector selectedTestTargets; // Prioritization disabled for now - // https://jira.agscollab.com/browse/SPEC-6563 + // SPEC-6563 for (const auto& [testTarget, dependerTargets] : selectedTestTargetAndDependerMap) { selectedTestTargets.push_back(testTarget); @@ -221,4 +221,4 @@ namespace TestImpact return selectedTestTargets; } -} +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.h index 261e469ee2..c742176073 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.h @@ -74,4 +74,4 @@ namespace TestImpact const DynamicDependencyMap* m_dynamicDependencyMap; DependencyGraphDataMap m_dependencyGraphDataMap; }; -} +} // namespace TestImpact From 20243549e6a36bd59e5b12f829b691cf88eca8ce Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 30 Apr 2021 12:51:26 +0100 Subject: [PATCH 009/233] Address PR comments. --- .../Source/Artifact/Static/TestImpactDependencyGraphData.h | 7 ++++--- .../Dependency/TestImpactTestSelectorAndPrioritizer.cpp | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactDependencyGraphData.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactDependencyGraphData.h index 8dae41c579..3c9f455254 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactDependencyGraphData.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactDependencyGraphData.h @@ -17,10 +17,11 @@ namespace TestImpact { + //! Raw representation of the dependency graph for a given build target. struct DependencyGraphData { - AZStd::string m_root; - AZStd::vector m_vertexes; - AZStd::vector> m_edges; + AZStd::string m_root; //!< The build target this dependency graph is for. + AZStd::vector m_vertices; //!< The depender/depending built targets in this graph. + AZStd::vector> m_edges; //!< The dependency connectivity of the build targets in this graph. }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp index 2962e91381..549ef0c108 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp @@ -17,13 +17,15 @@ namespace TestImpact { - TestSelectorAndPrioritizer::TestSelectorAndPrioritizer(const DynamicDependencyMap* dynamicDependencyMap, DependencyGraphDataMap&& dependencyGraphDataMap) + TestSelectorAndPrioritizer::TestSelectorAndPrioritizer( + const DynamicDependencyMap* dynamicDependencyMap, DependencyGraphDataMap&& dependencyGraphDataMap) : m_dynamicDependencyMap(dynamicDependencyMap) , m_dependencyGraphDataMap(AZStd::move(dependencyGraphDataMap)) { } - AZStd::vector TestSelectorAndPrioritizer::SelectTestTargets(const ChangeDependencyList& changeDependencyList, TestSelectionStrategy testSelectionStrategy) + AZStd::vector TestSelectorAndPrioritizer::SelectTestTargets( + const ChangeDependencyList& changeDependencyList, TestSelectionStrategy testSelectionStrategy) { const auto selectedTestTargetAndDependerMap = SelectTestTargets(changeDependencyList); const auto prioritizedSelectedTests = PrioritizeSelectedTestTargets(selectedTestTargetAndDependerMap, testSelectionStrategy); From 39ba583ad7ae1761e13d356bd50b02ae29e3f6b4 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 30 Apr 2021 12:58:48 +0100 Subject: [PATCH 010/233] Address PR comments. --- .../TestImpactDynamicDependencyMap.cpp | 108 +++++++++--------- .../TestImpactDynamicDependencyMap.h | 5 +- .../TestImpactSourceCoveringTestsList.cpp | 7 +- .../TestImpactSourceCoveringTestsList.h | 3 +- .../Dependency/TestImpactSourceDependency.cpp | 19 +-- .../Dependency/TestImpactSourceDependency.h | 11 +- .../Source/Target/TestImpactBuildTarget.h | 10 ++ 7 files changed, 88 insertions(+), 75 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp index f3edbe2507..f99300ad4e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp @@ -25,7 +25,8 @@ namespace TestImpact { for (const auto& source : target->GetSources().m_staticSources) { - if (auto mapping = m_sourceDependencyMap.find(source); mapping != m_sourceDependencyMap.end()) + if (auto mapping = m_sourceDependencyMap.find(source); + mapping != m_sourceDependencyMap.end()) { // This is an existing entry in the dependency map so update the parent build targets with this target mapping->second.m_parentTargets.insert(target); @@ -97,13 +98,15 @@ namespace TestImpact return buildTarget; } - AZStd::variant DynamicDependencyMap::GetTarget(const AZStd::string& name) const + OptionalTarget DynamicDependencyMap::GetTarget(const AZStd::string& name) const { - if (auto testTarget = m_testTargets.GetTarget(name); testTarget != nullptr) + if (const auto testTarget = m_testTargets.GetTarget(name); + testTarget != nullptr) { return testTarget; } - else if (auto productionTarget = m_productionTargets.GetTarget(name); productionTarget != nullptr) + else if (auto productionTarget = m_productionTargets.GetTarget(name); + productionTarget != nullptr) { return productionTarget; } @@ -111,9 +114,9 @@ namespace TestImpact return AZStd::monostate{}; } - AZStd::variant DynamicDependencyMap::GetTargetOrThrow(const AZStd::string& name) const + Target DynamicDependencyMap::GetTargetOrThrow(const AZStd::string& name) const { - AZStd::variant buildTarget; + Target buildTarget; AZStd::visit([&buildTarget, &name](auto&& target) { if constexpr (IsProductionTarget || IsTestTarget) @@ -148,8 +151,8 @@ namespace TestImpact // Update the dependency with any new coverage data for (const auto& unresolvedTestTarget : sourceCoverage.GetCoveringTestTargets()) { - const TestTarget* testTarget = m_testTargets.GetTarget(unresolvedTestTarget); - if (testTarget) + if (const TestTarget* testTarget = m_testTargets.GetTarget(unresolvedTestTarget); + testTarget) { // Source to covering test target mapping sourceDependency.m_coveringTestTargets.insert(testTarget); @@ -179,12 +182,13 @@ namespace TestImpact { for (const auto& path : paths) { - if (const auto outputSources = m_autogenInputToOutputMap.find(path); outputSources != m_autogenInputToOutputMap.end()) + if (const auto outputSources = m_autogenInputToOutputMap.find(path); + outputSources != m_autogenInputToOutputMap.end()) { // Clearing the coverage data of an autogen input source instead clears the coverage data of its output sources for (const auto& outputSource : outputSources->second) { - ReplaceSourceCoverage(SourceCoveringTestsList({ SourceCoveringTests(outputSource, { }) })); + ReplaceSourceCoverage(SourceCoveringTestsList({ SourceCoveringTests(outputSource) })); } } else @@ -207,7 +211,8 @@ namespace TestImpact AZStd::vector DynamicDependencyMap::GetCoveringTestTargetsForProductionTarget(const ProductionTarget& productionTarget) const { AZStd::vector coveringTestTargets; - if (const auto coverage = m_buildTargetCoverage.find(&productionTarget); coverage != m_buildTargetCoverage.end()) + if (const auto coverage = m_buildTargetCoverage.find(&productionTarget); + coverage != m_buildTargetCoverage.end()) { coveringTestTargets.reserve(coverage->second.size()); AZStd::copy(coverage->second.begin(), coverage->second.end(), AZStd::back_inserter(coveringTestTargets)); @@ -280,7 +285,7 @@ namespace TestImpact coverage.push_back(SourceCoveringTests(path, AZStd::move(souceCoveringTests))); } - return coverage; + return SourceCoveringTestsList(AZStd::move(coverage)); } AZStd::vector DynamicDependencyMap::GetOrphanSourceFiles() const @@ -313,31 +318,18 @@ namespace TestImpact auto sourceDependency = GetSourceDependency(createdFile); if (sourceDependency.has_value()) { - if (sourceDependency->GetNumParentTargets()) + if (sourceDependency->GetNumCoveringTestTargets()) { - if (sourceDependency->GetNumCoveringTestTargets()) - { - const AZStd::string msg = AZStd::string::format("The newly-created file %s belongs to a build target yet " - "still has coverage data in the source covering test list implying that a delete CRUD operation has been " - "missed, thus the integrity of the source covering test list has been compromised", createdFile.c_str()); - AZ_Error("File Creation", false, msg.c_str()); - throw DependencyException(msg); - } - else - { - createDependencies.emplace_back(AZStd::move(*sourceDependency)); - } + const AZStd::string msg = AZStd::string::format("The newly-created file %s belongs to a build target yet " + "still has coverage data in the source covering test list implying that a delete CRUD operation has been " + "missed, thus the integrity of the source covering test list has been compromised", createdFile.c_str()); + AZ_Error("File Creation", false, msg.c_str()); + throw DependencyException(msg); } - else + + if (sourceDependency->GetNumParentTargets()) { - if (sourceDependency->GetNumCoveringTestTargets()) - { - const AZStd::string msg = AZStd::string::format("The newly-created file %s does not belong to a build target " - "yet still has coverage data in the source covering test list, implying that a delete CRUD operation has been " - "missed, thus the integrity of the source covering test list has been compromised", createdFile.c_str()); - AZ_Error("File Creation", false, msg.c_str()); - throw DependencyException(msg); - } + createDependencies.emplace_back(AZStd::move(*sourceDependency)); } } } @@ -373,33 +365,35 @@ namespace TestImpact for (const auto& deletedFile : changeList.m_deletedFiles) { auto sourceDependency = GetSourceDependency(deletedFile); - if (sourceDependency.has_value()) + if (!sourceDependency.has_value()) { - if (sourceDependency->GetNumParentTargets()) + continue; + } + + if (sourceDependency->GetNumParentTargets()) + { + if (sourceDependency->GetNumCoveringTestTargets()) { - if (sourceDependency->GetNumCoveringTestTargets()) - { - const AZStd::string msg = AZStd::string::format("The deleted file %s still belongs to a build target and still " - "has coverage data in the source covering test list, implying that the integrity of both the source to target " - "mappings and the source covering test list has been compromised", deletedFile.c_str()); - AZ_Error("File Delete", false, msg.c_str()); - throw DependencyException(msg); - } - else - { - const AZStd::string msg = AZStd::string::format("The deleted file %s still belongs to a build target implying " - "that the integrity of the source to target mappings has been compromised", deletedFile.c_str()); - AZ_Error("File Delete", false, msg.c_str()); - throw DependencyException(msg); - } + const AZStd::string msg = AZStd::string::format("The deleted file %s still belongs to a build target and still " + "has coverage data in the source covering test list, implying that the integrity of both the source to target " + "mappings and the source covering test list has been compromised", deletedFile.c_str()); + AZ_Error("File Delete", false, msg.c_str()); + throw DependencyException(msg); } else { - if (sourceDependency->GetNumCoveringTestTargets()) - { - deleteDependencies.emplace_back(AZStd::move(*sourceDependency)); - coverageToDelete.push_back(deletedFile); - } + const AZStd::string msg = AZStd::string::format("The deleted file %s still belongs to a build target implying " + "that the integrity of the source to target mappings has been compromised", deletedFile.c_str()); + AZ_Error("File Delete", false, msg.c_str()); + throw DependencyException(msg); + } + } + else + { + if (sourceDependency->GetNumCoveringTestTargets()) + { + deleteDependencies.emplace_back(AZStd::move(*sourceDependency)); + coverageToDelete.push_back(deletedFile); } } } @@ -411,4 +405,4 @@ namespace TestImpact return ChangeDependencyList(AZStd::move(createDependencies), AZStd::move(updateDependencies), AZStd::move(deleteDependencies)); } -} +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h index 0057fdc264..527a6555c6 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h @@ -24,7 +24,6 @@ #include #include #include -#include namespace TestImpact { @@ -56,11 +55,11 @@ namespace TestImpact //! Attempts to get the specified target's specialized type. //! @param name The name of the target to get. //! @returns If found, the pointer to the specialized target, otherwise AZStd::monostate. - AZStd::variant GetTarget(const AZStd::string& name) const; + OptionalTarget GetTarget(const AZStd::string& name) const; //! Attempts to get the specified target's specialized type or throw TargetException. //! @param name The name of the target to get. - AZStd::variant GetTargetOrThrow(const AZStd::string& name) const; + Target GetTargetOrThrow(const AZStd::string& name) const; //! Get the list of production targets in the repository. const ProductionTargetList& GetProductionTargetList() const; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp index 57a11e9f69..792eb1f0b2 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp @@ -16,6 +16,11 @@ namespace TestImpact { + SourceCoveringTests::SourceCoveringTests(const AZStd::string& path) + : m_path(path) + { + } + SourceCoveringTests::SourceCoveringTests(const AZStd::string& path, AZStd::vector&& coveringTestTargets) : m_path(path) , m_coveringTestTargets(AZStd::move(coveringTestTargets)) @@ -55,4 +60,4 @@ namespace TestImpact { return m_coverage; } -} +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h index 68b09d9e5a..52f3a7c3f6 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h @@ -21,6 +21,7 @@ namespace TestImpact class SourceCoveringTests { public: + explicit SourceCoveringTests(const AZStd::string& path); SourceCoveringTests(const AZStd::string& path, AZStd::vector&& coveringTestTargets); //! Returns the path of this source file. @@ -40,7 +41,7 @@ namespace TestImpact class SourceCoveringTestsList { public: - SourceCoveringTestsList(AZStd::vector&& sourceCoveringTests); + explicit SourceCoveringTestsList(AZStd::vector&& sourceCoveringTests); //! Returns the number of source files in the collection. size_t GetNumSources() const; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp index 49d82e341a..39193d4dd9 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp @@ -20,28 +20,33 @@ namespace TestImpact { ParentTarget::ParentTarget(const TestTarget* target) - : m_buildTarget(target) - , m_target(target) + : m_target(target) { } ParentTarget::ParentTarget(const ProductionTarget* target) - : m_buildTarget(target) - , m_target(target) + : m_target(target) { } bool ParentTarget::operator==(const ParentTarget& other) const { - return m_buildTarget == other.m_buildTarget; + return GetBuildTarget() == other.GetBuildTarget(); } const BuildTarget* ParentTarget::GetBuildTarget() const { - return m_buildTarget; + const BuildTarget* buildTarget; + AZStd::visit([&buildTarget](auto&& target) + { + buildTarget = target; + + }, m_target); + + return buildTarget; } - const AZStd::variant& ParentTarget::GetTarget() const + const Target& ParentTarget::GetTarget() const { return m_target; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h index e6cd89df9d..a3b6783d13 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h @@ -12,15 +12,15 @@ #pragma once +#include + #include #include #include #include -#include namespace TestImpact { - class BuildTarget; class ProductionTarget; class TestTarget; @@ -38,12 +38,11 @@ namespace TestImpact const BuildTarget* GetBuildTarget() const; //! Returns the specialized target pointer for this parent. - const AZStd::variant& GetTarget() const; + const Target& GetTarget() const; bool operator==(const ParentTarget& other) const; private: - const BuildTarget* m_buildTarget; //! The base built target pointer for this parent. - AZStd::variant m_target; //! The specialized target pointer for this parent. + Target m_target; //! The specialized target pointer for this parent. }; } @@ -91,6 +90,6 @@ namespace TestImpact const AZStd::unordered_set& GetCoveringTestTargets() const; private: AZStd::string m_path; //!< The path of this source file. - DependencyData m_dependencyData; //!< + DependencyData m_dependencyData; //!< The dependency data for this source file. }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h index d4346c15ef..0cd6369472 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h @@ -14,10 +14,20 @@ #include +#include #include namespace TestImpact { + class TestTarget; + class ProductionTarget; + + //! Holder for specializations of BuildTarget. + using Target = AZStd::variant; + + //! Optional holder for specializations of BuildTarget. + using OptionalTarget = AZStd::variant; + //! Type id for querying specialized derived target types from base pointer/reference. enum class TargetType : bool { From d880aa5d2257528dfd050c32d0c52def4a106504 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 6 May 2021 15:33:02 +0100 Subject: [PATCH 011/233] Fix anonymous namespaces for unity builds. --- ...TestImpactBuildTargetDescriptorFactory.cpp | 54 +++++++-------- .../TestImpactModuleCoverageFactory.cpp | 65 +++++++++---------- .../TestImpactTestEnumerationSuiteFactory.cpp | 37 +++++------ .../Factory/TestImpactTestRunSuiteFactory.cpp | 57 ++++++++-------- .../TestImpactTestTargetMetaMapFactory.cpp | 9 ++- .../TestImpactTestEnumerationSerializer.cpp | 22 +++---- .../Test/Run/TestImpactTestRunSerializer.cpp | 52 +++++++-------- 7 files changed, 141 insertions(+), 155 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp index 9e82170dff..5e1f2bc890 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp @@ -19,35 +19,6 @@ namespace TestImpact { - namespace - { - // Keys for pertinent JSON node and attribute names - constexpr const char* Keys[] = - { - "target", - "name", - "output_name", - "path", - "sources", - "static", - "input", - "output" - }; - - enum - { - TargetKey, - NameKey, - OutputNameKey, - PathKey, - SourcesKey, - StaticKey, - InputKey, - OutputKey - }; - - } // namespace - AutogenSources PairAutogenSources( const AZStd::vector& inputSources, const AZStd::vector& outputSources, @@ -98,6 +69,31 @@ namespace TestImpact const AZStd::vector& autogenInputExtensionExcludes, const AZStd::string& autogenMatcher) { + // Keys for pertinent JSON node and attribute names + constexpr const char* Keys[] = + { + "target", + "name", + "output_name", + "path", + "sources", + "static", + "input", + "output" + }; + + enum + { + TargetKey, + NameKey, + OutputNameKey, + PathKey, + SourcesKey, + StaticKey, + InputKey, + OutputKey + }; + AZ_TestImpact_Eval(!autogenMatcher.empty(), ArtifactException, "Autogen matcher cannot be empty"); BuildTargetDescriptor buildTargetDescriptor; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp index 889f91c600..68eee409e7 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp @@ -18,46 +18,43 @@ namespace TestImpact { - namespace - { - // Keys for pertinent XML node and attribute names - constexpr const char* Keys[] = - { - "packages", - "name", - "filename", - "coverage", - "classes", - "lines", - "line", - "number", - "hits", - "sources", - "source" - }; - - enum - { - PackagesKey, - NameKey, - FileNameKey, - CoverageKey, - ClassesKey, - LinesKey, - LineKey, - NumberKey, - HitsKey, - SourcesKey, - SourceKey - }; - } // namespace - namespace Cobertura { // Note: OpenCppCoverage appears to have a very liberal interpretation of the Cobertura coverage file format so consider // this implementation to be provisional and coupled to the Windows platform and OpenCppCoverage tool AZStd::vector ModuleCoveragesFactory(const AZStd::string& coverageData) { + // Keys for pertinent XML node and attribute names + constexpr const char* Keys[] = + { + "packages", + "name", + "filename", + "coverage", + "classes", + "lines", + "line", + "number", + "hits", + "sources", + "source" + }; + + enum + { + PackagesKey, + NameKey, + FileNameKey, + CoverageKey, + ClassesKey, + LinesKey, + LineKey, + NumberKey, + HitsKey, + SourcesKey, + SourceKey + }; + AZ_TestImpact_Eval(!coverageData.empty(), ArtifactException, "Cannot parse coverage, string is empty"); AZStd::vector modules; AZStd::vector rawData(coverageData.begin(), coverageData.end()); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.cpp index bf8a19c812..8162256aaf 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.cpp @@ -18,30 +18,27 @@ namespace TestImpact { - namespace - { - // Keys for pertinent XML node and attribute names - constexpr const char* Keys[] = - { - "testsuites", - "testsuite", - "name", - "testcase" - }; - - enum - { - TestSuitesKey, - TestSuiteKey, - NameKey, - TestCaseKey - }; - } // namespace - namespace GTest { AZStd::vector TestEnumerationSuitesFactory(const AZStd::string& testEnumerationData) { + // Keys for pertinent XML node and attribute names + constexpr const char* Keys[] = + { + "testsuites", + "testsuite", + "name", + "testcase" + }; + + enum + { + TestSuitesKey, + TestSuiteKey, + NameKey, + TestCaseKey + }; + AZ_TestImpact_Eval(!testEnumerationData.empty(), ArtifactException, "Cannot parse enumeration, string is empty"); AZStd::vector testSuites; AZStd::vector rawData(testEnumerationData.begin(), testEnumerationData.end()); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp index 506e058824..ba5c398188 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp @@ -19,38 +19,35 @@ namespace TestImpact { - namespace - { - // Keys for pertinent XML node and attribute names - constexpr const char* Keys[] = - { - "testsuites", - "testsuite", - "name", - "testcase", - "status", - "run", - "notrun", - "time" - }; - - enum - { - TestSuitesKey, - TestSuiteKey, - NameKey, - TestCaseKey, - StatusKey, - RunKey, - NotRunKey, - DurationKey - }; - } // namespace - namespace GTest { AZStd::vector TestRunSuitesFactory(const AZStd::string& testEnumerationData) { + // Keys for pertinent XML node and attribute names + constexpr const char* Keys[] = + { + "testsuites", + "testsuite", + "name", + "testcase", + "status", + "run", + "notrun", + "time" + }; + + enum + { + TestSuitesKey, + TestSuiteKey, + NameKey, + TestCaseKey, + StatusKey, + RunKey, + NotRunKey, + DurationKey + }; + AZ_TestImpact_Eval(!testEnumerationData.empty(), ArtifactException, "Cannot parse test run, string is empty"); AZStd::vector testSuites; AZStd::vector rawData(testEnumerationData.begin(), testEnumerationData.end()); @@ -71,7 +68,7 @@ namespace TestImpact return !name.starts_with("DISABLED_") && name.find("/DISABLED_") == AZStd::string::npos; }; - const auto getDuration = [](const AZ::rapidxml::xml_node<>* node) + const auto getDuration = [&Keys](const AZ::rapidxml::xml_node<>* node) { const AZStd::string duration = node->first_attribute(Keys[DurationKey])->value(); return AZStd::chrono::milliseconds(AZStd::stof(duration) * 1000.f); @@ -85,7 +82,7 @@ namespace TestImpact for (auto testcase_node = testsuite_node->first_node(Keys[TestCaseKey]); testcase_node; testcase_node = testcase_node->next_sibling()) { - const auto getStatus = [](const AZ::rapidxml::xml_node<>* node) + const auto getStatus = [&Keys](const AZ::rapidxml::xml_node<>* node) { const AZStd::string status = node->first_attribute(Keys[StatusKey])->value(); if (status == Keys[RunKey]) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp index 9135a3c683..db4d5f8563 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp @@ -19,7 +19,7 @@ namespace TestImpact { - namespace + TestTargetMetaMap TestTargetMetaMapFactory(const AZStd::string& masterTestListData) { // Keys for pertinent JSON node and attribute names constexpr const char* Keys[] = @@ -45,16 +45,15 @@ namespace TestImpact StandAloneKey, NameKey }; - } // namespace - TestTargetMetaMap TestTargetMetaMapFactory(const AZStd::string& masterTestListData) - { + AZ_TestImpact_Eval(!masterTestListData.empty(), ArtifactException, "test meta-data cannot be empty"); + TestTargetMetaMap testMetas; rapidjson::Document masterTestList; if (masterTestList.Parse(masterTestListData.c_str()).HasParseError()) { - throw TestImpact::ArtifactException("Could not parse test meta-data file"); + throw TestImpact::ArtifactException("Could not parse test meta-data"); } const auto tests = masterTestList[Keys[GoogleKey]][Keys[TestKey]][Keys[TestsKey]].GetArray(); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp index 0b171ba404..efed04970d 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp @@ -20,7 +20,7 @@ namespace TestImpact { - namespace + namespace TestEnumFields { // Keys for pertinent JSON node and attribute names constexpr const char* Keys[] = @@ -46,23 +46,23 @@ namespace TestImpact rapidjson::PrettyWriter writer(stringBuffer); writer.StartObject(); - writer.Key(Keys[SuitesKey]); + writer.Key(TestEnumFields::Keys[TestEnumFields::SuitesKey]); writer.StartArray(); for (const auto& suite : testEnum.GetTestSuites()) { writer.StartObject(); - writer.Key(Keys[NameKey]); + writer.Key(TestEnumFields::Keys[TestEnumFields::NameKey]); writer.String(suite.m_name.c_str()); - writer.Key(Keys[EnabledKey]); + writer.Key(TestEnumFields::Keys[TestEnumFields::EnabledKey]); writer.Bool(suite.m_enabled); - writer.Key(Keys[TestsKey]); + writer.Key(TestEnumFields::Keys[TestEnumFields::TestsKey]); writer.StartArray(); for (const auto& test : suite.m_tests) { writer.StartObject(); - writer.Key(Keys[NameKey]); + writer.Key(TestEnumFields::Keys[TestEnumFields::NameKey]); writer.String(test.m_name.c_str()); - writer.Key(Keys[EnabledKey]); + writer.Key(TestEnumFields::Keys[TestEnumFields::EnabledKey]); writer.Bool(test.m_enabled); writer.EndObject(); } @@ -85,13 +85,13 @@ namespace TestImpact throw TestEnumerationException("Could not parse enumeration data"); } - for (const auto& suite : doc[Keys[SuitesKey]].GetArray()) + for (const auto& suite : doc[TestEnumFields::Keys[TestEnumFields::SuitesKey]].GetArray()) { - testSuites.emplace_back(TestEnumerationSuite{suite[Keys[NameKey]].GetString(), suite[Keys[EnabledKey]].GetBool(), {}}); - for (const auto& test : suite[Keys[TestsKey]].GetArray()) + testSuites.emplace_back(TestEnumerationSuite{suite[TestEnumFields::Keys[TestEnumFields::NameKey]].GetString(), suite[TestEnumFields::Keys[TestEnumFields::EnabledKey]].GetBool(), {}}); + for (const auto& test : suite[TestEnumFields::Keys[TestEnumFields::TestsKey]].GetArray()) { testSuites.back().m_tests.emplace_back( - TestEnumerationCase{test[Keys[NameKey]].GetString(), test[Keys[EnabledKey]].GetBool()}); + TestEnumerationCase{test[TestEnumFields::Keys[TestEnumFields::NameKey]].GetString(), test[TestEnumFields::Keys[TestEnumFields::EnabledKey]].GetBool()}); } } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.cpp index 15a135ad25..7f29dd77a9 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.cpp @@ -20,7 +20,7 @@ namespace TestImpact { - namespace + namespace TestRunFields { // Keys for pertinent JSON node and attribute names constexpr const char* Keys[] = @@ -55,11 +55,11 @@ namespace TestImpact writer.StartObject(); // Run duration - writer.Key(Keys[DurationKey]); + writer.Key(TestRunFields::Keys[TestRunFields::DurationKey]); writer.Uint(testRun.GetDuration().count()); // Suites - writer.Key(Keys[SuitesKey]); + writer.Key(TestRunFields::Keys[TestRunFields::SuitesKey]); writer.StartArray(); for (const auto& suite : testRun.GetTestSuites()) @@ -68,19 +68,19 @@ namespace TestImpact writer.StartObject(); // Suite name - writer.Key(Keys[NameKey]); + writer.Key(TestRunFields::Keys[TestRunFields::NameKey]); writer.String(suite.m_name.c_str()); // Suite duration - writer.Key(Keys[DurationKey]); + writer.Key(TestRunFields::Keys[TestRunFields::DurationKey]); writer.Uint(suite.m_duration.count()); // Suite enabled - writer.Key(Keys[EnabledKey]); + writer.Key(TestRunFields::Keys[TestRunFields::EnabledKey]); writer.Bool(suite.m_enabled); // Suite tests - writer.Key(Keys[TestsKey]); + writer.Key(TestRunFields::Keys[TestRunFields::TestsKey]); writer.StartArray(); for (const auto& test : suite.m_tests) { @@ -88,30 +88,30 @@ namespace TestImpact writer.StartObject(); // Test name - writer.Key(Keys[NameKey]); + writer.Key(TestRunFields::Keys[TestRunFields::NameKey]); writer.String(test.m_name.c_str()); // Test enabled - writer.Key(Keys[EnabledKey]); + writer.Key(TestRunFields::Keys[TestRunFields::EnabledKey]); writer.Bool(test.m_enabled); // Test duration - writer.Key(Keys[DurationKey]); + writer.Key(TestRunFields::Keys[TestRunFields::DurationKey]); writer.Uint(test.m_duration.count()); // Test status - writer.Key(Keys[StatusKey]); + writer.Key(TestRunFields::Keys[TestRunFields::StatusKey]); writer.Bool(static_cast(test.m_status)); // Test result if (test.m_status == TestRunStatus::Run) { - writer.Key(Keys[ResultKey]); + writer.Key(TestRunFields::Keys[TestRunFields::ResultKey]); writer.Bool(static_cast(test.m_result.value())); } else { - writer.Key(Keys[ResultKey]); + writer.Key(TestRunFields::Keys[TestRunFields::ResultKey]); writer.Null(); } @@ -146,38 +146,38 @@ namespace TestImpact } // Run duration - const AZStd::chrono::milliseconds runDuration = AZStd::chrono::milliseconds{doc[Keys[DurationKey]].GetUint()}; + const AZStd::chrono::milliseconds runDuration = AZStd::chrono::milliseconds{doc[TestRunFields::Keys[TestRunFields::DurationKey]].GetUint()}; // Suites - for (const auto& suite : doc[Keys[SuitesKey]].GetArray()) + for (const auto& suite : doc[TestRunFields::Keys[TestRunFields::SuitesKey]].GetArray()) { // Suite name - const AZStd::string name = suite[Keys[NameKey]].GetString(); + const AZStd::string name = suite[TestRunFields::Keys[TestRunFields::NameKey]].GetString(); // Suite duration - const AZStd::chrono::milliseconds suiteDuration = AZStd::chrono::milliseconds{suite[Keys[DurationKey]].GetUint()}; + const AZStd::chrono::milliseconds suiteDuration = AZStd::chrono::milliseconds{suite[TestRunFields::Keys[TestRunFields::DurationKey]].GetUint()}; // Suite enabled - const bool enabled = suite[Keys[EnabledKey]].GetBool(); + const bool enabled = suite[TestRunFields::Keys[TestRunFields::EnabledKey]].GetBool(); testSuites.emplace_back(TestRunSuite{ - suite[Keys[NameKey]].GetString(), - suite[Keys[EnabledKey]].GetBool(), + suite[TestRunFields::Keys[TestRunFields::NameKey]].GetString(), + suite[TestRunFields::Keys[TestRunFields::EnabledKey]].GetBool(), {}, - AZStd::chrono::milliseconds{suite[Keys[DurationKey]].GetUint()}}); + AZStd::chrono::milliseconds{suite[TestRunFields::Keys[TestRunFields::DurationKey]].GetUint()}}); // Suite tests - for (const auto& test : suite[Keys[TestsKey]].GetArray()) + for (const auto& test : suite[TestRunFields::Keys[TestRunFields::TestsKey]].GetArray()) { AZStd::optional result; - TestRunStatus status = static_cast(test[Keys[StatusKey]].GetBool()); + TestRunStatus status = static_cast(test[TestRunFields::Keys[TestRunFields::StatusKey]].GetBool()); if (status == TestRunStatus::Run) { - result = static_cast(test[Keys[ResultKey]].GetBool()); + result = static_cast(test[TestRunFields::Keys[TestRunFields::ResultKey]].GetBool()); } - const AZStd::chrono::milliseconds testDuration = AZStd::chrono::milliseconds{test[Keys[DurationKey]].GetUint()}; + const AZStd::chrono::milliseconds testDuration = AZStd::chrono::milliseconds{test[TestRunFields::Keys[TestRunFields::DurationKey]].GetUint()}; testSuites.back().m_tests.emplace_back( - TestRunCase{test[Keys[NameKey]].GetString(), test[Keys[EnabledKey]].GetBool(), result, testDuration, status}); + TestRunCase{test[TestRunFields::Keys[TestRunFields::NameKey]].GetString(), test[TestRunFields::Keys[TestRunFields::EnabledKey]].GetBool(), result, testDuration, status}); } } From f47f712bd07a7af10cd2e6ab48902c36e10f4e7f Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 6 May 2021 15:53:54 +0100 Subject: [PATCH 012/233] Fix missing pragma directive. --- .../Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h index ee245f1ff3..03bbd91454 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h @@ -17,6 +17,8 @@ #include #include +#pragma once + namespace TestImpact { template From 006a9ce85f833b5c53b88a8e97d5d778e93abdb2 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 6 May 2021 16:33:40 +0100 Subject: [PATCH 013/233] Fix unused variable warning. --- .../Code/Tests/Process/TestImpactProcessSchedulerTest.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessSchedulerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessSchedulerTest.cpp index c64b05e431..2b32067688 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessSchedulerTest.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessSchedulerTest.cpp @@ -227,8 +227,6 @@ namespace UnitTest // Expects the process to have exited gracefully of its own accord (i.e. not terminated for any reason by the scheduler) void ProcessSchedulerTestFixtureWithParams::ExpectGracefulExit(TestImpact::ProcessId pid) { - const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; - ExpectSuccessfulLaunch(pid); ExpectExitCondition(pid, TestImpact::ExitCondition::Gracefull); } @@ -236,8 +234,6 @@ namespace UnitTest // Expects the process to have been terminated by the client or scheduler void ProcessSchedulerTestFixtureWithParams::ExpectTerminatedProcess(TestImpact::ProcessId pid) { - const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; - ExpectSuccessfulLaunch(pid); ExpectExitCondition(pid, TestImpact::ExitCondition::Terminated); } @@ -245,8 +241,6 @@ namespace UnitTest // Expects the process to have been terminated by the scheduler due to the process or scheduler timing out void ProcessSchedulerTestFixtureWithParams::ExpectTimeoutProcess(TestImpact::ProcessId pid) { - const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; - ExpectSuccessfulLaunch(pid); ExpectExitCondition(pid, TestImpact::ExitCondition::Timeout); } From 31312606b198bf02c52617b0d74056bb4960d6aa Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 6 May 2021 16:50:52 +0100 Subject: [PATCH 014/233] Fix unused variable warning. --- .../Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp index e416d3836e..e7ebec17d1 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp @@ -332,7 +332,7 @@ namespace UnitTest try { // When attempting to find a target that does not exist - auto target = targetList.GetTargetOrThrow(GenerateBuildTargetName(i + targetList.GetNumTargets())); + targetList.GetTargetOrThrow(GenerateBuildTargetName(i + targetList.GetNumTargets())); // Do not expect the target list construction to succeed FAIL(); From fee010f6021c3f1f7462e80f1f0166a78de3a24e Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 6 May 2021 17:04:22 +0100 Subject: [PATCH 015/233] Fix test utils. --- .../Runtime/Code/Tests/TestImpactTestUtils.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.cpp index 90f384de85..6b2fa72310 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.cpp @@ -1142,7 +1142,7 @@ namespace UnitTest "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetA\\Code\\Tests\\TestImpactTestTargetA.cpp"; sourceCoverage.m_coverage = AZStd::vector(); - auto& lines = sourceCoverage.m_coverage.value(); + auto& lines = sourceCoverage.m_coverage; lines.push_back(TestImpact::LineCoverage{22, 1}); lines.push_back(TestImpact::LineCoverage{23, 1}); lines.push_back(TestImpact::LineCoverage{24, 1}); @@ -1202,7 +1202,7 @@ namespace UnitTest "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetB\\Code\\Tests\\TestImpactTestTargetB.cpp"; sourceCoverage.m_coverage = AZStd::vector(); - auto& lines = sourceCoverage.m_coverage.value(); + auto& lines = sourceCoverage.m_coverage; lines.push_back(TestImpact::LineCoverage{29, 1}); lines.push_back(TestImpact::LineCoverage{30, 1}); lines.push_back(TestImpact::LineCoverage{31, 1}); @@ -1250,7 +1250,7 @@ namespace UnitTest "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp"; sourceCoverage.m_coverage = AZStd::vector(); - auto& lines = sourceCoverage.m_coverage.value(); + auto& lines = sourceCoverage.m_coverage; lines.push_back(TestImpact::LineCoverage{32, 1}); lines.push_back(TestImpact::LineCoverage{33, 1}); lines.push_back(TestImpact::LineCoverage{34, 1}); @@ -1294,7 +1294,7 @@ namespace UnitTest "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp"; sourceCoverage.m_coverage = AZStd::vector(); - auto& lines = sourceCoverage.m_coverage.value(); + auto& lines = sourceCoverage.m_coverage; lines.push_back(TestImpact::LineCoverage{56, 1}); lines.push_back(TestImpact::LineCoverage{57, 1}); lines.push_back(TestImpact::LineCoverage{58, 1}); @@ -1762,18 +1762,18 @@ namespace UnitTest return false; } - if (lhs.m_coverage.has_value() != rhs.m_coverage.has_value()) + if (lhs.m_coverage.empty() != rhs.m_coverage.empty()) { AZ_Error( - "LineCoverage ==", false, "lhs.m_coverage.has_value(): %u, rhs.m_coverage.has_value(): %u", lhs.m_coverage.has_value(), - rhs.m_coverage.has_value()); + "LineCoverage ==", false, "lhs.m_coverage.empty(): %u, rhs.m_coverage.empty(): %u", lhs.m_coverage.empty(), + rhs.m_coverage.empty()); return false; } - if (lhs.m_coverage.has_value()) + if (!lhs.m_coverage.empty()) { return AZStd::equal( - lhs.m_coverage.value().begin(), lhs.m_coverage.value().end(), rhs.m_coverage.value().begin(), + lhs.m_coverage.begin(), lhs.m_coverage.end(), rhs.m_coverage.begin(), [](const TestImpact::LineCoverage& left, const TestImpact::LineCoverage& right) { return left == right; }); From 65550d3f1c3ec5d011485a8d9fdbe973b6789710 Mon Sep 17 00:00:00 2001 From: igarri Date: Mon, 10 May 2021 10:39:01 +0100 Subject: [PATCH 016/233] Entries updating on the table view --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 4 +- .../AssetBrowser/AssetBrowserModel.cpp | 3 +- .../AssetBrowser/AssetBrowserTableModel.cpp | 264 ++++++++++++++++-- .../AssetBrowser/AssetBrowserTableModel.h | 77 ++++- .../Views/AssetBrowserTableView.cpp | 46 ++- .../Views/AssetBrowserTableView.h | 22 +- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 25 +- .../AzAssetBrowser/AzAssetBrowserWindow.h | 2 + 8 files changed, 392 insertions(+), 51 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index 42498eb76e..5e9203cc29 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -31,7 +31,8 @@ namespace AzToolsFramework AssetBrowserFilterModel::AssetBrowserFilterModel(QObject* parent) : QSortFilterProxyModel(parent) { - m_showColumn.insert(AssetBrowserModel::m_column); + m_showColumn.insert(static_cast(AssetBrowserEntry::Column::DisplayName)); + m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); m_collator.setNumericMode(true); AssetBrowserComponentNotificationBus::Handler::BusConnect(); } @@ -163,7 +164,6 @@ namespace AzToolsFramework { m_alreadyRecomputingFilters = false; FilterUpdatedSlotImmediate(); - //beginInsertRows() } ); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp index d7c0164435..1101e11f3a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp @@ -144,7 +144,8 @@ namespace AzToolsFramework if (parent.isValid()) { if ((parent.column() != static_cast(AssetBrowserEntry::Column::DisplayName)) && - (parent.column() != static_cast(AssetBrowserEntry::Column::Name))) + (parent.column() != static_cast(AssetBrowserEntry::Column::Name)) && + (parent.column() != static_cast(AssetBrowserEntry::Column::Path))) { return 0; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index e7a49b2d99..7480d17d47 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -1,4 +1,16 @@ -#include "AssetBrowserTableModel.h" +#include +#include +#include +#include +AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") + +#include +#include + +#include +#include +#include +AZ_POP_DISABLE_WARNING namespace AzToolsFramework { namespace AssetBrowser @@ -6,9 +18,24 @@ namespace AzToolsFramework AssetBrowserTableModel::AssetBrowserTableModel(QObject* parent /* = nullptr */) : QSortFilterProxyModel(parent) { - m_showColumn.insert(static_cast(AssetBrowserEntry::Column::DisplayName)); - m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); + sort(0); + setDynamicSortFilter(false); + setRecursiveFilteringEnabled(true); + AssetBrowserComponentNotificationBus::Handler::BusConnect(); + } + AssetBrowserTableModel::~AssetBrowserTableModel() + { + AssetBrowserComponentNotificationBus::Handler::BusDisconnect(); } + void AssetBrowserTableModel::OnAssetBrowserComponentReady() + { + BuildMap(sourceModel()); + } + void AssetBrowserTableModel::setSourceModel(QAbstractItemModel* sourceModel) + { + QSortFilterProxyModel::setSourceModel(sourceModel); + } + QModelIndex AssetBrowserTableModel::mapToSource(const QModelIndex& proxyIndex) const { Q_ASSERT(!proxyIndex.isValid() || proxyIndex.model() == this); @@ -18,6 +45,11 @@ namespace AzToolsFramework } return m_indexMap[proxyIndex.row()]; } + QModelIndex AssetBrowserTableModel::parent(const QModelIndex& child) const + { + AZ_UNUSED(child); + return QModelIndex(); + } QModelIndex AssetBrowserTableModel::mapFromSource(const QModelIndex& sourceIndex) const { Q_ASSERT(!sourceIndex.isValid() || sourceIndex.model() == sourceModel()); @@ -27,16 +59,14 @@ namespace AzToolsFramework } return createIndex(m_rowMap[sourceIndex], sourceIndex.column(), sourceIndex.internalPointer()); } - //QModelIndex AssetBrowserTableModel::index(int row, int column, const QModelIndex& parent) const - //{ - // //return parent.isValid() ? QModelIndex() : createIndex(row, column , m_indexMap[row].internalPointer()); - // if (!parent.isValid()) - // { - // QModelIndex(); - // } - // return createIndex(row, column, m_indexMap[row].internalPointer()); - //} + QModelIndex AssetBrowserTableModel::index(int row, int column, const QModelIndex& parent) const + { + /*AZ_UNUSED(row); + AZ_UNUSED(column);*/ + return parent.isValid() ? QModelIndex() : createIndex(row, column, m_indexMap[row].internalPointer()); + } + QVariant AssetBrowserTableModel::data(const QModelIndex& index, int role) const { //AZ_UNUSED(role); @@ -47,34 +77,67 @@ namespace AzToolsFramework AssetBrowserEntry* entry = GetAssetEntry(sourceIndex); // static_cast(sourceIndex.internalPointer()); if (entry == nullptr) { - AZ_Assert(false, "ERROR - index internal pointer not pointing to an AssetEntry. Tree provided by the AssetBrowser invalid?"); + AZ_Assert( + false, "ERROR - index internal pointer not pointing to an AssetEntry. Tree provided by the AssetBrowser invalid?"); return Qt::PartiallyChecked; } - return sourceIndex.data(role); //return entry->data(index.column()); + return sourceIndex.data(role); // return entry->data(index.column()); //return QVariant::fromValue(entry); + //AZ_UNUSED(role); + //if (index.isValid()) + //{ + // ////if (role == AssetBrowserModel::EntryRole) + // //{ + // QModelIndex modelIndex = mapFromSource(index); + // auto assetEntry = static_cast(index.internalPointer()); + // return QVariant::fromValue(assetEntry); + //} + //return QVariant(); // AzToolsFramework::AssetBrowser::AssetBrowserModel::data(index, role); } - bool AssetBrowserTableModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const - { - AZ_UNUSED(source_row); - AZ_UNUSED(source_parent); - return true; - } - bool AssetBrowserTableModel::filterAcceptsColumn(int source_column, const QModelIndex&) const + + + int AssetBrowserTableModel::rowCount(const QModelIndex& parent) const { - return m_showColumn.find(source_column) != m_showColumn.end(); + return !parent.isValid() ? m_rowMap.size() : 0; } + int AssetBrowserTableModel::BuildMap(const QAbstractItemModel* model, const QModelIndex& parent, int row) { + //int rows = model ? model->rowCount(parent) : 0; + //for (int i = 0; i < rows; ++i) + //{ + // auto index = model->index(i, 0, parent); + // //if (!model->hasChildren(index)) + // //{ + // beginInsertRows(parent, row, row); + // m_rowMap[index] = row; + // m_indexMap[row] = index; + // endInsertRows(); + // Q_EMIT dataChanged(parent, parent); + // row = row + 1; + // //} + // if (model->hasChildren(index)) + // { + // row = BuildMap(model, index, row); + // } + //} + //return row; int rows = model ? model->rowCount(parent) : 0; for (int i = 0; i < rows; ++i) { auto index = model->index(i, 0, parent); + if (model->hasChildren(index) == false) + { + beginInsertRows(parent, row, row); + m_rowMap[index] = row; + m_indexMap[row] = index; + endInsertRows(); + Q_EMIT dataChanged(parent, parent); + row = row + 1; + } - m_rowMap[index] = row; - m_indexMap[row] = index; - row = row + 1; if (model->hasChildren(index)) { row = BuildMap(model, index, row); @@ -96,8 +159,159 @@ namespace AzToolsFramework } void AssetBrowserTableModel::UpdateMap() { + m_indexMap.clear(); + m_rowMap.clear(); BuildMap(sourceModel()); } + + //---------------------------------------AssetBrowserTableFilterModel-------------------------------------------- + AssetBrowserTableFilterModel::AssetBrowserTableFilterModel(QObject* parent) + : QSortFilterProxyModel(parent) + { + m_showColumn.insert(static_cast(AssetBrowserEntry::Column::DisplayName)); + m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); + AssetBrowserComponentNotificationBus::Handler::BusConnect(); + } + + AssetBrowserTableFilterModel::~AssetBrowserTableFilterModel() + { + AssetBrowserComponentNotificationBus::Handler::BusDisconnect(); + } + + void AssetBrowserTableFilterModel::setSourceModel(QAbstractItemModel* sourceModel) + { + QSortFilterProxyModel::setSourceModel(sourceModel); + } + + void AssetBrowserTableFilterModel::SetFilter(FilterConstType filter) + { + connect(filter.data(), &AssetBrowserEntryFilter::updatedSignal, this, &AssetBrowserTableFilterModel::filterUpdatedSlot); + m_filter = filter; + m_invalidateFilter = true; + // asset browser entries are not guaranteed to have populated when the filter is set, delay filtering until they are + bool isAssetBrowserComponentReady = false; + AssetBrowserComponentRequestBus::BroadcastResult(isAssetBrowserComponentReady, &AssetBrowserComponentRequests::AreEntriesReady); + if (isAssetBrowserComponentReady) + { + OnAssetBrowserComponentReady(); + } + } + + void AssetBrowserTableFilterModel::FilterUpdatedSlotImmediate() + { + auto compFilter = qobject_cast>(m_filter); + if (compFilter) + { + auto& subFilters = compFilter->GetSubFilters(); + auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool { + auto assetTypeFilter = qobject_cast>(filter); + return !assetTypeFilter.isNull(); + }); + if (it != subFilters.end()) + { + m_assetTypeFilter = qobject_cast>(*it); + } + it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool { + auto stringFilter = qobject_cast>(filter); + return !stringFilter.isNull(); + }); + if (it != subFilters.end()) + { + m_stringFilter = qobject_cast>(*it); + } + } + invalidateFilter(); + Q_EMIT filterChanged(); + } + + void AssetBrowserTableFilterModel::OnAssetBrowserComponentReady() + { + if (m_invalidateFilter) + { + invalidateFilter(); + m_invalidateFilter = false; + } + Q_EMIT entriesUpdated(); + } + + bool AssetBrowserTableFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const + { + AZ_UNUSED(source_row); + AZ_UNUSED(source_parent); + QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); + if (!idx.isValid()) + { + return false; + } + // no filter present, every entry is visible + if (!m_filter) + { + return true; + } + + //// the entry is the internal pointer of the index + //auto entry = static_cast(idx.internalPointer()); + + //if (entry) + //{ + // // root should return true even if its not displayed in the treeview + // if (entry && entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Root) + // { + // return true; + // } + // return m_filter->Match(entry); + //} + return true; + } + + bool AssetBrowserTableFilterModel::filterAcceptsColumn(int source_column, const QModelIndex&) const + { + return m_showColumn.find(source_column) != m_showColumn.end(); + } + + bool AssetBrowserTableFilterModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const + { + if (source_left.column() == source_right.column()) + { + QVariant leftData = sourceModel()->data(source_left, AssetBrowserModel::Roles::EntryRole); + QVariant rightData = sourceModel()->data(source_right, AssetBrowserModel::Roles::EntryRole); + if (leftData.canConvert() && rightData.canConvert()) + { + auto leftEntry = qvariant_cast(leftData); + auto rightEntry = qvariant_cast(rightData); + + // folders should always come first + if (azrtti_istypeof(leftEntry) && + azrtti_istypeof(rightEntry)) + { + return false; + } + if (azrtti_istypeof(leftEntry) && + azrtti_istypeof(rightEntry)) + { + return true; + } + + // if both entries are of same type, sort alphabetically + return m_collator.compare(leftEntry->GetDisplayName(), rightEntry->GetDisplayName()) > 0; + } + } + return QSortFilterProxyModel::lessThan(source_left, source_right); + } + + void AssetBrowserTableFilterModel::filterUpdatedSlot() + { + if (!m_alreadyRecomputingFilters) + { + m_alreadyRecomputingFilters = true; + // de-bounce it, since we may get many filter updates all at once. + QTimer::singleShot(0, this, [this]() { + m_alreadyRecomputingFilters = false; + FilterUpdatedSlotImmediate(); + }); + } + } + } // namespace AssetBrowser } // namespace AzToolsFramework #include "AssetBrowser/moc_AssetBrowserTableModel.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index ef87ad8512..8ffd1a469e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -21,32 +21,87 @@ namespace AzToolsFramework { class AssetBrowserTableModel : public QSortFilterProxyModel + , public AssetBrowserComponentNotificationBus::Handler { Q_OBJECT + public: AZ_CLASS_ALLOCATOR(AssetBrowserTableModel, AZ::SystemAllocator, 0); explicit AssetBrowserTableModel(QObject* parent = nullptr); - - QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; - QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; - //QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + ~AssetBrowserTableModel(); + //////////////////////////////////////////////////////////////////// + // AssetBrowserComponentNotificationBus + //////////////////////////////////////////////////////////////////// + void OnAssetBrowserComponentReady() override; + void setSourceModel(QAbstractItemModel* sourceModel) override; + //////////////////////////////////////////////////////////////////// + // QSortFilterProxyModel + QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; + QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override; + QModelIndex parent(const QModelIndex& child) const override; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; public Q_SLOTS: void UpdateMap(); + protected: + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + //QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override; + //////////////////////////////////////////////////////////////////// + private: + AssetBrowserEntry* GetAssetEntry(QModelIndex index) const; + int BuildMap(const QAbstractItemModel* model, const QModelIndex& parent = QModelIndex(), int row = 0); + private: + QMap m_indexMap; + QMap m_rowMap; + }; + + class AssetBrowserTableFilterModel + : public QSortFilterProxyModel + , public AssetBrowserComponentNotificationBus::Handler + { + Q_OBJECT + public: + explicit AssetBrowserTableFilterModel(QObject* parent = nullptr); + ~AssetBrowserTableFilterModel(); + + void setSourceModel(QAbstractItemModel* sourceModel) override; + // asset type filtering + void SetFilter(FilterConstType filter); + void FilterUpdatedSlotImmediate(); + + ////////////////////////////////////////////////////////////////////////// + // AssetBrowserComponentNotificationBus + ////////////////////////////////////////////////////////////////////////// + void OnAssetBrowserComponentReady() override; + + Q_SIGNALS: + void filterChanged(); + void entriesUpdated(); + + ////////////////////////////////////////////////////////////////////////// + // QSortFilterProxyModel protected: bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; bool filterAcceptsColumn(int source_column, const QModelIndex& /*source_parent*/) const override; + bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override; + ////////////////////////////////////////////////////////////////////////// - private: - int BuildMap(const QAbstractItemModel* model, const QModelIndex& parent = QModelIndex(), int row = 0); - AssetBrowserEntry* GetAssetEntry(QModelIndex index) const; + public Q_SLOTS: + void filterUpdatedSlot(); private: AZStd::fixed_unordered_set(AssetBrowserEntry::Column::Count)> m_showColumn; - QMap m_indexMap; - QMap m_rowMap; + bool m_alreadyRecomputingFilters = false; + // asset source name match filter + FilterConstType m_filter; + AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...' + QWeakPointer m_stringFilter; + QWeakPointer m_assetTypeFilter; + QCollator m_collator; // cache the collator as its somewhat expensive to constantly create and destroy one. + AZ_POP_DISABLE_WARNING + bool m_invalidateFilter = false; }; - } -} + } // namespace AssetBrowser +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp index 927deb0039..aa35d99894 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -41,13 +41,13 @@ namespace AzToolsFramework { setSortingEnabled(true); setItemDelegate(m_delegate); - // header()->hide(); + //header()->hide(); setContextMenuPolicy(Qt::CustomContextMenu); setMouseTracking(true); connect(this, &QTableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu); - //connect(m_scTimer, &QTimer::timeout, this, &AssetBrowserTreeView::OnUpdateSCThumbnailsList); + connect(m_scTimer, &QTimer::timeout, this, &AssetBrowserTableView::OnUpdateSCThumbnailsList); AssetBrowserViewRequestBus::Handler::BusConnect(); AssetBrowserComponentNotificationBus::Handler::BusConnect(); @@ -59,9 +59,9 @@ namespace AzToolsFramework } void AssetBrowserTableView::setModel(QAbstractItemModel* model) { - m_sourceModel = qobject_cast(model); - AZ_Assert(m_sourceModel, "Expecting AssetBrowserTableModel"); - m_sourceFilterModel = qobject_cast(m_sourceModel->sourceModel()); + m_filterModel = qobject_cast(model); + AZ_Assert(m_filterModel, "Expecting AssetBrowserTableModel"); + m_sourceModel = qobject_cast(m_filterModel->sourceModel()); QTableView::setModel(model); } void AssetBrowserTableView::SetName(const QString& name) @@ -76,9 +76,40 @@ namespace AzToolsFramework } AZStd::vector AssetBrowserTableView::GetSelectedAssets() const { - return AZStd::vector(); + QModelIndexList sourceIndexes{}; + //for (const auto& index : selectedIndexes()) + //{ + // sourceIndexes.push_back(m_sourceModel->mapToSource(index)); + //} + + AZStd::vector entries; + //AssetBrowserModel::SourceIndexesToAssetDatabaseEntries(sourceIndexes, entries); + return entries; } + void AssetBrowserTableView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) + { + AZ_UNUSED(selected); + AZ_UNUSED(deselected); + } + void AssetBrowserTableView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) + { + // if selected entry is being removed, clear selection so not to select (and attempt to preview) other entries potentially + // marked for deletion + if (selectionModel() && selectionModel()->selectedIndexes().size() == 1) + { + QModelIndex selectedIndex = selectionModel()->selectedIndexes().first(); + QModelIndex parentSelectedIndex = selectedIndex.parent(); + if (parentSelectedIndex == parent && selectedIndex.row() >= start && selectedIndex.row() <= end) + { + selectionModel()->clear(); + } + } + QTableView::rowsAboutToBeRemoved(parent, start, end); + } + void AssetBrowserTableView::OnUpdateSCThumbnailsList() + { + } void AssetBrowserTableView::SelectProduct(AZ::Data::AssetId assetID) { AZ_UNUSED(assetID); @@ -91,6 +122,9 @@ namespace AzToolsFramework void AssetBrowserTableView::ClearFilter() { + emit ClearStringFilter(); + emit ClearTypeFilter(); + m_sourceModel->FilterUpdatedSlotImmediate(); } void AssetBrowserTableView::Update() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h index bec24cca9b..aef7353334 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -48,18 +48,32 @@ namespace AzToolsFramework void OnAssetBrowserComponentReady() override; ////////////////////////////////////////////////////////////////////////// - private Q_SLOTS: - void OnContextMenu(const QPoint& point); + + Q_SIGNALS: + void selectionChangedSignal(const QItemSelection& selected, const QItemSelection& deselected); + void ClearStringFilter(); + void ClearTypeFilter(); + + protected Q_SLOTS: + void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override; + void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override; //! Get all visible source entries and place them in a queue to update their source control status //void OnUpdateSCThumbnailsList(); private: QString m_name; - QPointer m_sourceFilterModel = nullptr; - QPointer m_sourceModel = nullptr; + QPointer m_filterModel = nullptr; + QPointer m_sourceModel = nullptr; EntryDelegate* m_delegate = nullptr; + QTimer* m_scTimer = nullptr; + const int m_scUpdateInterval = 100; + + private Q_SLOTS: + void OnContextMenu(const QPoint& point); + //! Get all visible source entries and place them in a queue to update their source control status + void OnUpdateSCThumbnailsList(); }; } // namespace AssetBrowser } // namespace AzToolsFramework diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index d67ee0d0ca..fb850f4f26 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -68,6 +68,7 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) , m_ui(new Ui::AzAssetBrowserWindowClass()) , m_filterModel(new AzToolsFramework::AssetBrowser::AssetBrowserFilterModel(parent)) , m_tableModel(new AzToolsFramework::AssetBrowser::AssetBrowserTableModel(parent)) + , m_tableFilterModel(new AzToolsFramework::AssetBrowser::AssetBrowserTableFilterModel(parent)) { m_ui->setupUi(this); m_ui->m_searchWidget->Setup(true, true); @@ -82,12 +83,18 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_tableModel->setSourceModel(m_filterModel.data()); //m_tableModel->setSourceModel(m_assetBrowserModel); + m_tableFilterModel->setSourceModel(m_tableModel.data()); + m_tableFilterModel->SetFilter(m_ui->m_searchWidget->GetFilter()); + + m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); - m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data()); + m_ui->m_assetBrowserTreeViewWidget->hideColumn(static_cast(AssetBrowserEntry::Column::Path)); + //m_ui->m_assetBrowserTableViewWidget->setModel(m_tableFilterModel.data()); + m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data()); m_ui->m_assetBrowserTableViewWidget->setVisible(false); - connect(m_filterModel.data(), &AssetBrowserFilterModel::entriesUpdated, m_tableModel.data(), &AssetBrowserTableModel::UpdateMap); + //connect(m_filterModel.data(), &AssetBrowserFilterModel::entriesUpdated, m_tableModel.data(), &AssetBrowserTableModel::UpdateMap); connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), &AssetBrowserFilterModel::filterUpdatedSlot); @@ -97,6 +104,17 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) const bool selectFirstFilteredIndex = false; m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); }); + + //connect( m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_tableFilterModel.data(), + // &AssetBrowserTableFilterModel::filterUpdatedSlot); + //connect(m_tableFilterModel.data(), &AssetBrowserTableFilterModel::filterChanged, this, [this]() { + // const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty(); + // const bool selectFirstFilteredIndex = false; + // m_ui->m_assetBrowserTableViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); + //}); + + connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, m_tableModel.data(), &AssetBrowserTableModel::UpdateMap); + connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, this, &AzAssetBrowserWindow::SelectionChangedSlot); connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); @@ -104,6 +122,9 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, &SearchWidget::ClearStringFilter); connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter); + connect(m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget, &SearchWidget::ClearStringFilter); + connect(m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter); + m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main"); diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h index 1a5604b98f..a8a8474ef8 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h @@ -31,6 +31,7 @@ namespace AzToolsFramework class AssetBrowserFilterModel; class AssetBrowserTableModel; class AssetBrowserModel; + class AssetBrowserTableFilterModel; } } @@ -55,6 +56,7 @@ private: QScopedPointer m_ui; QScopedPointer m_filterModel; QScopedPointer m_tableModel; + QScopedPointer m_tableFilterModel; AzToolsFramework::AssetBrowser::AssetBrowserModel* m_assetBrowserModel; void UpdatePreview() const; From 6e15e87e41d610391facaeb210bc0d5b484c1d1c Mon Sep 17 00:00:00 2001 From: igarri Date: Mon, 10 May 2021 15:52:35 +0100 Subject: [PATCH 017/233] Asset Browser Table view filtering --- .../AssetBrowser/AssetBrowserFilterModel.h | 2 +- .../AssetBrowser/AssetBrowserTableModel.cpp | 302 ++++++++++-------- .../AssetBrowser/AssetBrowserTableModel.h | 87 ++--- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 6 +- .../AzAssetBrowser/AzAssetBrowserWindow.h | 2 +- 5 files changed, 217 insertions(+), 182 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h index 6aa1a08e45..3fff43859f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h @@ -45,7 +45,7 @@ namespace AzToolsFramework //asset type filtering void SetFilter(FilterConstType filter); void FilterUpdatedSlotImmediate(); - + const FilterConstType& GetFilter() const { return m_filter; } ////////////////////////////////////////////////////////////////////////// // AssetBrowserComponentNotificationBus ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index 7480d17d47..b75d3e9400 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -5,7 +5,7 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") #include -#include +#include #include #include @@ -29,10 +29,12 @@ namespace AzToolsFramework } void AssetBrowserTableModel::OnAssetBrowserComponentReady() { - BuildMap(sourceModel()); + //BuildMap(sourceModel()); } void AssetBrowserTableModel::setSourceModel(QAbstractItemModel* sourceModel) { + m_filterModel = qobject_cast(sourceModel); + AZ_Assert(m_filterModel, "Expecting AssetBrowserFilterModel"); QSortFilterProxyModel::setSourceModel(sourceModel); } @@ -60,6 +62,18 @@ namespace AzToolsFramework return createIndex(m_rowMap[sourceIndex], sourceIndex.column(), sourceIndex.internalPointer()); } + bool AssetBrowserTableModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const + { + AZ_UNUSED(source_row); + AZ_UNUSED(source_parent); + // no filter present, every entry is not visible + if (!m_filterModel->GetFilter()) + { + return false; + } + return true; + } + QModelIndex AssetBrowserTableModel::index(int row, int column, const QModelIndex& parent) const { /*AZ_UNUSED(row); @@ -159,158 +173,172 @@ namespace AzToolsFramework } void AssetBrowserTableModel::UpdateMap() { - m_indexMap.clear(); - m_rowMap.clear(); + //m_indexMap.clear(); + //m_rowMap.clear(); + + if (m_indexMap.size() > 0) + { + //beginRemoveRows(m_indexMap.first().parent(), m_indexMap.first().row(), m_indexMap.last().row()); + for (const auto& key : m_indexMap.keys()) + { + beginRemoveRows(m_indexMap[key], m_indexMap[key].row(), m_indexMap[key].row()); + m_rowMap.remove(m_indexMap[key]); + m_indexMap.remove(key); + endRemoveRows(); + } + //endRemoveRows(); + } + BuildMap(sourceModel()); } //---------------------------------------AssetBrowserTableFilterModel-------------------------------------------- - AssetBrowserTableFilterModel::AssetBrowserTableFilterModel(QObject* parent) - : QSortFilterProxyModel(parent) - { - m_showColumn.insert(static_cast(AssetBrowserEntry::Column::DisplayName)); - m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); - AssetBrowserComponentNotificationBus::Handler::BusConnect(); - } + //AssetBrowserTableFilterModel::AssetBrowserTableFilterModel(QObject* parent) + // : QSortFilterProxyModel(parent) + //{ + // m_showColumn.insert(static_cast(AssetBrowserEntry::Column::DisplayName)); + // m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); + // AssetBrowserComponentNotificationBus::Handler::BusConnect(); + //} - AssetBrowserTableFilterModel::~AssetBrowserTableFilterModel() - { - AssetBrowserComponentNotificationBus::Handler::BusDisconnect(); - } + //AssetBrowserTableFilterModel::~AssetBrowserTableFilterModel() + //{ + // AssetBrowserComponentNotificationBus::Handler::BusDisconnect(); + //} - void AssetBrowserTableFilterModel::setSourceModel(QAbstractItemModel* sourceModel) - { - QSortFilterProxyModel::setSourceModel(sourceModel); - } + //void AssetBrowserTableFilterModel::setSourceModel(QAbstractItemModel* sourceModel) + //{ + // QSortFilterProxyModel::setSourceModel(sourceModel); + //} - void AssetBrowserTableFilterModel::SetFilter(FilterConstType filter) - { - connect(filter.data(), &AssetBrowserEntryFilter::updatedSignal, this, &AssetBrowserTableFilterModel::filterUpdatedSlot); - m_filter = filter; - m_invalidateFilter = true; - // asset browser entries are not guaranteed to have populated when the filter is set, delay filtering until they are - bool isAssetBrowserComponentReady = false; - AssetBrowserComponentRequestBus::BroadcastResult(isAssetBrowserComponentReady, &AssetBrowserComponentRequests::AreEntriesReady); - if (isAssetBrowserComponentReady) - { - OnAssetBrowserComponentReady(); - } - } + //void AssetBrowserTableFilterModel::SetFilter(FilterConstType filter) + //{ + // connect(filter.data(), &AssetBrowserEntryFilter::updatedSignal, this, &AssetBrowserTableFilterModel::filterUpdatedSlot); + // m_filter = filter; + // m_invalidateFilter = true; + // // asset browser entries are not guaranteed to have populated when the filter is set, delay filtering until they are + // bool isAssetBrowserComponentReady = false; + // AssetBrowserComponentRequestBus::BroadcastResult(isAssetBrowserComponentReady, &AssetBrowserComponentRequests::AreEntriesReady); + // if (isAssetBrowserComponentReady) + // { + // OnAssetBrowserComponentReady(); + // } + //} - void AssetBrowserTableFilterModel::FilterUpdatedSlotImmediate() - { - auto compFilter = qobject_cast>(m_filter); - if (compFilter) - { - auto& subFilters = compFilter->GetSubFilters(); - auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool { - auto assetTypeFilter = qobject_cast>(filter); - return !assetTypeFilter.isNull(); - }); - if (it != subFilters.end()) - { - m_assetTypeFilter = qobject_cast>(*it); - } - it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool { - auto stringFilter = qobject_cast>(filter); - return !stringFilter.isNull(); - }); - if (it != subFilters.end()) - { - m_stringFilter = qobject_cast>(*it); - } - } - invalidateFilter(); - Q_EMIT filterChanged(); - } + //void AssetBrowserTableFilterModel::FilterUpdatedSlotImmediate() + //{ + // auto compFilter = qobject_cast>(m_filter); + // if (compFilter) + // { + // auto& subFilters = compFilter->GetSubFilters(); + // auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool { + // auto assetTypeFilter = qobject_cast>(filter); + // return !assetTypeFilter.isNull(); + // }); + // if (it != subFilters.end()) + // { + // m_assetTypeFilter = qobject_cast>(*it); + // } + // it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool { + // auto stringFilter = qobject_cast>(filter); + // return !stringFilter.isNull(); + // }); + // if (it != subFilters.end()) + // { + // m_stringFilter = qobject_cast>(*it); + // } + // } + // invalidateFilter(); + // Q_EMIT filterChanged(); + //} - void AssetBrowserTableFilterModel::OnAssetBrowserComponentReady() - { - if (m_invalidateFilter) - { - invalidateFilter(); - m_invalidateFilter = false; - } - Q_EMIT entriesUpdated(); - } + //void AssetBrowserTableFilterModel::OnAssetBrowserComponentReady() + //{ + // if (m_invalidateFilter) + // { + // invalidateFilter(); + // m_invalidateFilter = false; + // } + // Q_EMIT entriesUpdated(); + //} - bool AssetBrowserTableFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const - { - AZ_UNUSED(source_row); - AZ_UNUSED(source_parent); - QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); - if (!idx.isValid()) - { - return false; - } - // no filter present, every entry is visible - if (!m_filter) - { - return true; - } + //bool AssetBrowserTableFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const + //{ + // AZ_UNUSED(source_row); + // AZ_UNUSED(source_parent); + // QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); + // if (!idx.isValid()) + // { + // return false; + // } + // // no filter present, every entry is visible + // if (!m_filter) + // { + // return true; + // } - //// the entry is the internal pointer of the index - //auto entry = static_cast(idx.internalPointer()); + // //// the entry is the internal pointer of the index + // //auto entry = static_cast(idx.internalPointer()); - //if (entry) - //{ - // // root should return true even if its not displayed in the treeview - // if (entry && entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Root) - // { - // return true; - // } - // return m_filter->Match(entry); - //} - return true; - } + // //if (entry) + // //{ + // // // root should return true even if its not displayed in the treeview + // // if (entry && entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Root) + // // { + // // return true; + // // } + // // return m_filter->Match(entry); + // //} + // return true; + //} - bool AssetBrowserTableFilterModel::filterAcceptsColumn(int source_column, const QModelIndex&) const - { - return m_showColumn.find(source_column) != m_showColumn.end(); - } + //bool AssetBrowserTableFilterModel::filterAcceptsColumn(int source_column, const QModelIndex&) const + //{ + // return m_showColumn.find(source_column) != m_showColumn.end(); + //} - bool AssetBrowserTableFilterModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const - { - if (source_left.column() == source_right.column()) - { - QVariant leftData = sourceModel()->data(source_left, AssetBrowserModel::Roles::EntryRole); - QVariant rightData = sourceModel()->data(source_right, AssetBrowserModel::Roles::EntryRole); - if (leftData.canConvert() && rightData.canConvert()) - { - auto leftEntry = qvariant_cast(leftData); - auto rightEntry = qvariant_cast(rightData); + //bool AssetBrowserTableFilterModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const + //{ + // if (source_left.column() == source_right.column()) + // { + // QVariant leftData = sourceModel()->data(source_left, AssetBrowserModel::Roles::EntryRole); + // QVariant rightData = sourceModel()->data(source_right, AssetBrowserModel::Roles::EntryRole); + // if (leftData.canConvert() && rightData.canConvert()) + // { + // auto leftEntry = qvariant_cast(leftData); + // auto rightEntry = qvariant_cast(rightData); - // folders should always come first - if (azrtti_istypeof(leftEntry) && - azrtti_istypeof(rightEntry)) - { - return false; - } - if (azrtti_istypeof(leftEntry) && - azrtti_istypeof(rightEntry)) - { - return true; - } + // // folders should always come first + // if (azrtti_istypeof(leftEntry) && + // azrtti_istypeof(rightEntry)) + // { + // return false; + // } + // if (azrtti_istypeof(leftEntry) && + // azrtti_istypeof(rightEntry)) + // { + // return true; + // } - // if both entries are of same type, sort alphabetically - return m_collator.compare(leftEntry->GetDisplayName(), rightEntry->GetDisplayName()) > 0; - } - } - return QSortFilterProxyModel::lessThan(source_left, source_right); - } + // // if both entries are of same type, sort alphabetically + // return m_collator.compare(leftEntry->GetDisplayName(), rightEntry->GetDisplayName()) > 0; + // } + // } + // return QSortFilterProxyModel::lessThan(source_left, source_right); + //} - void AssetBrowserTableFilterModel::filterUpdatedSlot() - { - if (!m_alreadyRecomputingFilters) - { - m_alreadyRecomputingFilters = true; - // de-bounce it, since we may get many filter updates all at once. - QTimer::singleShot(0, this, [this]() { - m_alreadyRecomputingFilters = false; - FilterUpdatedSlotImmediate(); - }); - } - } + //void AssetBrowserTableFilterModel::filterUpdatedSlot() + //{ + // if (!m_alreadyRecomputingFilters) + // { + // m_alreadyRecomputingFilters = true; + // // de-bounce it, since we may get many filter updates all at once. + // QTimer::singleShot(0, this, [this]() { + // m_alreadyRecomputingFilters = false; + // FilterUpdatedSlotImmediate(); + // }); + // } + //} } // namespace AssetBrowser } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index 8ffd1a469e..34cf8432ff 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -7,6 +7,8 @@ #include #include #include +//#include +#include AZ_PUSH_DISABLE_WARNING( 4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...' @@ -19,6 +21,9 @@ namespace AzToolsFramework { namespace AssetBrowser { + class AssetBrowserFilterModel; + + class AssetBrowserTableModel : public QSortFilterProxyModel , public AssetBrowserComponentNotificationBus::Handler @@ -38,6 +43,7 @@ namespace AzToolsFramework // QSortFilterProxyModel QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override; + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; QModelIndex parent(const QModelIndex& child) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; @@ -53,55 +59,56 @@ namespace AzToolsFramework AssetBrowserEntry* GetAssetEntry(QModelIndex index) const; int BuildMap(const QAbstractItemModel* model, const QModelIndex& parent = QModelIndex(), int row = 0); private: + QPointer m_filterModel; QMap m_indexMap; QMap m_rowMap; }; - class AssetBrowserTableFilterModel - : public QSortFilterProxyModel - , public AssetBrowserComponentNotificationBus::Handler - { - Q_OBJECT - public: - explicit AssetBrowserTableFilterModel(QObject* parent = nullptr); - ~AssetBrowserTableFilterModel(); + //class AssetBrowserTableFilterModel + // : public QSortFilterProxyModel + // , public AssetBrowserComponentNotificationBus::Handler + //{ + // Q_OBJECT + //public: + // explicit AssetBrowserTableFilterModel(QObject* parent = nullptr); + // ~AssetBrowserTableFilterModel(); - void setSourceModel(QAbstractItemModel* sourceModel) override; - // asset type filtering - void SetFilter(FilterConstType filter); - void FilterUpdatedSlotImmediate(); + // void setSourceModel(QAbstractItemModel* sourceModel) override; + // // asset type filtering + // void SetFilter(FilterConstType filter); + // void FilterUpdatedSlotImmediate(); - ////////////////////////////////////////////////////////////////////////// - // AssetBrowserComponentNotificationBus - ////////////////////////////////////////////////////////////////////////// - void OnAssetBrowserComponentReady() override; + // ////////////////////////////////////////////////////////////////////////// + // // AssetBrowserComponentNotificationBus + // ////////////////////////////////////////////////////////////////////////// + // void OnAssetBrowserComponentReady() override; - Q_SIGNALS: - void filterChanged(); - void entriesUpdated(); + //Q_SIGNALS: + // void filterChanged(); + // void entriesUpdated(); - ////////////////////////////////////////////////////////////////////////// - // QSortFilterProxyModel - protected: - bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; - bool filterAcceptsColumn(int source_column, const QModelIndex& /*source_parent*/) const override; - bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override; - ////////////////////////////////////////////////////////////////////////// + // ////////////////////////////////////////////////////////////////////////// + // // QSortFilterProxyModel + //protected: + // bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; + // bool filterAcceptsColumn(int source_column, const QModelIndex& /*source_parent*/) const override; + // bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override; + // ////////////////////////////////////////////////////////////////////////// - public Q_SLOTS: - void filterUpdatedSlot(); + //public Q_SLOTS: + // void filterUpdatedSlot(); - private: - AZStd::fixed_unordered_set(AssetBrowserEntry::Column::Count)> m_showColumn; - bool m_alreadyRecomputingFilters = false; - // asset source name match filter - FilterConstType m_filter; - AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...' - QWeakPointer m_stringFilter; - QWeakPointer m_assetTypeFilter; - QCollator m_collator; // cache the collator as its somewhat expensive to constantly create and destroy one. - AZ_POP_DISABLE_WARNING - bool m_invalidateFilter = false; - }; + //private: + // AZStd::fixed_unordered_set(AssetBrowserEntry::Column::Count)> m_showColumn; + // bool m_alreadyRecomputingFilters = false; + // // asset source name match filter + // FilterConstType m_filter; + // AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...' + // QWeakPointer m_stringFilter; + // QWeakPointer m_assetTypeFilter; + // QCollator m_collator; // cache the collator as its somewhat expensive to constantly create and destroy one. + // AZ_POP_DISABLE_WARNING + // bool m_invalidateFilter = false; + //}; } // namespace AssetBrowser } // namespace AzToolsFramework diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index fb850f4f26..dd4b3b0cad 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -68,7 +68,7 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) , m_ui(new Ui::AzAssetBrowserWindowClass()) , m_filterModel(new AzToolsFramework::AssetBrowser::AssetBrowserFilterModel(parent)) , m_tableModel(new AzToolsFramework::AssetBrowser::AssetBrowserTableModel(parent)) - , m_tableFilterModel(new AzToolsFramework::AssetBrowser::AssetBrowserTableFilterModel(parent)) + /*, m_tableFilterModel(new AzToolsFramework::AssetBrowser::AssetBrowserTableFilterModel(parent))*/ { m_ui->setupUi(this); m_ui->m_searchWidget->Setup(true, true); @@ -83,8 +83,8 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_tableModel->setSourceModel(m_filterModel.data()); //m_tableModel->setSourceModel(m_assetBrowserModel); - m_tableFilterModel->setSourceModel(m_tableModel.data()); - m_tableFilterModel->SetFilter(m_ui->m_searchWidget->GetFilter()); + //m_tableFilterModel->setSourceModel(m_tableModel.data()); + //m_tableFilterModel->SetFilter(m_ui->m_searchWidget->GetFilter()); m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h index a8a8474ef8..801af3e2a8 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h @@ -56,7 +56,7 @@ private: QScopedPointer m_ui; QScopedPointer m_filterModel; QScopedPointer m_tableModel; - QScopedPointer m_tableFilterModel; + //QScopedPointer m_tableFilterModel; AzToolsFramework::AssetBrowser::AssetBrowserModel* m_assetBrowserModel; void UpdatePreview() const; From 1c990b2ef6b206c254e810d0301365c0a411ab88 Mon Sep 17 00:00:00 2001 From: igarri Date: Wed, 12 May 2021 11:03:02 +0100 Subject: [PATCH 018/233] Filter Sorting Working --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 3 +- .../AssetBrowser/AssetBrowserFilterModel.h | 1 - .../AssetBrowser/AssetBrowserTableModel.cpp | 234 ++---------------- .../AssetBrowser/AssetBrowserTableModel.h | 60 +---- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 20 +- 5 files changed, 33 insertions(+), 285 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index 5e9203cc29..34f8b97b5a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -32,7 +32,7 @@ namespace AzToolsFramework : QSortFilterProxyModel(parent) { m_showColumn.insert(static_cast(AssetBrowserEntry::Column::DisplayName)); - m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); + //m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); m_collator.setNumericMode(true); AssetBrowserComponentNotificationBus::Handler::BusConnect(); } @@ -63,7 +63,6 @@ namespace AzToolsFramework invalidateFilter(); m_invalidateFilter = false; } - Q_EMIT entriesUpdated(); } bool AssetBrowserFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h index 3fff43859f..6cccc53eb6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h @@ -53,7 +53,6 @@ namespace AzToolsFramework Q_SIGNALS: void filterChanged(); - void entriesUpdated(); ////////////////////////////////////////////////////////////////////////// //QSortFilterProxyModel diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index b75d3e9400..ce9e97520a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -7,9 +7,7 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") #include #include -#include #include -#include AZ_POP_DISABLE_WARNING namespace AzToolsFramework { @@ -18,18 +16,7 @@ namespace AzToolsFramework AssetBrowserTableModel::AssetBrowserTableModel(QObject* parent /* = nullptr */) : QSortFilterProxyModel(parent) { - sort(0); setDynamicSortFilter(false); - setRecursiveFilteringEnabled(true); - AssetBrowserComponentNotificationBus::Handler::BusConnect(); - } - AssetBrowserTableModel::~AssetBrowserTableModel() - { - AssetBrowserComponentNotificationBus::Handler::BusDisconnect(); - } - void AssetBrowserTableModel::OnAssetBrowserComponentReady() - { - //BuildMap(sourceModel()); } void AssetBrowserTableModel::setSourceModel(QAbstractItemModel* sourceModel) { @@ -69,86 +56,68 @@ namespace AzToolsFramework // no filter present, every entry is not visible if (!m_filterModel->GetFilter()) { - return false; + return true; } return true; } QModelIndex AssetBrowserTableModel::index(int row, int column, const QModelIndex& parent) const { - /*AZ_UNUSED(row); - AZ_UNUSED(column);*/ return parent.isValid() ? QModelIndex() : createIndex(row, column, m_indexMap[row].internalPointer()); } QVariant AssetBrowserTableModel::data(const QModelIndex& index, int role) const { - //AZ_UNUSED(role); auto sourceIndex = mapToSource(index); if (!sourceIndex.isValid()) return QVariant(); - AssetBrowserEntry* entry = GetAssetEntry(sourceIndex); // static_cast(sourceIndex.internalPointer()); + AssetBrowserEntry* entry = GetAssetEntry(sourceIndex); if (entry == nullptr) { - AZ_Assert( - false, "ERROR - index internal pointer not pointing to an AssetEntry. Tree provided by the AssetBrowser invalid?"); + AZ_Assert(false, "ERROR - index internal pointer not pointing to an AssetEntry. Tree provided by the AssetBrowser invalid?"); return Qt::PartiallyChecked; } - return sourceIndex.data(role); // return entry->data(index.column()); - //return QVariant::fromValue(entry); - //AZ_UNUSED(role); - //if (index.isValid()) - //{ - // ////if (role == AssetBrowserModel::EntryRole) - // //{ - // QModelIndex modelIndex = mapFromSource(index); - // auto assetEntry = static_cast(index.internalPointer()); - // return QVariant::fromValue(assetEntry); - //} - //return QVariant(); // AzToolsFramework::AssetBrowser::AssetBrowserModel::data(index, role); - + return sourceIndex.data(role); } - int AssetBrowserTableModel::rowCount(const QModelIndex& parent) const { return !parent.isValid() ? m_rowMap.size() : 0; } + QVariant AssetBrowserTableModel::headerData(int section, Qt::Orientation orientation, int role) const + { + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) + { + switch (section) + { + case static_cast(AssetBrowserEntry::Column::Name): + return QString("Name"); + case static_cast(AssetBrowserEntry::Column::Path): + return QString("Path"); + default: + return QString::number(section); + } + } + return QSortFilterProxyModel::headerData(section, orientation, role); // QVariant(); + } + int AssetBrowserTableModel::BuildMap(const QAbstractItemModel* model, const QModelIndex& parent, int row) { - //int rows = model ? model->rowCount(parent) : 0; - //for (int i = 0; i < rows; ++i) - //{ - // auto index = model->index(i, 0, parent); - // //if (!model->hasChildren(index)) - // //{ - // beginInsertRows(parent, row, row); - // m_rowMap[index] = row; - // m_indexMap[row] = index; - // endInsertRows(); - // Q_EMIT dataChanged(parent, parent); - // row = row + 1; - // //} - // if (model->hasChildren(index)) - // { - // row = BuildMap(model, index, row); - // } - //} - //return row; int rows = model ? model->rowCount(parent) : 0; for (int i = 0; i < rows; ++i) { - auto index = model->index(i, 0, parent); + QModelIndex index = model->index(i, 0, parent); if (model->hasChildren(index) == false) { beginInsertRows(parent, row, row); m_rowMap[index] = row; m_indexMap[row] = index; endInsertRows(); - Q_EMIT dataChanged(parent, parent); + + Q_EMIT dataChanged(index, index); row = row + 1; } @@ -173,12 +142,12 @@ namespace AzToolsFramework } void AssetBrowserTableModel::UpdateMap() { + //Not properly clears the indexes. //m_indexMap.clear(); //m_rowMap.clear(); - + emit layoutAboutToBeChanged(); if (m_indexMap.size() > 0) { - //beginRemoveRows(m_indexMap.first().parent(), m_indexMap.first().row(), m_indexMap.last().row()); for (const auto& key : m_indexMap.keys()) { beginRemoveRows(m_indexMap[key], m_indexMap[key].row(), m_indexMap[key].row()); @@ -186,160 +155,11 @@ namespace AzToolsFramework m_indexMap.remove(key); endRemoveRows(); } - //endRemoveRows(); } BuildMap(sourceModel()); + sort(0); } - - //---------------------------------------AssetBrowserTableFilterModel-------------------------------------------- - //AssetBrowserTableFilterModel::AssetBrowserTableFilterModel(QObject* parent) - // : QSortFilterProxyModel(parent) - //{ - // m_showColumn.insert(static_cast(AssetBrowserEntry::Column::DisplayName)); - // m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); - // AssetBrowserComponentNotificationBus::Handler::BusConnect(); - //} - - //AssetBrowserTableFilterModel::~AssetBrowserTableFilterModel() - //{ - // AssetBrowserComponentNotificationBus::Handler::BusDisconnect(); - //} - - //void AssetBrowserTableFilterModel::setSourceModel(QAbstractItemModel* sourceModel) - //{ - // QSortFilterProxyModel::setSourceModel(sourceModel); - //} - - //void AssetBrowserTableFilterModel::SetFilter(FilterConstType filter) - //{ - // connect(filter.data(), &AssetBrowserEntryFilter::updatedSignal, this, &AssetBrowserTableFilterModel::filterUpdatedSlot); - // m_filter = filter; - // m_invalidateFilter = true; - // // asset browser entries are not guaranteed to have populated when the filter is set, delay filtering until they are - // bool isAssetBrowserComponentReady = false; - // AssetBrowserComponentRequestBus::BroadcastResult(isAssetBrowserComponentReady, &AssetBrowserComponentRequests::AreEntriesReady); - // if (isAssetBrowserComponentReady) - // { - // OnAssetBrowserComponentReady(); - // } - //} - - //void AssetBrowserTableFilterModel::FilterUpdatedSlotImmediate() - //{ - // auto compFilter = qobject_cast>(m_filter); - // if (compFilter) - // { - // auto& subFilters = compFilter->GetSubFilters(); - // auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool { - // auto assetTypeFilter = qobject_cast>(filter); - // return !assetTypeFilter.isNull(); - // }); - // if (it != subFilters.end()) - // { - // m_assetTypeFilter = qobject_cast>(*it); - // } - // it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool { - // auto stringFilter = qobject_cast>(filter); - // return !stringFilter.isNull(); - // }); - // if (it != subFilters.end()) - // { - // m_stringFilter = qobject_cast>(*it); - // } - // } - // invalidateFilter(); - // Q_EMIT filterChanged(); - //} - - //void AssetBrowserTableFilterModel::OnAssetBrowserComponentReady() - //{ - // if (m_invalidateFilter) - // { - // invalidateFilter(); - // m_invalidateFilter = false; - // } - // Q_EMIT entriesUpdated(); - //} - - //bool AssetBrowserTableFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const - //{ - // AZ_UNUSED(source_row); - // AZ_UNUSED(source_parent); - // QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); - // if (!idx.isValid()) - // { - // return false; - // } - // // no filter present, every entry is visible - // if (!m_filter) - // { - // return true; - // } - - // //// the entry is the internal pointer of the index - // //auto entry = static_cast(idx.internalPointer()); - - // //if (entry) - // //{ - // // // root should return true even if its not displayed in the treeview - // // if (entry && entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Root) - // // { - // // return true; - // // } - // // return m_filter->Match(entry); - // //} - // return true; - //} - - //bool AssetBrowserTableFilterModel::filterAcceptsColumn(int source_column, const QModelIndex&) const - //{ - // return m_showColumn.find(source_column) != m_showColumn.end(); - //} - - //bool AssetBrowserTableFilterModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const - //{ - // if (source_left.column() == source_right.column()) - // { - // QVariant leftData = sourceModel()->data(source_left, AssetBrowserModel::Roles::EntryRole); - // QVariant rightData = sourceModel()->data(source_right, AssetBrowserModel::Roles::EntryRole); - // if (leftData.canConvert() && rightData.canConvert()) - // { - // auto leftEntry = qvariant_cast(leftData); - // auto rightEntry = qvariant_cast(rightData); - - // // folders should always come first - // if (azrtti_istypeof(leftEntry) && - // azrtti_istypeof(rightEntry)) - // { - // return false; - // } - // if (azrtti_istypeof(leftEntry) && - // azrtti_istypeof(rightEntry)) - // { - // return true; - // } - - // // if both entries are of same type, sort alphabetically - // return m_collator.compare(leftEntry->GetDisplayName(), rightEntry->GetDisplayName()) > 0; - // } - // } - // return QSortFilterProxyModel::lessThan(source_left, source_right); - //} - - //void AssetBrowserTableFilterModel::filterUpdatedSlot() - //{ - // if (!m_alreadyRecomputingFilters) - // { - // m_alreadyRecomputingFilters = true; - // // de-bounce it, since we may get many filter updates all at once. - // QTimer::singleShot(0, this, [this]() { - // m_alreadyRecomputingFilters = false; - // FilterUpdatedSlotImmediate(); - // }); - // } - //} - } // namespace AssetBrowser } // namespace AzToolsFramework #include "AssetBrowser/moc_AssetBrowserTableModel.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index 34cf8432ff..69b8ead7c5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -1,9 +1,6 @@ #pragma once #if !defined(Q_MOC_RUN) -#include #include -#include - #include #include #include @@ -26,21 +23,15 @@ namespace AzToolsFramework class AssetBrowserTableModel : public QSortFilterProxyModel - , public AssetBrowserComponentNotificationBus::Handler { Q_OBJECT public: AZ_CLASS_ALLOCATOR(AssetBrowserTableModel, AZ::SystemAllocator, 0); explicit AssetBrowserTableModel(QObject* parent = nullptr); - ~AssetBrowserTableModel(); - //////////////////////////////////////////////////////////////////// - // AssetBrowserComponentNotificationBus - //////////////////////////////////////////////////////////////////// - void OnAssetBrowserComponentReady() override; - void setSourceModel(QAbstractItemModel* sourceModel) override; //////////////////////////////////////////////////////////////////// // QSortFilterProxyModel + void setSourceModel(QAbstractItemModel* sourceModel) override; QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override; bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; @@ -52,7 +43,7 @@ namespace AzToolsFramework void UpdateMap(); protected: int rowCount(const QModelIndex& parent = QModelIndex()) const override; - //QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override; //////////////////////////////////////////////////////////////////// private: @@ -63,52 +54,5 @@ namespace AzToolsFramework QMap m_indexMap; QMap m_rowMap; }; - - //class AssetBrowserTableFilterModel - // : public QSortFilterProxyModel - // , public AssetBrowserComponentNotificationBus::Handler - //{ - // Q_OBJECT - //public: - // explicit AssetBrowserTableFilterModel(QObject* parent = nullptr); - // ~AssetBrowserTableFilterModel(); - - // void setSourceModel(QAbstractItemModel* sourceModel) override; - // // asset type filtering - // void SetFilter(FilterConstType filter); - // void FilterUpdatedSlotImmediate(); - - // ////////////////////////////////////////////////////////////////////////// - // // AssetBrowserComponentNotificationBus - // ////////////////////////////////////////////////////////////////////////// - // void OnAssetBrowserComponentReady() override; - - //Q_SIGNALS: - // void filterChanged(); - // void entriesUpdated(); - - // ////////////////////////////////////////////////////////////////////////// - // // QSortFilterProxyModel - //protected: - // bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; - // bool filterAcceptsColumn(int source_column, const QModelIndex& /*source_parent*/) const override; - // bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override; - // ////////////////////////////////////////////////////////////////////////// - - //public Q_SLOTS: - // void filterUpdatedSlot(); - - //private: - // AZStd::fixed_unordered_set(AssetBrowserEntry::Column::Count)> m_showColumn; - // bool m_alreadyRecomputingFilters = false; - // // asset source name match filter - // FilterConstType m_filter; - // AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...' - // QWeakPointer m_stringFilter; - // QWeakPointer m_assetTypeFilter; - // QCollator m_collator; // cache the collator as its somewhat expensive to constantly create and destroy one. - // AZ_POP_DISABLE_WARNING - // bool m_invalidateFilter = false; - //}; } // namespace AssetBrowser } // namespace AzToolsFramework diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index dd4b3b0cad..554c62a251 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -68,7 +68,6 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) , m_ui(new Ui::AzAssetBrowserWindowClass()) , m_filterModel(new AzToolsFramework::AssetBrowser::AssetBrowserFilterModel(parent)) , m_tableModel(new AzToolsFramework::AssetBrowser::AssetBrowserTableModel(parent)) - /*, m_tableFilterModel(new AzToolsFramework::AssetBrowser::AssetBrowserTableFilterModel(parent))*/ { m_ui->setupUi(this); m_ui->m_searchWidget->Setup(true, true); @@ -81,21 +80,13 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_tableModel->setFilterRole(Qt::DisplayRole); m_tableModel->setSourceModel(m_filterModel.data()); - //m_tableModel->setSourceModel(m_assetBrowserModel); - - //m_tableFilterModel->setSourceModel(m_tableModel.data()); - //m_tableFilterModel->SetFilter(m_ui->m_searchWidget->GetFilter()); - m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); m_ui->m_assetBrowserTreeViewWidget->hideColumn(static_cast(AssetBrowserEntry::Column::Path)); - //m_ui->m_assetBrowserTableViewWidget->setModel(m_tableFilterModel.data()); m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data()); m_ui->m_assetBrowserTableViewWidget->setVisible(false); - //connect(m_filterModel.data(), &AssetBrowserFilterModel::entriesUpdated, m_tableModel.data(), &AssetBrowserTableModel::UpdateMap); - connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), &AssetBrowserFilterModel::filterUpdatedSlot); connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, this, [this]() @@ -105,20 +96,15 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); }); - //connect( m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_tableFilterModel.data(), - // &AssetBrowserTableFilterModel::filterUpdatedSlot); - //connect(m_tableFilterModel.data(), &AssetBrowserTableFilterModel::filterChanged, this, [this]() { - // const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty(); - // const bool selectFirstFilteredIndex = false; - // m_ui->m_assetBrowserTableViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); - //}); - connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, m_tableModel.data(), &AssetBrowserTableModel::UpdateMap); connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, this, &AzAssetBrowserWindow::SelectionChangedSlot); connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); + connect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); + + connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, &SearchWidget::ClearStringFilter); connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter); From 80684b383b66b762dbc3e4537afb536c033a2caa Mon Sep 17 00:00:00 2001 From: igarri Date: Wed, 12 May 2021 11:42:39 +0100 Subject: [PATCH 019/233] Code cleanup --- .../AssetBrowser/AssetBrowserTableModel.cpp | 97 ++++++++----------- .../AssetBrowser/AssetBrowserTableModel.h | 22 ++--- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 2 +- 3 files changed, 49 insertions(+), 72 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index ce9e97520a..724e7a54d9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -1,14 +1,8 @@ -#include -#include -#include -#include -AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") #include #include +#include -#include -AZ_POP_DISABLE_WARNING namespace AzToolsFramework { namespace AssetBrowser @@ -34,11 +28,6 @@ namespace AzToolsFramework } return m_indexMap[proxyIndex.row()]; } - QModelIndex AssetBrowserTableModel::parent(const QModelIndex& child) const - { - AZ_UNUSED(child); - return QModelIndex(); - } QModelIndex AssetBrowserTableModel::mapFromSource(const QModelIndex& sourceIndex) const { Q_ASSERT(!sourceIndex.isValid() || sourceIndex.model() == sourceModel()); @@ -49,21 +38,21 @@ namespace AzToolsFramework return createIndex(m_rowMap[sourceIndex], sourceIndex.column(), sourceIndex.internalPointer()); } - bool AssetBrowserTableModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const + QVariant AssetBrowserTableModel::headerData(int section, Qt::Orientation orientation, int role) const { - AZ_UNUSED(source_row); - AZ_UNUSED(source_parent); - // no filter present, every entry is not visible - if (!m_filterModel->GetFilter()) + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { - return true; + switch (section) + { + case static_cast(AssetBrowserEntry::Column::Name): + return QString("Name"); + case static_cast(AssetBrowserEntry::Column::Path): + return QString("Path"); + default: + return QString::number(section); + } } - return true; - } - - QModelIndex AssetBrowserTableModel::index(int row, int column, const QModelIndex& parent) const - { - return parent.isValid() ? QModelIndex() : createIndex(row, column, m_indexMap[row].internalPointer()); + return QSortFilterProxyModel::headerData(section, orientation, role); // QVariant(); } QVariant AssetBrowserTableModel::data(const QModelIndex& index, int role) const @@ -82,29 +71,34 @@ namespace AzToolsFramework return sourceIndex.data(role); } - int AssetBrowserTableModel::rowCount(const QModelIndex& parent) const + QModelIndex AssetBrowserTableModel::index(int row, int column, const QModelIndex& parent) const { - return !parent.isValid() ? m_rowMap.size() : 0; + return parent.isValid() ? QModelIndex() : createIndex(row, column, m_indexMap[row].internalPointer()); } - QVariant AssetBrowserTableModel::headerData(int section, Qt::Orientation orientation, int role) const + QModelIndex AssetBrowserTableModel::parent(const QModelIndex& child) const { - if (role == Qt::DisplayRole && orientation == Qt::Horizontal) + AZ_UNUSED(child); + return QModelIndex(); + } + bool AssetBrowserTableModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const + { + AZ_UNUSED(source_row); + AZ_UNUSED(source_parent); + // no filter present, every entry is not visible + if (!m_filterModel->GetFilter()) { - switch (section) - { - case static_cast(AssetBrowserEntry::Column::Name): - return QString("Name"); - case static_cast(AssetBrowserEntry::Column::Path): - return QString("Path"); - default: - return QString::number(section); - } + return true; } - return QSortFilterProxyModel::headerData(section, orientation, role); // QVariant(); + return true; + } + + int AssetBrowserTableModel::rowCount(const QModelIndex& parent) const + { + return !parent.isValid() ? m_rowMap.size() : 0; } - int AssetBrowserTableModel::BuildMap(const QAbstractItemModel* model, const QModelIndex& parent, int row) + int AssetBrowserTableModel::BuildTableModelMap(const QAbstractItemModel* model, const QModelIndex& parent /*= QModelIndex()*/, int row /*= 0*/) { int rows = model ? model->rowCount(parent) : 0; for (int i = 0; i < rows; ++i) @@ -123,7 +117,7 @@ namespace AzToolsFramework if (model->hasChildren(index)) { - row = BuildMap(model, index, row); + row = BuildTableModelMap(model, index, row); } } return row; @@ -140,25 +134,18 @@ namespace AzToolsFramework return nullptr; } } - void AssetBrowserTableModel::UpdateMap() - { - //Not properly clears the indexes. - //m_indexMap.clear(); - //m_rowMap.clear(); + void AssetBrowserTableModel::UpdateTableModelMaps() +{ emit layoutAboutToBeChanged(); if (m_indexMap.size() > 0) { - for (const auto& key : m_indexMap.keys()) - { - beginRemoveRows(m_indexMap[key], m_indexMap[key].row(), m_indexMap[key].row()); - m_rowMap.remove(m_indexMap[key]); - m_indexMap.remove(key); - endRemoveRows(); - } + beginRemoveRows(m_indexMap.first(), m_indexMap.first().row(), m_indexMap.last().row()); + m_rowMap.clear(); + m_indexMap.clear(); + endRemoveRows(); } - - BuildMap(sourceModel()); - sort(0); + BuildTableModelMap(sourceModel()); + emit layoutChanged(); } } // namespace AssetBrowser } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index 69b8ead7c5..51976c3482 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -1,25 +1,16 @@ #pragma once #if !defined(Q_MOC_RUN) -#include #include -#include -#include -//#include -#include - -AZ_PUSH_DISABLE_WARNING( - 4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...' -#include -#include #include +#include #endif -AZ_POP_DISABLE_WARNING + namespace AzToolsFramework { namespace AssetBrowser { class AssetBrowserFilterModel; - + class AssetBrowserEntry; class AssetBrowserTableModel : public QSortFilterProxyModel @@ -34,21 +25,20 @@ namespace AzToolsFramework void setSourceModel(QAbstractItemModel* sourceModel) override; QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override; - bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; QModelIndex parent(const QModelIndex& child) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - public Q_SLOTS: - void UpdateMap(); + void UpdateTableModelMaps(); protected: + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override; //////////////////////////////////////////////////////////////////// private: AssetBrowserEntry* GetAssetEntry(QModelIndex index) const; - int BuildMap(const QAbstractItemModel* model, const QModelIndex& parent = QModelIndex(), int row = 0); + int BuildTableModelMap(const QAbstractItemModel* model, const QModelIndex& parent = QModelIndex(), int row = 0); private: QPointer m_filterModel; QMap m_indexMap; diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 554c62a251..f415e0a135 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -96,7 +96,7 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); }); - connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, m_tableModel.data(), &AssetBrowserTableModel::UpdateMap); + connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, m_tableModel.data(), &AssetBrowserTableModel::UpdateTableModelMaps); connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, this, &AzAssetBrowserWindow::SelectionChangedSlot); From c1e21185b43e7892973dc1055ae19d1b9099b5e1 Mon Sep 17 00:00:00 2001 From: igarri Date: Wed, 12 May 2021 12:20:23 +0100 Subject: [PATCH 020/233] Selecting correct indexes from Asset Browser Model --- .../Views/AssetBrowserTableView.cpp | 22 +++++------ .../Views/AssetBrowserTableView.h | 7 +--- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 39 ++++++++++++++++++- .../AzAssetBrowser/AzAssetBrowserWindow.h | 2 +- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp index aa35d99894..0a6aeed374 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -47,7 +47,7 @@ namespace AzToolsFramework setMouseTracking(true); connect(this, &QTableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu); - connect(m_scTimer, &QTimer::timeout, this, &AssetBrowserTableView::OnUpdateSCThumbnailsList); + //connect(m_scTimer, &QTimer::timeout, this, &AssetBrowserTableView::OnUpdateSCThumbnailsList); AssetBrowserViewRequestBus::Handler::BusConnect(); AssetBrowserComponentNotificationBus::Handler::BusConnect(); @@ -59,9 +59,9 @@ namespace AzToolsFramework } void AssetBrowserTableView::setModel(QAbstractItemModel* model) { - m_filterModel = qobject_cast(model); - AZ_Assert(m_filterModel, "Expecting AssetBrowserTableModel"); - m_sourceModel = qobject_cast(m_filterModel->sourceModel()); + m_tableModel = qobject_cast(model); + AZ_Assert(m_tableModel, "Expecting AssetBrowserTableModel"); + m_sourceFilterModel = qobject_cast(m_tableModel->sourceModel()); QTableView::setModel(model); } void AssetBrowserTableView::SetName(const QString& name) @@ -76,14 +76,14 @@ namespace AzToolsFramework } AZStd::vector AssetBrowserTableView::GetSelectedAssets() const { - QModelIndexList sourceIndexes{}; - //for (const auto& index : selectedIndexes()) - //{ - // sourceIndexes.push_back(m_sourceModel->mapToSource(index)); - //} + QModelIndexList sourceIndexes; + for (const auto& index : selectedIndexes()) + { + sourceIndexes.push_back(m_sourceFilterModel->mapToSource(m_tableModel->mapToSource(index))); + } AZStd::vector entries; - //AssetBrowserModel::SourceIndexesToAssetDatabaseEntries(sourceIndexes, entries); + AssetBrowserModel::SourceIndexesToAssetDatabaseEntries(sourceIndexes, entries); return entries; } void AssetBrowserTableView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) @@ -124,7 +124,7 @@ namespace AzToolsFramework { emit ClearStringFilter(); emit ClearTypeFilter(); - m_sourceModel->FilterUpdatedSlotImmediate(); + m_sourceFilterModel->FilterUpdatedSlotImmediate(); } void AssetBrowserTableView::Update() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h index aef7353334..d1bab70104 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -63,13 +63,10 @@ namespace AzToolsFramework private: QString m_name; - QPointer m_filterModel = nullptr; - QPointer m_sourceModel = nullptr; + QPointer m_tableModel = nullptr; + QPointer m_sourceFilterModel = nullptr; EntryDelegate* m_delegate = nullptr; - QTimer* m_scTimer = nullptr; - const int m_scUpdateInterval = 100; - private Q_SLOTS: void OnContextMenu(const QPoint& point); //! Get all visible source entries and place them in a queue to update their source control status diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index f415e0a135..b59190d9a9 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -102,7 +102,7 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) this, &AzAssetBrowserWindow::SelectionChangedSlot); connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); - connect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); + connect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItemTableModel); connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, &SearchWidget::ClearStringFilter); @@ -242,6 +242,43 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& } +void AzAssetBrowserWindow::DoubleClickedItemTableModel([[maybe_unused]] const QModelIndex& element) +{ + using namespace AzToolsFramework; + using namespace AzToolsFramework::AssetBrowser; + // assumption: Double clicking an item selects it before telling us we double clicked it. + auto selectedAssets = m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets(); + for (const AssetBrowserEntry* entry : selectedAssets) + { + AZ::Data::AssetId assetIdToOpen; + AZStd::string fullFilePath; + + if (const ProductAssetBrowserEntry* productEntry = azrtti_cast(entry)) + { + assetIdToOpen = productEntry->GetAssetId(); + fullFilePath = entry->GetFullPath(); + } + else if (const SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry)) + { + // manufacture an empty AssetID with the source's UUID + assetIdToOpen = AZ::Data::AssetId(sourceEntry->GetSourceUuid(), 0); + fullFilePath = entry->GetFullPath(); + } + + bool handledBySomeone = false; + if (assetIdToOpen.IsValid()) + { + AssetBrowserInteractionNotificationBus::Broadcast( + &AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); + } + + if (!handledBySomeone && !fullFilePath.empty()) + { + AzAssetBrowserRequestHandler::OpenWithOS(fullFilePath); + } + } +} + void AzAssetBrowserWindow::SwitchDisplayView(const int state) { m_ui->m_assetBrowserTableViewWidget->setVisible(state); diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h index 801af3e2a8..86d81ad873 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h @@ -56,7 +56,6 @@ private: QScopedPointer m_ui; QScopedPointer m_filterModel; QScopedPointer m_tableModel; - //QScopedPointer m_tableFilterModel; AzToolsFramework::AssetBrowser::AssetBrowserModel* m_assetBrowserModel; void UpdatePreview() const; @@ -64,6 +63,7 @@ private: private Q_SLOTS: void SelectionChangedSlot(const QItemSelection& selected, const QItemSelection& deselected) const; void DoubleClickedItem(const QModelIndex& element); + void DoubleClickedItemTableModel(const QModelIndex& element); void SwitchDisplayView(const int state); }; From d6868df735033804d51c52949bfcc87d1d6cbeb6 Mon Sep 17 00:00:00 2001 From: igarri Date: Thu, 13 May 2021 12:29:16 +0100 Subject: [PATCH 021/233] Expanding TableView Columns --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 2 +- .../AssetBrowser/AssetBrowserTableModel.cpp | 1 - .../AssetBrowser/Views/AssetBrowserTableView.cpp | 15 ++++----------- .../AssetBrowser/Views/AssetBrowserTableView.h | 5 ----- 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index 34f8b97b5a..ee3aa04e27 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -32,7 +32,7 @@ namespace AzToolsFramework : QSortFilterProxyModel(parent) { m_showColumn.insert(static_cast(AssetBrowserEntry::Column::DisplayName)); - //m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); + m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); m_collator.setNumericMode(true); AssetBrowserComponentNotificationBus::Handler::BusConnect(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index 724e7a54d9..13b298cdec 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -1,4 +1,3 @@ - #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp index 0a6aeed374..40e0b9db5e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -1,7 +1,5 @@ #include -#include -#include #include #include @@ -14,8 +12,6 @@ #include #include #include -#include -#include AZ_PUSH_DISABLE_WARNING( 4244 4251 4800, "-Wunknown-warning-option") // conversion from 'int' to 'float', possible loss of data, needs to have dll-interface to @@ -37,17 +33,16 @@ namespace AzToolsFramework AssetBrowserTableView::AssetBrowserTableView(QWidget* parent) : QTableView(parent) , m_delegate(new EntryDelegate(this)) - { setSortingEnabled(true); setItemDelegate(m_delegate); - //header()->hide(); + verticalHeader()->hide(); setContextMenuPolicy(Qt::CustomContextMenu); setMouseTracking(true); + setSortingEnabled(false); connect(this, &QTableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu); - //connect(m_scTimer, &QTimer::timeout, this, &AssetBrowserTableView::OnUpdateSCThumbnailsList); AssetBrowserViewRequestBus::Handler::BusConnect(); AssetBrowserComponentNotificationBus::Handler::BusConnect(); @@ -63,6 +58,8 @@ namespace AzToolsFramework AZ_Assert(m_tableModel, "Expecting AssetBrowserTableModel"); m_sourceFilterModel = qobject_cast(m_tableModel->sourceModel()); QTableView::setModel(model); + horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch); + horizontalHeader()->setSectionResizeMode(1,QHeaderView::ResizeMode::Stretch); } void AssetBrowserTableView::SetName(const QString& name) { @@ -105,10 +102,6 @@ namespace AzToolsFramework } } QTableView::rowsAboutToBeRemoved(parent, start, end); - } - void AssetBrowserTableView::OnUpdateSCThumbnailsList() - { - } void AssetBrowserTableView::SelectProduct(AZ::Data::AssetId assetID) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h index d1bab70104..531e962297 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -58,9 +58,6 @@ namespace AzToolsFramework void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override; void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override; - //! Get all visible source entries and place them in a queue to update their source control status - //void OnUpdateSCThumbnailsList(); - private: QString m_name; QPointer m_tableModel = nullptr; @@ -69,8 +66,6 @@ namespace AzToolsFramework private Q_SLOTS: void OnContextMenu(const QPoint& point); - //! Get all visible source entries and place them in a queue to update their source control status - void OnUpdateSCThumbnailsList(); }; } // namespace AssetBrowser } // namespace AzToolsFramework From d7ca3f273bb656f0b162eeeede07ba87cafca22f Mon Sep 17 00:00:00 2001 From: igarri Date: Thu, 13 May 2021 13:25:03 +0100 Subject: [PATCH 022/233] croll to top on view when the filter updates --- .../AssetBrowser/AssetBrowserTableModel.cpp | 1 + .../AssetBrowser/Views/AssetBrowserTableView.cpp | 9 +++++++++ .../AssetBrowser/Views/AssetBrowserTableView.h | 1 + 3 files changed, 11 insertions(+) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index 13b298cdec..7991eb35d8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -145,6 +145,7 @@ namespace AzToolsFramework } BuildTableModelMap(sourceModel()); emit layoutChanged(); + } } // namespace AssetBrowser } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp index 40e0b9db5e..83a0f2ce9f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -58,6 +58,8 @@ namespace AzToolsFramework AZ_Assert(m_tableModel, "Expecting AssetBrowserTableModel"); m_sourceFilterModel = qobject_cast(m_tableModel->sourceModel()); QTableView::setModel(model); + connect(m_tableModel, &AssetBrowserTableModel::layoutChanged, this, &AssetBrowserTableView::layoutChangedSlot); + horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch); horizontalHeader()->setSectionResizeMode(1,QHeaderView::ResizeMode::Stretch); } @@ -103,6 +105,13 @@ namespace AzToolsFramework } QTableView::rowsAboutToBeRemoved(parent, start, end); } + void AssetBrowserTableView::layoutChangedSlot(const QList& parents, QAbstractItemModel::LayoutChangeHint hint) + { + AZ_UNUSED(parents); + AZ_UNUSED(hint); + + scrollToTop(); + } void AssetBrowserTableView::SelectProduct(AZ::Data::AssetId assetID) { AZ_UNUSED(assetID); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h index 531e962297..b39d48c391 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -57,6 +57,7 @@ namespace AzToolsFramework protected Q_SLOTS: void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override; void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override; + void layoutChangedSlot(const QList &parents = QList(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint); private: QString m_name; From 33240c9f90fad3cf4c4e30ef1fcda909a0acad7b Mon Sep 17 00:00:00 2001 From: igarri Date: Thu, 13 May 2021 16:08:14 +0100 Subject: [PATCH 023/233] Selecting assets from tableview --- .../AssetBrowser/AssetBrowserTableModel.cpp | 13 +------------ .../AssetBrowser/AssetBrowserTableModel.h | 2 -- .../AssetBrowser/Views/AssetBrowserTableView.cpp | 12 ++++++++---- .../AssetBrowser/Views/AssetBrowserTreeView.cpp | 5 ++++- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index 7991eb35d8..bebdf44c9a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -27,15 +27,6 @@ namespace AzToolsFramework } return m_indexMap[proxyIndex.row()]; } - QModelIndex AssetBrowserTableModel::mapFromSource(const QModelIndex& sourceIndex) const - { - Q_ASSERT(!sourceIndex.isValid() || sourceIndex.model() == sourceModel()); - if (!sourceIndex.isValid()) - { - return QModelIndex(); - } - return createIndex(m_rowMap[sourceIndex], sourceIndex.column(), sourceIndex.internalPointer()); - } QVariant AssetBrowserTableModel::headerData(int section, Qt::Orientation orientation, int role) const { @@ -94,7 +85,7 @@ namespace AzToolsFramework int AssetBrowserTableModel::rowCount(const QModelIndex& parent) const { - return !parent.isValid() ? m_rowMap.size() : 0; + return !parent.isValid() ? m_indexMap.size() : 0; } int AssetBrowserTableModel::BuildTableModelMap(const QAbstractItemModel* model, const QModelIndex& parent /*= QModelIndex()*/, int row /*= 0*/) @@ -106,7 +97,6 @@ namespace AzToolsFramework if (model->hasChildren(index) == false) { beginInsertRows(parent, row, row); - m_rowMap[index] = row; m_indexMap[row] = index; endInsertRows(); @@ -139,7 +129,6 @@ namespace AzToolsFramework if (m_indexMap.size() > 0) { beginRemoveRows(m_indexMap.first(), m_indexMap.first().row(), m_indexMap.last().row()); - m_rowMap.clear(); m_indexMap.clear(); endRemoveRows(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index 51976c3482..432300194b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -24,7 +24,6 @@ namespace AzToolsFramework // QSortFilterProxyModel void setSourceModel(QAbstractItemModel* sourceModel) override; QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; - QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override; QModelIndex parent(const QModelIndex& child) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; @@ -42,7 +41,6 @@ namespace AzToolsFramework private: QPointer m_filterModel; QMap m_indexMap; - QMap m_rowMap; }; } // namespace AssetBrowser } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp index 83a0f2ce9f..cb4a989043 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -41,6 +41,7 @@ namespace AzToolsFramework setMouseTracking(true); setSortingEnabled(false); + setSelectionMode(QAbstractItemView::SingleSelection); connect(this, &QTableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu); @@ -61,7 +62,7 @@ namespace AzToolsFramework connect(m_tableModel, &AssetBrowserTableModel::layoutChanged, this, &AssetBrowserTableView::layoutChangedSlot); horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch); - horizontalHeader()->setSectionResizeMode(1,QHeaderView::ResizeMode::Stretch); + horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch); } void AssetBrowserTableView::SetName(const QString& name) { @@ -78,7 +79,10 @@ namespace AzToolsFramework QModelIndexList sourceIndexes; for (const auto& index : selectedIndexes()) { - sourceIndexes.push_back(m_sourceFilterModel->mapToSource(m_tableModel->mapToSource(index))); + if (index.column() == 0) + { + sourceIndexes.push_back(m_sourceFilterModel->mapToSource(m_tableModel->mapToSource(index))); + } } AZStd::vector entries; @@ -87,8 +91,8 @@ namespace AzToolsFramework } void AssetBrowserTableView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { - AZ_UNUSED(selected); - AZ_UNUSED(deselected); + QTableView::selectionChanged(selected, deselected); + Q_EMIT selectionChangedSignal(selected, deselected); } void AssetBrowserTableView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp index eeee433835..f2a5cc1a3a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp @@ -101,7 +101,10 @@ namespace AzToolsFramework QModelIndexList sourceIndexes; for (const auto& index : selectedIndexes()) { - sourceIndexes.push_back(m_assetBrowserSortFilterProxyModel->mapToSource(index)); + if (index.column() == 0) + { + sourceIndexes.push_back(m_assetBrowserSortFilterProxyModel->mapToSource(index)); + } } AZStd::vector entries; From cff9fea535c91a08ce4d444c2a79823d53f2f75c Mon Sep 17 00:00:00 2001 From: igarri Date: Fri, 14 May 2021 13:09:14 +0100 Subject: [PATCH 024/233] Switching views with filters --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 6 ++++++ .../AssetBrowser/AssetBrowserFilterModel.h | 2 +- .../AzToolsFramework/AssetBrowser/Search/Filter.cpp | 5 +++++ .../AzToolsFramework/AssetBrowser/Search/Filter.h | 2 +- .../AssetBrowser/Views/AssetBrowserTreeView.cpp | 2 ++ .../Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp | 9 +++++++-- .../Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h | 2 +- 7 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index ee3aa04e27..b4728633d3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -130,6 +130,9 @@ namespace AzToolsFramework if (compFilter) { auto& subFilters = compFilter->GetSubFilters(); + //bool bNoFilters = false; + + auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool { auto assetTypeFilter = qobject_cast >(filter); @@ -147,8 +150,11 @@ namespace AzToolsFramework if (it != subFilters.end()) { m_stringFilter = qobject_cast >(*it); + emit switchFilterView(m_stringFilter.toStrongRef()->IsEmpty()); } } + + invalidateFilter(); Q_EMIT filterChanged(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h index 6cccc53eb6..71689354d1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h @@ -52,8 +52,8 @@ namespace AzToolsFramework void OnAssetBrowserComponentReady() override; Q_SIGNALS: + void switchFilterView(int); void filterChanged(); - ////////////////////////////////////////////////////////////////////////// //QSortFilterProxyModel protected: diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.cpp index ceda5f8e19..86315613ad 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.cpp @@ -233,6 +233,11 @@ namespace AzToolsFramework Q_EMIT updatedSignal(); } + bool StringFilter::IsEmpty() const + { + return m_filterString.isEmpty(); + } + QString StringFilter::GetNameInternal() const { return m_filterString; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.h index b67b699862..6d19fb3b47 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.h @@ -110,7 +110,7 @@ namespace AzToolsFramework ~StringFilter() override = default; void SetFilterString(const QString& filterString); - + bool IsEmpty() const; protected: QString GetNameInternal() const override; bool MatchInternal(const AssetBrowserEntry* entry) const override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp index f2a5cc1a3a..e29607698d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp @@ -52,6 +52,7 @@ namespace AzToolsFramework setSortingEnabled(true); setItemDelegate(m_delegate); header()->hide(); + setContextMenuPolicy(Qt::CustomContextMenu); setMouseTracking(true); @@ -174,6 +175,7 @@ namespace AzToolsFramework void AssetBrowserTreeView::OnAssetBrowserComponentReady() { + hideColumn(static_cast(AssetBrowserEntry::Column::Path)); if (!m_name.isEmpty()) { auto crc = AZ::Crc32(m_name.toUtf8().data()); diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index b59190d9a9..d1bbbb1f9d 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -82,7 +82,7 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_tableModel->setSourceModel(m_filterModel.data()); m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); - m_ui->m_assetBrowserTreeViewWidget->hideColumn(static_cast(AssetBrowserEntry::Column::Path)); + //m_ui->m_assetBrowserTreeViewWidget->hideColumn(static_cast(AssetBrowserEntry::Column::Path)); m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data()); m_ui->m_assetBrowserTableViewWidget->setVisible(false); @@ -97,9 +97,14 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) }); connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, m_tableModel.data(), &AssetBrowserTableModel::UpdateTableModelMaps); + connect(m_filterModel.data(), &AssetBrowserFilterModel::switchFilterView, this, &AzAssetBrowserWindow::SwitchDisplayView); connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, this, &AzAssetBrowserWindow::SelectionChangedSlot); + + connect(m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::selectionChangedSignal, + this, &AzAssetBrowserWindow::SelectionChangedSlot); + connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); connect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItemTableModel); @@ -279,7 +284,7 @@ void AzAssetBrowserWindow::DoubleClickedItemTableModel([[maybe_unused]] const QM } } -void AzAssetBrowserWindow::SwitchDisplayView(const int state) +void AzAssetBrowserWindow::SwitchDisplayView(bool state) { m_ui->m_assetBrowserTableViewWidget->setVisible(state); m_ui->m_assetBrowserTreeViewWidget->setVisible(!state); diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h index 86d81ad873..8c32a913ba 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h @@ -64,7 +64,7 @@ private Q_SLOTS: void SelectionChangedSlot(const QItemSelection& selected, const QItemSelection& deselected) const; void DoubleClickedItem(const QModelIndex& element); void DoubleClickedItemTableModel(const QModelIndex& element); - void SwitchDisplayView(const int state); + void SwitchDisplayView(bool state); }; extern const char* AZ_ASSET_BROWSER_PREVIEW_NAME; From b9c9811d3566a008feb330ef79d82b501bcd2ac3 Mon Sep 17 00:00:00 2001 From: igarri Date: Mon, 17 May 2021 14:06:01 +0100 Subject: [PATCH 025/233] Fixing qobject_cast to the StringFilter --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 33 ++++++++++++++----- .../AssetBrowser/AssetBrowserFilterModel.h | 2 +- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 2 +- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index b4728633d3..9ee00a96ae 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -130,9 +130,7 @@ namespace AzToolsFramework if (compFilter) { auto& subFilters = compFilter->GetSubFilters(); - //bool bNoFilters = false; - auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool { auto assetTypeFilter = qobject_cast >(filter); @@ -142,21 +140,38 @@ namespace AzToolsFramework { m_assetTypeFilter = qobject_cast >(*it); } - it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool + + it = AZStd::find_if(subFilters.begin(), subFilters.end(), [](FilterConstType filter) -> bool { - auto stringFilter = qobject_cast >(filter); - return !stringFilter.isNull(); + auto stringCompositeFilter = qobject_cast >(filter); + bool isStringFilter = false; + if (stringCompositeFilter) + { + auto& subFilters = stringCompositeFilter->GetSubFilters(); + auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [](FilterConstType filt) -> bool + { + auto strFilter = qobject_cast>(filt); + return !strFilter.isNull(); + }); + if (it != subFilters.end()) + { + isStringFilter = true; + } + } + + return isStringFilter; }); if (it != subFilters.end()) { - m_stringFilter = qobject_cast >(*it); - emit switchFilterView(m_stringFilter.toStrongRef()->IsEmpty()); + auto compStringFilter = qobject_cast>(*it); + m_stringFilter = qobject_cast>(compStringFilter->GetSubFilters()[0]); } - } - + } invalidateFilter(); Q_EMIT filterChanged(); + emit stringFilterPopulated(!m_stringFilter.isNull()); + } void AssetBrowserFilterModel::filterUpdatedSlot() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h index 71689354d1..7a6faf9f18 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h @@ -52,7 +52,7 @@ namespace AzToolsFramework void OnAssetBrowserComponentReady() override; Q_SIGNALS: - void switchFilterView(int); + void stringFilterPopulated(bool); void filterChanged(); ////////////////////////////////////////////////////////////////////////// //QSortFilterProxyModel diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index d1bbbb1f9d..047d4d4dc2 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -97,7 +97,6 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) }); connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, m_tableModel.data(), &AssetBrowserTableModel::UpdateTableModelMaps); - connect(m_filterModel.data(), &AssetBrowserFilterModel::switchFilterView, this, &AzAssetBrowserWindow::SwitchDisplayView); connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, this, &AzAssetBrowserWindow::SelectionChangedSlot); @@ -119,6 +118,7 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main"); + connect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); connect(m_ui->m_viewSwitcherCheckBox, &QCheckBox::stateChanged, this, &AzAssetBrowserWindow::SwitchDisplayView); } From 10ca002ced59314ac7e4bc01365a2fb7f4682ebe Mon Sep 17 00:00:00 2001 From: igarri Date: Mon, 17 May 2021 14:25:43 +0100 Subject: [PATCH 026/233] Checkbox to select view --- .../Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp | 13 ++++++++++++- .../Editor/AzAssetBrowser/AzAssetBrowserWindow.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 047d4d4dc2..8c01f6841c 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -119,7 +119,7 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main"); connect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); - connect(m_ui->m_viewSwitcherCheckBox, &QCheckBox::stateChanged, this, &AzAssetBrowserWindow::SwitchDisplayView); + connect(m_ui->m_viewSwitcherCheckBox, &QCheckBox::stateChanged, this, &AzAssetBrowserWindow::LockToDefaultView); } AzAssetBrowserWindow::~AzAssetBrowserWindow() @@ -290,4 +290,15 @@ void AzAssetBrowserWindow::SwitchDisplayView(bool state) m_ui->m_assetBrowserTreeViewWidget->setVisible(!state); } +void AzAssetBrowserWindow::LockToDefaultView(bool state) +{ + using namespace AzToolsFramework; + using namespace AzToolsFramework::AssetBrowser; + SwitchDisplayView(!state); + if (state == true) + disconnect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); + else + connect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); +} + #include diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h index 8c32a913ba..1174335995 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h @@ -65,6 +65,7 @@ private Q_SLOTS: void DoubleClickedItem(const QModelIndex& element); void DoubleClickedItemTableModel(const QModelIndex& element); void SwitchDisplayView(bool state); + void LockToDefaultView(bool state); }; extern const char* AZ_ASSET_BROWSER_PREVIEW_NAME; From 61ac423ebdc0c9865a95710d94f7f21c1c9ce463 Mon Sep 17 00:00:00 2001 From: igarri Date: Mon, 17 May 2021 16:04:43 +0100 Subject: [PATCH 027/233] Deleted unused files --- .../AzToolsFramework/AssetBrowserTableModel.h | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 Code/Framework/AzToolsFramework/AssetBrowserTableModel.h diff --git a/Code/Framework/AzToolsFramework/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AssetBrowserTableModel.h deleted file mode 100644 index 4e65b3921c..0000000000 --- a/Code/Framework/AzToolsFramework/AssetBrowserTableModel.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#if !defined(Q_MOC_RUN) -#include -#include -#include - -#include -#include -#include - -AZ_PUSH_DISABLE_WARNING( - 4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...' -#include -#include -#include -#endif -AZ_POP_DISABLE_WARNING -namespace AzToolsFramework -{ - namespace AssetBrowser - { - class AssetBrowserTableModel - : public QSortFilterProxyModel - { - Q_OBJECT - public: - AZ_CLASS_ALLOCATOR(AssetBrowserTableModel, AZ::SystemAllocator, 0); - explicit AssetBrowserTableModel(QObject* parent = nullptr); - - QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; - QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; - QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - }; - } -} From 1c2b8f91118f744314dc4f34ed33fc66245eea7c Mon Sep 17 00:00:00 2001 From: igarri Date: Tue, 18 May 2021 13:33:21 +0100 Subject: [PATCH 028/233] Adding AZ_CVAR for the new feature --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 10 +++- .../AssetBrowser/Search/Filter.cpp | 5 -- .../AssetBrowser/Search/Filter.h | 1 - .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 51 ++++++++++++------- 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index 9ee00a96ae..5b820c4d06 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -12,6 +12,7 @@ #include #include #include +#include AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") #include @@ -22,6 +23,10 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") #include AZ_POP_DISABLE_WARNING +AZ_CVAR( + bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null, + "Use the new AssetBrowser TableView for searching assets."); + namespace AzToolsFramework { namespace AssetBrowser @@ -32,7 +37,10 @@ namespace AzToolsFramework : QSortFilterProxyModel(parent) { m_showColumn.insert(static_cast(AssetBrowserEntry::Column::DisplayName)); - m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); + if (ed_useNewAssetBrowserTableView) + { + m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); + } m_collator.setNumericMode(true); AssetBrowserComponentNotificationBus::Handler::BusConnect(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.cpp index 86315613ad..ceda5f8e19 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.cpp @@ -233,11 +233,6 @@ namespace AzToolsFramework Q_EMIT updatedSignal(); } - bool StringFilter::IsEmpty() const - { - return m_filterString.isEmpty(); - } - QString StringFilter::GetNameInternal() const { return m_filterString; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.h index 6d19fb3b47..135dd00925 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.h @@ -110,7 +110,6 @@ namespace AzToolsFramework ~StringFilter() override = default; void SetFilterString(const QString& filterString); - bool IsEmpty() const; protected: QString GetNameInternal() const override; bool MatchInternal(const AssetBrowserEntry* entry) const override; diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 8c01f6841c..c4a79a9394 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -20,6 +20,7 @@ #include #include #include +#include // AzQtComponents #include @@ -32,6 +33,9 @@ AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING #include AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING +AZ_CVAR( + bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null, + "Use the new AssetBrowser TableView for searching assets."); class ListenerForShowAssetEditorEvent : public QObject @@ -78,15 +82,40 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_filterModel->setSourceModel(m_assetBrowserModel); m_filterModel->SetFilter(m_ui->m_searchWidget->GetFilter()); - m_tableModel->setFilterRole(Qt::DisplayRole); - m_tableModel->setSourceModel(m_filterModel.data()); + m_ui->m_viewSwitcherCheckBox->setVisible(false); + m_ui->m_assetBrowserTableViewWidget->setVisible(false); + if (ed_useNewAssetBrowserTableView) + { + m_ui->m_viewSwitcherCheckBox->setVisible(true); + m_tableModel->setFilterRole(Qt::DisplayRole); + m_tableModel->setSourceModel(m_filterModel.data()); + m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data()); + connect( + m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, m_tableModel.data(), + &AssetBrowserTableModel::UpdateTableModelMaps); + connect( + m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::selectionChangedSignal, this, + &AzAssetBrowserWindow::SelectionChangedSlot); + connect( + m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, + &AzAssetBrowserWindow::DoubleClickedItemTableModel); + connect( + m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget, + &SearchWidget::ClearStringFilter); + connect( + m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget, + &SearchWidget::ClearTypeFilter); + + m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main"); + + connect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); + connect(m_ui->m_viewSwitcherCheckBox, &QCheckBox::stateChanged, this, &AzAssetBrowserWindow::LockToDefaultView); + + } m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); //m_ui->m_assetBrowserTreeViewWidget->hideColumn(static_cast(AssetBrowserEntry::Column::Path)); - m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data()); - m_ui->m_assetBrowserTableViewWidget->setVisible(false); - connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), &AssetBrowserFilterModel::filterUpdatedSlot); connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, this, [this]() @@ -96,30 +125,18 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); }); - connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, m_tableModel.data(), &AssetBrowserTableModel::UpdateTableModelMaps); connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, this, &AzAssetBrowserWindow::SelectionChangedSlot); - connect(m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::selectionChangedSignal, - this, &AzAssetBrowserWindow::SelectionChangedSlot); - connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); - connect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItemTableModel); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, &SearchWidget::ClearStringFilter); connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter); - connect(m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget, &SearchWidget::ClearStringFilter); - connect(m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter); m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); - m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main"); - - connect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); - connect(m_ui->m_viewSwitcherCheckBox, &QCheckBox::stateChanged, this, &AzAssetBrowserWindow::LockToDefaultView); } AzAssetBrowserWindow::~AzAssetBrowserWindow() From f49e4b33337b47b6939f1df90c99696fd0e5efd5 Mon Sep 17 00:00:00 2001 From: igarri Date: Tue, 18 May 2021 15:26:05 +0100 Subject: [PATCH 029/233] modified code from feedback --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 33 +++++++++++-------- .../AssetBrowser/AssetBrowserModel.cpp | 10 +++--- .../AssetBrowser/AssetBrowserModel.h | 2 -- .../Views/AssetBrowserTreeView.cpp | 1 + .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 5 ++- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index 5b820c4d06..77e5523ee3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -23,9 +23,7 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") #include AZ_POP_DISABLE_WARNING -AZ_CVAR( - bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null, - "Use the new AssetBrowser TableView for searching assets."); +AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserTableView); namespace AzToolsFramework { @@ -36,10 +34,10 @@ namespace AzToolsFramework AssetBrowserFilterModel::AssetBrowserFilterModel(QObject* parent) : QSortFilterProxyModel(parent) { - m_showColumn.insert(static_cast(AssetBrowserEntry::Column::DisplayName)); + m_showColumn.insert(aznumeric_cast(AssetBrowserEntry::Column::DisplayName)); if (ed_useNewAssetBrowserTableView) { - m_showColumn.insert(static_cast(AssetBrowserEntry::Column::Path)); + m_showColumn.insert(aznumeric_cast(AssetBrowserEntry::Column::Path)); } m_collator.setNumericMode(true); AssetBrowserComponentNotificationBus::Handler::BusConnect(); @@ -149,19 +147,23 @@ namespace AzToolsFramework m_assetTypeFilter = qobject_cast >(*it); } - it = AZStd::find_if(subFilters.begin(), subFilters.end(), [](FilterConstType filter) -> bool + auto compStringFilterIter = AZStd::find_if(subFilters.begin(), subFilters.end(), [](FilterConstType filter) -> bool { + //The real StringFilter is really a CompositeFilter with just one StringFilter in its subfilter list + //To know if it is actually a StringFilter we have to get that subfilter and check if it is a Stringfilter. auto stringCompositeFilter = qobject_cast >(filter); bool isStringFilter = false; if (stringCompositeFilter) { - auto& subFilters = stringCompositeFilter->GetSubFilters(); - auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [](FilterConstType filt) -> bool - { + const auto& stringSubfilters = stringCompositeFilter->GetSubFilters(); + auto canBeCasted = [](FilterConstType filt) -> bool { auto strFilter = qobject_cast>(filt); return !strFilter.isNull(); - }); - if (it != subFilters.end()) + }; + auto stringSubfliterConstIter = AZStd::find_if(stringSubfilters.begin(), stringSubfilters.end(), canBeCasted); + + //A Composite StringFilter will only have just one subfilter and nothing more. + if (stringSubfliterConstIter != stringSubfilters.end() && stringSubfilters.size() == 1) { isStringFilter = true; } @@ -169,10 +171,13 @@ namespace AzToolsFramework return isStringFilter; }); - if (it != subFilters.end()) + if (compStringFilterIter != subFilters.end()) { - auto compStringFilter = qobject_cast>(*it); - m_stringFilter = qobject_cast>(compStringFilter->GetSubFilters()[0]); + auto compStringFilter = qobject_cast>(*compStringFilterIter); + if (compStringFilter->GetSubFilters().size() > 0 && compStringFilter->GetSubFilters()[0]) + { + m_stringFilter = qobject_cast>(compStringFilter->GetSubFilters()[0]); + } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp index 1101e11f3a..a5cfad09ba 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp @@ -27,8 +27,6 @@ namespace AzToolsFramework { namespace AssetBrowser { - const int AssetBrowserModel::m_column = static_cast(AssetBrowserEntry::Column::DisplayName); - AssetBrowserModel::AssetBrowserModel(QObject* parent) : QAbstractItemModel(parent) , m_rootEntry(nullptr) @@ -143,9 +141,9 @@ namespace AzToolsFramework if (parent.isValid()) { - if ((parent.column() != static_cast(AssetBrowserEntry::Column::DisplayName)) && - (parent.column() != static_cast(AssetBrowserEntry::Column::Name)) && - (parent.column() != static_cast(AssetBrowserEntry::Column::Path))) + if ((parent.column() != aznumeric_cast(AssetBrowserEntry::Column::DisplayName)) && + (parent.column() != aznumeric_cast(AssetBrowserEntry::Column::Name)) && + (parent.column() != aznumeric_cast(AssetBrowserEntry::Column::Path))) { return 0; } @@ -394,7 +392,7 @@ namespace AzToolsFramework } int row = entry->row(); - index = createIndex(row, m_column, entry); + index = createIndex(row, aznumeric_cast(AssetBrowserEntry::Column::DisplayName), entry); return true; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.h index 9e60c44dae..3c4cae5588 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.h @@ -91,8 +91,6 @@ namespace AzToolsFramework static void SourceIndexesToAssetIds(const QModelIndexList& indexes, AZStd::vector& assetIds); static void SourceIndexesToAssetDatabaseEntries(const QModelIndexList& indexes, AZStd::vector& entries); - const static int m_column; - private: AZStd::shared_ptr m_rootEntry; bool m_loaded; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp index e29607698d..660c04ccd4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp @@ -102,6 +102,7 @@ namespace AzToolsFramework QModelIndexList sourceIndexes; for (const auto& index : selectedIndexes()) { + //If we check for more than one column then the model will try to select the same entry several times. if (index.column() == 0) { sourceIndexes.push_back(m_assetBrowserSortFilterProxyModel->mapToSource(index)); diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index c4a79a9394..412dc61465 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -114,7 +114,6 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) } m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); - //m_ui->m_assetBrowserTreeViewWidget->hideColumn(static_cast(AssetBrowserEntry::Column::Path)); connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), &AssetBrowserFilterModel::filterUpdatedSlot); @@ -232,7 +231,7 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& using namespace AzToolsFramework; using namespace AzToolsFramework::AssetBrowser; // assumption: Double clicking an item selects it before telling us we double clicked it. - auto selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); + const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); for (const AssetBrowserEntry* entry : selectedAssets) { AZ::Data::AssetId assetIdToOpen; @@ -269,7 +268,7 @@ void AzAssetBrowserWindow::DoubleClickedItemTableModel([[maybe_unused]] const QM using namespace AzToolsFramework; using namespace AzToolsFramework::AssetBrowser; // assumption: Double clicking an item selects it before telling us we double clicked it. - auto selectedAssets = m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets(); + const auto& selectedAssets = m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets(); for (const AssetBrowserEntry* entry : selectedAssets) { AZ::Data::AssetId assetIdToOpen; From ec784b005ffdd39288097ed17f44612cf00b32a4 Mon Sep 17 00:00:00 2001 From: igarri Date: Tue, 18 May 2021 15:49:35 +0100 Subject: [PATCH 030/233] More Corrections --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 2 +- .../AssetBrowser/AssetBrowserTableModel.cpp | 44 ++++++++++--------- .../AssetBrowser/AssetBrowserTableModel.h | 13 +++++- .../Views/AssetBrowserTableView.cpp | 28 +++++++----- .../Views/AssetBrowserTableView.h | 11 +++++ 5 files changed, 64 insertions(+), 34 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index 77e5523ee3..12b0256c5d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -174,7 +174,7 @@ namespace AzToolsFramework if (compStringFilterIter != subFilters.end()) { auto compStringFilter = qobject_cast>(*compStringFilterIter); - if (compStringFilter->GetSubFilters().size() > 0 && compStringFilter->GetSubFilters()[0]) + if (!compStringFilter->GetSubFilters().isEmpty() && compStringFilter->GetSubFilters()[0]) { m_stringFilter = qobject_cast>(compStringFilter->GetSubFilters()[0]); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index bebdf44c9a..b9bfe1bea6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -1,3 +1,14 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include #include #include @@ -32,24 +43,27 @@ namespace AzToolsFramework { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { - switch (section) + auto columnRole = aznumeric_cast(role); + switch (columnRole) { - case static_cast(AssetBrowserEntry::Column::Name): + case AssetBrowserEntry::Column::Name: return QString("Name"); - case static_cast(AssetBrowserEntry::Column::Path): + case AssetBrowserEntry::Column::Path: return QString("Path"); default: return QString::number(section); } } - return QSortFilterProxyModel::headerData(section, orientation, role); // QVariant(); + return QSortFilterProxyModel::headerData(section, orientation, role); } QVariant AssetBrowserTableModel::data(const QModelIndex& index, int role) const { auto sourceIndex = mapToSource(index); if (!sourceIndex.isValid()) + { return QVariant(); + } AssetBrowserEntry* entry = GetAssetEntry(sourceIndex); if (entry == nullptr) @@ -66,22 +80,10 @@ namespace AzToolsFramework return parent.isValid() ? QModelIndex() : createIndex(row, column, m_indexMap[row].internalPointer()); } - QModelIndex AssetBrowserTableModel::parent(const QModelIndex& child) const + QModelIndex AssetBrowserTableModel::parent([[maybe_unused]] const QModelIndex& child) const { - AZ_UNUSED(child); return QModelIndex(); } - bool AssetBrowserTableModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const - { - AZ_UNUSED(source_row); - AZ_UNUSED(source_parent); - // no filter present, every entry is not visible - if (!m_filterModel->GetFilter()) - { - return true; - } - return true; - } int AssetBrowserTableModel::rowCount(const QModelIndex& parent) const { @@ -94,14 +96,14 @@ namespace AzToolsFramework for (int i = 0; i < rows; ++i) { QModelIndex index = model->index(i, 0, parent); - if (model->hasChildren(index) == false) + if (!model->hasChildren(index)) { beginInsertRows(parent, row, row); m_indexMap[row] = index; endInsertRows(); Q_EMIT dataChanged(index, index); - row = row + 1; + ++row; } if (model->hasChildren(index)) @@ -124,9 +126,9 @@ namespace AzToolsFramework } } void AssetBrowserTableModel::UpdateTableModelMaps() -{ + { emit layoutAboutToBeChanged(); - if (m_indexMap.size() > 0) + if (!m_indexMap.isEmpty()) { beginRemoveRows(m_indexMap.first(), m_indexMap.first().row(), m_indexMap.last().row()); m_indexMap.clear(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index 432300194b..b0497213a0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -1,4 +1,16 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once + #if !defined(Q_MOC_RUN) #include #include @@ -30,7 +42,6 @@ namespace AzToolsFramework public Q_SLOTS: void UpdateTableModelMaps(); protected: - bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override; //////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp index cb4a989043..df3b0b8644 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -1,3 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + #include #include @@ -109,21 +121,16 @@ namespace AzToolsFramework } QTableView::rowsAboutToBeRemoved(parent, start, end); } - void AssetBrowserTableView::layoutChangedSlot(const QList& parents, QAbstractItemModel::LayoutChangeHint hint) + void AssetBrowserTableView::layoutChangedSlot([[maybe_unused]] const QList& parents,[[maybe_unused]] QAbstractItemModel::LayoutChangeHint hint) { - AZ_UNUSED(parents); - AZ_UNUSED(hint); - scrollToTop(); } - void AssetBrowserTableView::SelectProduct(AZ::Data::AssetId assetID) + void AssetBrowserTableView::SelectProduct([[maybe_unused]] AZ::Data::AssetId assetID) { - AZ_UNUSED(assetID); } - void AssetBrowserTableView::SelectFileAtPath(const AZStd::string& assetPath) + void AssetBrowserTableView::SelectFileAtPath([[maybe_unused]] const AZStd::string& assetPath) { - AZ_UNUSED(assetPath); } void AssetBrowserTableView::ClearFilter() @@ -142,11 +149,10 @@ namespace AzToolsFramework { } - void AssetBrowserTableView::OnContextMenu(const QPoint& point) + void AssetBrowserTableView::OnContextMenu([[maybe_unused]] const QPoint& point) { - AZ_UNUSED(point); - auto selectedAssets = GetSelectedAssets(); + const auto& selectedAssets = GetSelectedAssets(); if (selectedAssets.size() != 1) { return; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h index b39d48c391..482a072373 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -1,3 +1,14 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once #if !defined(Q_MOC_RUN) #include From 0b50b6cc63842c7537ff43609a039e39295f1a5d Mon Sep 17 00:00:00 2001 From: igarri Date: Wed, 19 May 2021 12:04:40 +0100 Subject: [PATCH 031/233] Fixed code Style and minor issues from feedback --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 1 - .../AssetBrowser/AssetBrowserTableModel.cpp | 13 ++++++++----- .../AssetBrowser/Views/AssetBrowserTableView.cpp | 8 ++++++++ .../AssetBrowser/Views/EntryDelegate.cpp | 6 +++--- .../Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp | 4 ++++ 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index 12b0256c5d..c5723832c7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -184,7 +184,6 @@ namespace AzToolsFramework invalidateFilter(); Q_EMIT filterChanged(); emit stringFilterPopulated(!m_stringFilter.isNull()); - } void AssetBrowserFilterModel::filterUpdatedSlot() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index b9bfe1bea6..6d9803dfd4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -22,10 +22,11 @@ namespace AzToolsFramework { setDynamicSortFilter(false); } + void AssetBrowserTableModel::setSourceModel(QAbstractItemModel* sourceModel) { m_filterModel = qobject_cast(sourceModel); - AZ_Assert(m_filterModel, "Expecting AssetBrowserFilterModel"); + AZ_Assert(m_filterModel, "Error in AssetBrowserTableModel initialization, class expects source model to be an AssetBrowserFilterModel."); QSortFilterProxyModel::setSourceModel(sourceModel); } @@ -47,9 +48,9 @@ namespace AzToolsFramework switch (columnRole) { case AssetBrowserEntry::Column::Name: - return QString("Name"); + return tr("Name"); case AssetBrowserEntry::Column::Path: - return QString("Path"); + return tr("Path"); default: return QString::number(section); } @@ -68,8 +69,8 @@ namespace AzToolsFramework AssetBrowserEntry* entry = GetAssetEntry(sourceIndex); if (entry == nullptr) { - AZ_Assert(false, "ERROR - index internal pointer not pointing to an AssetEntry. Tree provided by the AssetBrowser invalid?"); - return Qt::PartiallyChecked; + AZ_Assert(false, "AssetBrowserTableModel - QModelIndex does not reference an AssetEntry. Source model is not valid."); + return QVariant(); } return sourceIndex.data(role); @@ -113,6 +114,7 @@ namespace AzToolsFramework } return row; } + AssetBrowserEntry* AssetBrowserTableModel::GetAssetEntry(QModelIndex index) const { if (index.isValid()) @@ -125,6 +127,7 @@ namespace AzToolsFramework return nullptr; } } + void AssetBrowserTableModel::UpdateTableModelMaps() { emit layoutAboutToBeChanged(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp index df3b0b8644..f7128cb504 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -60,11 +60,13 @@ namespace AzToolsFramework AssetBrowserViewRequestBus::Handler::BusConnect(); AssetBrowserComponentNotificationBus::Handler::BusConnect(); } + AssetBrowserTableView::~AssetBrowserTableView() { AssetBrowserViewRequestBus::Handler::BusDisconnect(); AssetBrowserComponentNotificationBus::Handler::BusDisconnect(); } + void AssetBrowserTableView::setModel(QAbstractItemModel* model) { m_tableModel = qobject_cast(model); @@ -76,6 +78,7 @@ namespace AzToolsFramework horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch); horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch); } + void AssetBrowserTableView::SetName(const QString& name) { m_name = name; @@ -86,6 +89,7 @@ namespace AzToolsFramework OnAssetBrowserComponentReady(); } } + AZStd::vector AssetBrowserTableView::GetSelectedAssets() const { QModelIndexList sourceIndexes; @@ -101,11 +105,13 @@ namespace AzToolsFramework AssetBrowserModel::SourceIndexesToAssetDatabaseEntries(sourceIndexes, entries); return entries; } + void AssetBrowserTableView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { QTableView::selectionChanged(selected, deselected); Q_EMIT selectionChangedSignal(selected, deselected); } + void AssetBrowserTableView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { // if selected entry is being removed, clear selection so not to select (and attempt to preview) other entries potentially @@ -121,10 +127,12 @@ namespace AzToolsFramework } QTableView::rowsAboutToBeRemoved(parent, start, end); } + void AssetBrowserTableView::layoutChangedSlot([[maybe_unused]] const QList& parents,[[maybe_unused]] QAbstractItemModel::LayoutChangeHint hint) { scrollToTop(); } + void AssetBrowserTableView::SelectProduct([[maybe_unused]] AZ::Data::AssetId assetID) { } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index b968f0480a..dff8a170c7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -99,9 +99,9 @@ namespace AzToolsFramework style->drawItemText( painter, remainingRect, option.displayAlignment, actualPalette, isEnabled, - index.column() == static_cast(AssetBrowserEntry::Column::Name) - ? qvariant_cast(entry->data(static_cast(AssetBrowserEntry::Column::Name))) - : qvariant_cast(entry->data(static_cast(AssetBrowserEntry::Column::Path))), + index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name) + ? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name))) + : qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Path))), isSelected ? QPalette::HighlightedText : QPalette::Text); } } diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 412dc61465..35734042a0 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -312,9 +312,13 @@ void AzAssetBrowserWindow::LockToDefaultView(bool state) using namespace AzToolsFramework::AssetBrowser; SwitchDisplayView(!state); if (state == true) + { disconnect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); + } else + { connect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); + } } #include From 884022eb6411f5543c103c060c18d4bb9576f4a9 Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 19 May 2021 13:31:47 +0100 Subject: [PATCH 032/233] Add additional meta-data for static artifacts --- cmake/LYTestWrappers.cmake | 29 +- .../ConsoleFrontendConfig.in | 295 +++++++++++------- .../LYTestImpactFramework.cmake | 181 ++++++----- 3 files changed, 299 insertions(+), 206 deletions(-) diff --git a/cmake/LYTestWrappers.cmake b/cmake/LYTestWrappers.cmake index 2a65929cf6..40669f7426 100644 --- a/cmake/LYTestWrappers.cmake +++ b/cmake/LYTestWrappers.cmake @@ -151,12 +151,6 @@ function(ly_add_test) set(LY_ADDED_TEST_NAME ${qualified_test_run_name_with_suite}::TEST_RUN) set(LY_ADDED_TEST_NAME ${LY_ADDED_TEST_NAME} PARENT_SCOPE) - # Store the test so we can walk through all of them in LYTestImpactFramework.cmake - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS ${LY_ADDED_TEST_NAME}) - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${LY_ADDED_TEST_NAME}_TEST_NAME ${ly_add_test_NAME}) - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${LY_ADDED_TEST_NAME}_TEST_SUITE ${ly_add_test_TEST_SUITE}) - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${LY_ADDED_TEST_NAME}_TEST_LIBRARY ${ly_add_test_TEST_LIBRARY}) - set(final_labels SUITE_${ly_add_test_TEST_SUITE}) if (ly_add_test_TEST_REQUIRES) @@ -247,6 +241,12 @@ function(ly_add_test) endif() + # Store the test so we can walk through all of them in LYTestImpactFramework.cmake + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS ${ly_add_test_NAME}) + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_test_NAME}_TEST_SUITE ${ly_add_test_TEST_SUITE}) + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_test_NAME}_TEST_LIBRARY ${ly_add_test_TEST_LIBRARY}) + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_test_NAME}_TEST_TIMEOUT ${ly_add_test_TIMEOUT}) + endfunction() #! ly_add_pytest: registers target PyTest-based test with CTest @@ -298,8 +298,8 @@ function(ly_add_pytest) ${ly_add_pytest_UNPARSED_ARGUMENTS} ) + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_pytest_NAME}_SCRIPT_PATH ${ly_add_pytest_PATH}) set_tests_properties(${LY_ADDED_TEST_NAME} PROPERTIES RUN_SERIAL "${ly_add_pytest_TEST_SERIAL}") - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${LY_ADDED_TEST_NAME}_SCRIPT_PATH ${ly_add_pytest_PATH}) endfunction() #! ly_add_editor_python_test: registers target Editor Python Bindings test with CTest @@ -363,8 +363,8 @@ function(ly_add_editor_python_test) COMPONENT ${ly_add_editor_python_test_COMPONENT} ) + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_editor_python_test_NAME}_SCRIPT_PATH ${ly_add_editor_python_test_PATH}) set_tests_properties(${LY_ADDED_TEST_NAME} PROPERTIES RUN_SERIAL "${ly_add_editor_python_test_TEST_SERIAL}") - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${LY_ADDED_TEST_NAME}_SCRIPT_PATH ${ly_add_editor_python_test_PATH}) endfunction() #! ly_add_googletest: Adds a new RUN_TEST using for the specified target using the supplied command or fallback to running @@ -424,8 +424,14 @@ function(ly_add_googletest) set(full_test_command $ $ AzRunUnitTests) # Add AzTestRunner as a build dependency ly_add_dependencies(${build_target} AZ::AzTestRunner) + # Ideally, we would populate the full command procedurally but the generator expressions won't be expanded by the time we need this data + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_googletest_NAME}_TEST_COMMAND "AzRunUnitTests") else() set(full_test_command ${ly_add_googletest_TEST_COMMAND}) + # Remove the generator expressions so we are left with the argument(s) required to run unit tests for executable targets + string(REPLACE ";" "" stripped_test_command ${full_test_command}) + string(GENEX_STRIP ${stripped_test_command} stripped_test_command) + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_googletest_NAME}_TEST_COMMAND ${stripped_test_command}) endif() string(REPLACE "::" "_" report_directory "${GTEST_XML_OUTPUT_DIR}/${ly_add_googletest_NAME}.xml") @@ -507,8 +513,14 @@ function(ly_add_googlebenchmark) # If command is not supplied attempts, uses the AzTestRunner to run googlebenchmarks on the supplied TARGET set(full_test_command $ $ AzRunBenchmarks ${output_format_args}) + # Ideally, we would populate the full command procedurally but the generator expressions won't be expanded by the time we need this data + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_googlebenchmark_NAME}_TEST_COMMAND "AzRunUnitTests") else() set(full_test_command ${ly_add_googlebenchmark_TEST_COMMAND}) + # Remove the generator expressions so we are left with the argument(s) required to run unit tests for executable targets + string(REPLACE ";" "" stripped_test_command ${full_test_command}) + string(GENEX_STRIP ${stripped_test_command} stripped_test_command) + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_googletest_NAME}_TEST_COMMAND ${stripped_test_command}) endif() ly_add_test( @@ -524,6 +536,5 @@ function(ly_add_googlebenchmark) AZ::AzTestRunner COMPONENT ${ly_add_googlebenchmark_COMPONENT} ) - endfunction() diff --git a/cmake/TestImpactFramework/ConsoleFrontendConfig.in b/cmake/TestImpactFramework/ConsoleFrontendConfig.in index 5f3908671e..73e90ea0ce 100644 --- a/cmake/TestImpactFramework/ConsoleFrontendConfig.in +++ b/cmake/TestImpactFramework/ConsoleFrontendConfig.in @@ -1,111 +1,184 @@ -[path.configuration] -repo_dir = "${repo_dir}" -working_dir = "${working_dir}" -bin_dir = "${runtime_bin_dir}" -tests_dir = "${tests_dir}" -temp_dir = "${temp_dir}" -target_mappings_dir = "${source_target_mapping_dir}" -test_type_dir = "${test_type_dir}" -dependencies_dir = "${target_dependency_dir}" - -[sourcetree.configuration.filters.autogen] -# E.g. matches input /Foo/{Bar}.FooBar.xml with output /Baz/{Bar}.BazBar.cpp -input_output_pairer = "(.*)\\..*" -[sourcetree.configuration.filters.autogen.input] -exclude_filter = [".jinja"] -[sourcetree.configuration.filters.source] -exclude_filter = [".cmake"] -[sourcetree.configuration.testtype.enumerated] -file = "All.tests" -# The table to read from the test enumeration file that contains the test targets -target_table = "google.test" -[sourcetree.configuration.dependency] -# E.g. matches WhiteBox.Editor.Static\n(Gem::WhiteBox.Editor.Static) or WhiteBox.Editor.Static -target_dependency_file_matcher = "target\\.(.*)\\.(dependers)?" -# E.g. matches target.WhiteBox.Editor.Static (for dependency) target.WhiteBox.Editor.Static.dependers (for dependers) -target_vertex_matcher = "(?:(.*)\\n|(.*)" - -[spartia.configuration] -test_impact_Data_file = "TestImpactData.spartia" -test_run_coverage_file = "{test_dir}\\{test_target}.coverage.xml" -test_run_results_file = "{test_dir}\\{test_target}.results.xml" -test_enumeration_file = "{temp_dir}\\{test_target}.enum" -test_shard_selection_file = "{temp_dir}\\{test_target}.filter.{shard_id}" -exclude_filter = [ -{ target = "AssetBundler.Tests", tests = ["*"] }, -{ target = "AssetProcessor.Tests", tests = ["*"] }, -{ target = "CryRenderD3D11.Tests", tests = ["*"] }, -{ target = "CryRenderD3D12.Tests", tests = ["*"] }, -{ target = "LyzardApplicationDescriptors.Tests", tests = ["*"] }, -{ target = "EMotionFX.Editor.Tests", tests = ["UIFixture.*", "SimulatedObjectModelTestsFixture.*", "TestParametersFixture.*", "CanSeeJointsFixture.*", "LODSkinnedMeshFixtureTests/LODSkinnedMeshFixture.CheckLODLevels/*"] }, -{ target = "EMotionFX.Tests", tests = ["UIFixture.*", "SimulatedObjectModelTestsFixture.*", "TestParametersFixture.*", "CanSeeJointsFixture.*"] }, -{ target = "AzCore.Tests", tests = ["AllocatorsTestFixtureLeakDetectionDeathTest_SKIPCODECOVERAGE.AllocatorLeak"] }, -] -[spartia.configuration.shard] -# Long tests that will be sharded -include_filter = [ -{ target = "AzCore.Tests", policy = "fixture_contiguous" }, -{ target = "AzToolsFramework.Tests", policy = "fixture_contiguous" }, -{ target = "Framework.Tests", policy = "test_interleaved" }, -{ target = "LmbrCentral.Editor.Tests", policy = "test_interleaved" }, -{ target = "EditorLib.Tests", policy = "test_interleaved" }, -{ target = "PhysX.Tests", policy = "test_interleaved" }, -{ target = "ImageProcessing.Tests", policy = "test_interleaved" }, -{ target = "Atom_RPI.Tests", policy = "test_interleaved" }, -{ target = "Atom_RHI.Tests", policy = "test_interleaved" }, -{ target = "AzManipulatorFramework.Tests", policy = "test_interleaved" }, -{ target = "WhiteBox.Editor.Tests", policy = "test_interleaved" }, -{ target = "AzManipulatorTestFramework.Tests", policy = "test_interleaved" }, -{ target = "AtomCore.Tests", policy = "test_interleaved" }, -{ target = "ImageProcessingAtom.Editor.Tests", policy = "test_interleaved" }, -{ target = "EditorPythonBindings.Tests", policy = "test_interleaved" }, -{ target = "Atom_Utils.Tests", policy = "test_interleaved" }, -{ target = "AudioEngineWwise.Editor.Tests", policy = "test_interleaved" }, -{ target = "Multiplayer.Tests", policy = "test_interleaved" }, -{ target = "LmbrCentral.Tests", policy = "test_interleaved" }, -{ target = "LyMetricsShared.Tests", policy = "fixture_contiguous" }, -{ target = "PhysX.Editor.Tests", policy = "test_interleaved" }, -{ target = "ComponentEntityEditorPlugin.Tests", policy = "test_interleaved" }, -{ target = "DeltaCataloger.Tests", policy = "test_interleaved" }, -{ target = "GradientSignal.Tests", policy = "test_interleaved" }, -{ target = "LyShine.Tests", policy = "test_interleaved" }, -{ target = "EMotionFX.Editor.Tests", policy = "test_interleaved" }, -{ target = "EMotionFX.Tests", policy = "test_interleaved" }, -{ target = "CrySystem.Tests", policy = "test_interleaved" }, -] -[spartia.configuration.instrumentation] -abs_bin = "${instrumentation_bin}" -[spartia.configuration.instrumentation.errors] -# AzCppCoverage error codes -incorrect_args = -1618178468 -[spartia.configuration.instrumentation.test_coverage] -args = "--export_type cobertura:\"{test_run_coverage_file}\"" -[spartia.configuration.instrumentation.test_selection] -args = "--gtest_filter={test_selection}" -[spartia.configuration.instrumentation.test_enumeration] -args = "--gtest_list_tests" -[spartia.configuration.instrumentation.test_results] -args = "--gtest_output=xml:\"{test_run_results_file}\"" -[spartia.configuration.instrumentation.test_results.errors] -test_success = 0 -test_failures = 1 -[spartia.configuration.instrumentation.binary_type.dynlib] -abs_bin = "{bin_dir}\\AzTestRunner.exe" -args = "\"{bin_dir}\\{test_target}.dll\" AzRunUnitTests" -[spartia.configuration.instrumentation.binary_type.dynlib.test_enumeration] -args = "--stdout_to_file \"{test_enumeration_file}\" {test_enumeration}" -[spartia.configuration.instrumentation.binary_type.dynlib.test_shard_selection] -args = "--args_from_file \"{test_shard_selection_file}\"" -[spartia.configuration.instrumentation.binary_type.dynlib.errors] -# AzTestRunner error codes -failed_to_find_target_bin = 102 -incorrect_args = 101 -known_errors = [ 103, 104] -[spartia.configuration.instrumentation.binary_type.executable] -abs_bin = "{bin_dir}\\{test_target}.exe" -[spartia.configuration.instrumentation.binary_type.executable.test_enumeration] -args = "--stdout_to_file \"{test_enumeration_file}\" {test_enumeration}" -[spartia.configuration.instrumentation.binary_type.executable.test_shard_selection] -args = "--args_from_file \"{test_shard_selection_file}\"" -[spartia.configuration.test_run.seed] -instrumentation_args = "--modules \"{bin_dir}\" --excluded_modules \"{binary_type.dynlib.abs_bin}\" --sources \"{repo_dir}\" --no_breakpoints {test_coverage} -- " \ No newline at end of file +{ + "meta": { + "platform": "${platform}", + "timestamp": "${timestamp}" + }, + "repo": { + "root": "${repo_dir}" + }, + "workspace": { + "temp": { + "root": "${temp_dir}", + "artifact_dir": "RuntimeArtifact" + }, + "persistent": { + "root": "${persistent_dir}", + "test_impact_data_file": "TestImpactData.spartia", + "enumeration_cache_dir": "EnumerationCache" + } + }, + "artifacts": { + "static": { + "build_target_descriptor": { + "dir": "${source_target_mapping_dir}", + "target_sources": { + "static": { + "include_filters": [ + ".h", ".hpp", ".hxx", ".inl", ".c", ".cpp", ".cxx" + ] + }, + "autogen": { + "input_output_pairer": "(.*)\\..*", + "input": { + "include_filters": [ + ".xml" + ] + } + } + } + }, + "dependency_graph_data": { + "dir": "${target_dependency_dir}", + "matchers": { + "target_dependency_file": "target\\.(.*)\\.(dependers)?", + "target_vertex": "(?:(.*)\\n|(.*)" + } + }, + "test_target_meta": { + "file": "${test_target_type_file}" + } + } + }, + "test_engine": { + "test_runner": { + "bin": "${test_runner_bin}" + }, + "instrumentation": { + "bin": "${instrumentation_bin}" + } + }, + "target": { + "dir": "${bin_dir}", + "exclude": [ + + ], + "shard": [ + { + "policy": "fixture_contiguous", + "target": "AzCore.Tests" + }, + { + "policy": "fixture_contiguous", + "target": "AzToolsFramework.Tests" + }, + { + "policy": "test_interleaved", + "target": "Framework.Tests" + }, + { + "policy": "test_interleaved", + "target": "LmbrCentral.Editor.Tests" + }, + { + "policy": "test_interleaved", + "target": "EditorLib.Tests" + }, + { + "policy": "test_interleaved", + "target": "PhysX.Tests" + }, + { + "policy": "test_interleaved", + "target": "ImageProcessing.Tests" + }, + { + "policy": "test_interleaved", + "target": "Atom_RPI.Tests" + }, + { + "policy": "test_interleaved", + "target": "Atom_RHI.Tests" + }, + { + "policy": "test_interleaved", + "target": "AzManipulatorFramework.Tests" + }, + { + "policy": "test_interleaved", + "target": "WhiteBox.Editor.Tests" + }, + { + "policy": "test_interleaved", + "target": "ImageProcessing.Tests" + }, + { + "policy": "test_interleaved", + "target": "AzManipulatorTestFramework.Tests" + }, + { + "policy": "test_interleaved", + "target": "AtomCore.Tests" + }, + { + "policy": "test_interleaved", + "target": "ImageProcessingAtom.Editor.Tests" + }, + { + "policy": "test_interleaved", + "target": "EditorPythonBindings.Tests" + }, + { + "policy": "test_interleaved", + "target": "Atom_Utils.Tests" + }, + { + "policy": "test_interleaved", + "target": "AudioEngineWwise.Editor.Tests" + }, + { + "policy": "test_interleaved", + "target": "Multiplayer.Tests" + }, + { + "policy": "test_interleaved", + "target": "LmbrCentral.Tests" + }, + { + "policy": "fixture_contiguous", + "target": "LyMetricsShared.Tests" + }, + { + "policy": "test_interleaved", + "target": "PhysX.Editor.Tests" + }, + { + "policy": "test_interleaved", + "target": "ComponentEntityEditorPlugin.Tests" + }, + { + "policy": "test_interleaved", + "target": "DeltaCataloger.Tests" + }, + { + "policy": "test_interleaved", + "target": "GradientSignal.Tests" + }, + { + "policy": "test_interleaved", + "target": "LyShine.Tests" + }, + { + "policy": "test_interleaved", + "target": "EMotionFX.Editor.Tests" + }, + { + "policy": "test_interleaved", + "target": "EMotionFX.Tests" + }, + { + "policy": "test_interleaved", + "target": "CrySystem.Tests" + } + ] + } +} diff --git a/cmake/TestImpactFramework/LYTestImpactFramework.cmake b/cmake/TestImpactFramework/LYTestImpactFramework.cmake index c08c64db80..606ab0df4b 100644 --- a/cmake/TestImpactFramework/LYTestImpactFramework.cmake +++ b/cmake/TestImpactFramework/LYTestImpactFramework.cmake @@ -15,6 +15,9 @@ option(LY_TEST_IMPACT_ACTIVE "Enable test impact framework" OFF) # Path to test instrumentation binary option(LY_TEST_IMPACT_INSTRUMENTATION_BIN "Path to test impact framework instrumentation binary" OFF) +# Name of test impact framework console static library target +set(LY_TEST_IMPACT_CONSOLE_STATIC_TARGET "TestImpact.Frontend.Console.Static") + # Name of test impact framework console target set(LY_TEST_IMPACT_CONSOLE_TARGET "TestImpact.Frontend.Console") @@ -33,11 +36,8 @@ set(LY_TEST_IMPACT_SOURCE_TARGET_MAPPING_DIR "${LY_TEST_IMPACT_ARTIFACT_DIR}/Map # Directory for build target dependency/depender graphs set(LY_TEST_IMPACT_TARGET_DEPENDENCY_DIR "${LY_TEST_IMPACT_ARTIFACT_DIR}/Dependency") -# Directory for test type enumeration files -set(LY_TEST_IMPACT_TEST_TYPE_DIR "${LY_TEST_IMPACT_ARTIFACT_DIR}/TestType") - # Master test enumeration file for all test types -set(LY_TEST_IMPACT_TEST_TYPE_FILE "${LY_TEST_IMPACT_TEST_TYPE_DIR}/All.tests") +set(LY_TEST_IMPACT_TEST_TYPE_FILE "${LY_TEST_IMPACT_ARTIFACT_DIR}/TestType/All.tests") #! ly_test_impact_rebase_file_to_repo_root: rebases the relative and/or absolute path to be relative to repo root directory and places the resulting path in quotes. # @@ -93,14 +93,13 @@ function(ly_test_impact_get_test_launch_method TARGET_NAME LAUNCH_METHOD) endif() endfunction() -#! ly_test_impact_extract_google_test: explodes a composite google test string into namespace, test and suite components. +#! ly_test_impact_extract_google_test_name: extracts the google test name from the composite 'namespace::test_name' string # # \arg:COMPOSITE_TEST test in the form 'namespace::test' -# \arg:TEST_NAMESPACE namespace for the test # \arg:TEST_NAME name of test function(ly_test_impact_extract_google_test COMPOSITE_TEST TEST_NAMESPACE TEST_NAME) get_property(test_components GLOBAL PROPERTY LY_ALL_TESTS_${COMPOSITE_TEST}_TEST_NAME) - # Namespace and test are mandetiry + # Namespace and test are mandetory string(REPLACE "::" ";" test_components ${test_components}) list(LENGTH test_components num_test_components) if(num_test_components LESS 2) @@ -113,37 +112,74 @@ function(ly_test_impact_extract_google_test COMPOSITE_TEST TEST_NAMESPACE TEST_N set(${TEST_NAME} ${test_name} PARENT_SCOPE) endfunction() -#! ly_test_impact_extract_python_test: explodes a composite python test string into filename, namespace, test and suite components. +#! ly_test_impact_extract_python_test_name: extracts the python test name from the composite 'namespace::test_name' string # # \arg:COMPOSITE_TEST test in form 'namespace::test' or 'test' -# \arg:TEST_NAMESPACE namespace for the test (optional) # \arg:TEST_NAME name of test -# \arg:TEST_FILE the Python script path for this test -function(ly_test_impact_extract_python_test COMPOSITE_TEST TEST_NAMESPACE TEST_NAME TEST_FILE) +function(ly_test_impact_extract_python_test COMPOSITE_TEST TEST_NAME) get_property(test_components GLOBAL PROPERTY LY_ALL_TESTS_${COMPOSITE_TEST}_TEST_NAME) - get_property(test_file GLOBAL PROPERTY LY_ALL_TESTS_${COMPOSITE_TEST}_SCRIPT_PATH) # namespace is optional, in which case this component will be simply the test name string(REPLACE "::" ";" test_components ${test_components}) list(LENGTH test_components num_test_components) if(num_test_components GREATER 1) - list(GET test_components 0 test_namespace) list(GET test_components 1 test_name) else() - set(test_namespace "") + set(test_name ${test_components}) + endif() + + set(${TEST_NAME} ${test_name} PARENT_SCOPE) +endfunction() + +#! ly_test_impact_extract_google_test_params: extracts the google test name and command parameters. +# +# \arg:COMPOSITE_TEST test in the form 'namespace::test' +# \arg:TEST_NAMESPACE namespace for the test +# \arg:TEST_NAME name of test +# \arg:TEST_NAME optional command arguments to run the test +# \arg:TEST_NAME test timeout value +function(ly_test_impact_extract_google_test_params COMPOSITE_TEST TEST_NAME TEST_COMMAND) + get_property(test_command GLOBAL PROPERTY LY_ALL_TESTS_${COMPOSITE_TEST}_TEST_COMMAND) + # Namespace and test are mandetory + string(REPLACE "::" ";" test_components ${COMPOSITE_TEST}) + list(LENGTH test_components num_test_components) + if(num_test_components LESS 2) + message(FATAL_ERROR "The test ${test_components} appears to have been specified without a namespace, i.e.:\ly_add_googletest/benchmark(NAME ${test_components})\nInstead of (perhaps):\ly_add_googletest/benchmark(NAME Gem::${test_components})\nPlease add the missing namespace before proceeding.") + endif() + + list(GET test_components 0 test_namespace) + list(GET test_components 1 test_name) + set(${TEST_NAMESPACE} ${test_namespace} PARENT_SCOPE) + set(${TEST_NAME} ${test_name} PARENT_SCOPE) + set(${TEST_COMMAND} ${test_command} PARENT_SCOPE) +endfunction() + +#! ly_test_impact_extract_python_test_params: extracts the python test name and relative script path parameters. +# +# \arg:COMPOSITE_TEST test in form 'namespace::test' or 'test' +# \arg:TEST_NAME name of test +# \arg:SCRIPT_PATH name of test +function(ly_test_impact_extract_python_test_params COMPOSITE_TEST TEST_NAME SCRIPT_PATH) + get_property(script_path GLOBAL PROPERTY LY_ALL_TESTS_${COMPOSITE_TEST}_SCRIPT_PATH) + + # namespace is optional, in which case this component will be simply the test name + string(REPLACE "::" ";" test_components ${COMPOSITE_TEST}) + list(LENGTH test_components num_test_components) + if(num_test_components GREATER 1) + list(GET test_components 1 test_name) + else() set(test_name ${test_components}) endif() # Get python script path relative to repo root ly_test_impact_rebase_file_to_repo_root( - ${test_file} - test_file + ${script_path} + script_path ${LY_ROOT_FOLDER} ) - set(${TEST_NAMESPACE} ${test_namespace} PARENT_SCOPE) set(${TEST_NAME} ${test_name} PARENT_SCOPE) - set(${TEST_FILE} ${test_file} PARENT_SCOPE) + set(${SCRIPT_PATH} ${script_path} PARENT_SCOPE) endfunction() #! ly_test_impact_write_test_enumeration_file: exports the master test lists to file. @@ -164,25 +200,26 @@ function(ly_test_impact_write_test_enumeration_file TEST_ENUMERATION_TEMPLATE_FI message(TRACE "Parsing ${test}") get_property(test_type GLOBAL PROPERTY LY_ALL_TESTS_${test}_TEST_LIBRARY) get_property(test_suite GLOBAL PROPERTY LY_ALL_TESTS_${test}_TEST_SUITE) + get_property(test_timeout GLOBAL PROPERTY LY_ALL_TESTS_${test}_TEST_TIMEOUT) if("${test_type}" STREQUAL "pytest") # Python tests - ly_test_impact_extract_python_test(${test} test_namespace test_name test_file) - list(APPEND python_tests " { \"name\": \"${test_name}\", \"namespace\": \"${test_namespace}\", \"suite\": \"${test_suite}\", \"path\": \"${test_file}\" }") + ly_test_impact_extract_python_test_params(${test} test_name script_path) + list(APPEND python_tests " { \"name\": \"${test_name}\", \"suite\": \"${test_suite}\", \"script\": \"${script_path}\", \"timeout\":${test_timeout} }") elseif("${test_type}" STREQUAL "pytest_editor") # Python editor tests - ly_test_impact_extract_python_test(${test} test_namespace test_name test_file) - list(APPEND python_editor_tests " { \"name\": \"${test_name}\", \"namespace\": \"${test_namespace}\", \"suite\": \"${test_suite}\", \"path\": \"${test_file}\" }") + ly_test_impact_extract_python_test_params(${test} test_name script_path) + list(APPEND python_editor_tests " { \"name\": \"${test_name}\", \"suite\": \"${test_suite}\", \"script\": \"${script_path}\", \"timeout\":${test_timeout} }") elseif("${test_type}" STREQUAL "googletest") # Google tests - ly_test_impact_extract_google_test(${test} test_namespace test_name) + ly_test_impact_extract_google_test_params(${test} test_name test_command) ly_test_impact_get_test_launch_method(${test_name} launch_method) - list(APPEND google_tests " { \"name\": \"${test_name}\", \"namespace\": \"${test_namespace}\", \"suite\": \"${test_suite}\", \"launch_method\": \"${launch_method}\" }") + list(APPEND google_tests " { \"name\": \"${test_name}\", \"suite\": \"${test_suite}\", \"command\": \"${test_command}\", \"timeout\":${test_timeout}, \"launch_method\": \"${launch_method}\" }") elseif("${test_type}" STREQUAL "googlebenchmark") # Google benchmarks - ly_test_impact_extract_google_test(${test} test_namespace test_name) - list(APPEND google_benchmarks " { \"name\": \"${test_name}\", \"namespace\": \"${test_namespace}\", \"suite\": \"${test_suite}\" }") + ly_test_impact_extract_google_test_params(${test} test_name test_command) + list(APPEND google_benchmarks " { \"name\": \"${test_name}\", \"suite\": \"${test_suite}\", \"command\": \"${test_command}\", \"timeout\":${test_timeout} }") else() - message("${test} is of unknown type (TEST_LIBRARY property is empty)") + message("${test_name} is of unknown type (TEST_LIBRARY property is empty)") list(APPEND unknown_tests " { \"name\": \"${test}\" }") endif() endforeach() @@ -242,12 +279,7 @@ function(ly_test_impact_export_source_target_mappings MAPPING_TEMPLATE_FILE) endif() # Static source file mappings - get_target_property(target_type ${target} TYPE) - if("${target_type}" STREQUAL "INTERFACE_LIBRARY") - get_target_property(static_sources ${target}_HEADERS SOURCES) - else() - get_target_property(static_sources ${target} SOURCES) - endif() + get_target_property(static_sources ${target} SOURCES) # Rebase static source files to repo root ly_test_impact_rebase_files_to_repo_root( @@ -271,75 +303,52 @@ endfunction() # # \arg:CONFIG_TEMPLATE_FILE path to the runtime configuration template file # \arg:PERSISTENT_DATA_DIR path to the test impact framework persistent data directory -# \arg:RUNTIME_BIN_DIR path to repo binary ourput directory -function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE PERSISTENT_DATA_DIR RUNTIME_BIN_DIR) - set(repo_dir ${LY_ROOT_FOLDER}) +# \arg:BIN_DIR path to repo binary output directory +function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE PERSISTENT_DATA_DIR BIN_DIR) + # Platform this config file is being generated for + set(platform ${PAL_PLATFORM_NAME}) - # SparTIA instrumentation binary + # Timestamp this config file was generated at + string(TIMESTAMP timestamp "%Y-%m-%d %H:%M:%S") + + # Instrumentation binary if(NOT LY_TEST_IMPACT_INSTRUMENTATION_BIN) message(FATAL_ERROR "No test impact framework instrumentation binary was specified, please provide the path with option LY_TEST_IMPACT_INSTRUMENTATION_BIN") endif() file(TO_CMAKE_PATH ${LY_TEST_IMPACT_INSTRUMENTATION_BIN} instrumentation_bin) - - # test impact framework working dir - ly_test_impact_rebase_file_to_repo_root( - ${LY_TEST_IMPACT_WORKING_DIR} - working_dir - ${LY_ROOT_FOLDER} - ) - - # test impact framework console binary dir - ly_test_impact_rebase_file_to_repo_root( - ${RUNTIME_BIN_DIR} - runtime_bin_dir - ${LY_ROOT_FOLDER} - ) - # Test dir - ly_test_impact_rebase_file_to_repo_root( - "${PERSISTENT_DATA_DIR}/Tests" - tests_dir - ${LY_ROOT_FOLDER} - ) + # Testrunner binary + set(test_runner_bin $) + + # Repository root + set(repo_dir ${LY_ROOT_FOLDER}) - # Temp dir - ly_test_impact_rebase_file_to_repo_root( - "${LY_TEST_IMPACT_TEMP_DIR}" - temp_dir - ${LY_ROOT_FOLDER} - ) + # Test impact framework output binary dir + set(bin_dir ${BIN_DIR}) + # Temp dir + set(temp_dir "${LY_TEST_IMPACT_TEMP_DIR}") + + # Persistent dir + set(persistent_dir "${PERSISTENT_DATA_DIR}") + # Source to target mappings dir - ly_test_impact_rebase_file_to_repo_root( - "${LY_TEST_IMPACT_SOURCE_TARGET_MAPPING_DIR}" - source_target_mapping_dir - ${LY_ROOT_FOLDER} - ) + set(source_target_mapping_dir "${LY_TEST_IMPACT_SOURCE_TARGET_MAPPING_DIR}") - # Test type artifact dir - ly_test_impact_rebase_file_to_repo_root( - "${LY_TEST_IMPACT_TEST_TYPE_DIR}" - test_type_dir - ${LY_ROOT_FOLDER} - ) + # Test type artifact file + set(test_target_type_file "${LY_TEST_IMPACT_TEST_TYPE_FILE}") # Build dependency artifact dir - ly_test_impact_rebase_file_to_repo_root( - "${LY_TEST_IMPACT_TARGET_DEPENDENCY_DIR}" - target_dependency_dir - ${LY_ROOT_FOLDER} - ) + set(target_dependency_dir "${LY_TEST_IMPACT_TARGET_DEPENDENCY_DIR}") # Substitute config file template with above vars file(READ "${CONFIG_TEMPLATE_FILE}" config_file) string(CONFIGURE ${config_file} config_file) # Write out entire config contents to a file in the build directory of the test impact framework console target - string(TIMESTAMP timestamp "%Y-%m-%d %H:%M:%S") - set(header "# Test Impact Framework configuration file for Lumberyard\n# Platform: ${CMAKE_SYSTEM_NAME}\n# Build: $\n# ${timestamp}") file(GENERATE - OUTPUT "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/$.$.cfg" - CONTENT "${header}\n\n${config_file}" + OUTPUT "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/$.$.json" + CONTENT ${config_file} ) endfunction() @@ -353,7 +362,7 @@ function(ly_test_impact_post_step) set(persistent_data_dir "${LY_ROOT_FOLDER}/Tests/test_impact_framework/${CMAKE_SYSTEM_NAME}/$") # Directory for binaries built for this profile - set(runtime_bin_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$") + set(bin_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$") # Erase any existing non-persistent data to avoid getting test impact framework out of sync with current repo state file(REMOVE_RECURSE "${LY_TEST_IMPACT_WORKING_DIR}") @@ -372,7 +381,7 @@ function(ly_test_impact_post_step) ly_test_impact_write_config_file( "cmake/TestImpactFramework/ConsoleFrontendConfig.in" ${persistent_data_dir} - ${runtime_bin_dir} + ${bin_dir} ) # Copy over the graphviz options file for the build dependency graphs @@ -380,6 +389,6 @@ function(ly_test_impact_post_step) file(COPY "cmake/TestImpactFramework/CMakeGraphVizOptions.cmake" DESTINATION ${CMAKE_BINARY_DIR}) # Set the above config file as the default config file to use for the test impact framework console target - target_compile_definitions(${LY_TEST_IMPACT_CONSOLE_TARGET} PRIVATE "LY_TEST_IMPACT_DEFAULT_CONFIG_FILE=\"$.$.cfg\"") + target_compile_definitions(${LY_TEST_IMPACT_CONSOLE_STATIC_TARGET} PUBLIC "LY_TEST_IMPACT_DEFAULT_CONFIG_FILE=\"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/$.$.json\"") message(DEBUG "Test impact framework post steps complete") endfunction() \ No newline at end of file From 0647b3186f557d25b47b8756289ad15b39f1968a Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 19 May 2021 13:40:49 +0100 Subject: [PATCH 033/233] Add missing new line --- cmake/TestImpactFramework/LYTestImpactFramework.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/TestImpactFramework/LYTestImpactFramework.cmake b/cmake/TestImpactFramework/LYTestImpactFramework.cmake index 606ab0df4b..fc3ba0af44 100644 --- a/cmake/TestImpactFramework/LYTestImpactFramework.cmake +++ b/cmake/TestImpactFramework/LYTestImpactFramework.cmake @@ -391,4 +391,4 @@ function(ly_test_impact_post_step) # Set the above config file as the default config file to use for the test impact framework console target target_compile_definitions(${LY_TEST_IMPACT_CONSOLE_STATIC_TARGET} PUBLIC "LY_TEST_IMPACT_DEFAULT_CONFIG_FILE=\"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/$.$.json\"") message(DEBUG "Test impact framework post steps complete") -endfunction() \ No newline at end of file +endfunction() From abe35aad75fd4cdb6484e7bca1e2b831c9195c2c Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 19 May 2021 13:45:26 +0100 Subject: [PATCH 034/233] Add missing args to PythonBindingsExample.Tests --- Code/Tools/PythonBindingsExample/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/PythonBindingsExample/CMakeLists.txt b/Code/Tools/PythonBindingsExample/CMakeLists.txt index 3abd24b5fc..90cb718ce4 100644 --- a/Code/Tools/PythonBindingsExample/CMakeLists.txt +++ b/Code/Tools/PythonBindingsExample/CMakeLists.txt @@ -114,6 +114,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_add_googletest( NAME AZ::PythonBindingsExample.Tests - TEST_COMMAND $ + TEST_COMMAND $ --unittest ) endif() From b00906ef36a9d6d5540f814d8b1e1159ae8c928a Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 19 May 2021 13:49:35 +0100 Subject: [PATCH 035/233] Delete moved and obselete runtime files --- .../TestImpactFramework/TestImpactBitwise.h | 35 - .../TestImpactFramework/TestImpactCallback.h | 23 - .../TestImpactFrameworkPath.h | 41 - .../Artifact/Dynamic/TestImpactChangeList.h | 27 - .../Factory/TestImpactChangeListFactory.cpp | 175 ---- .../Factory/TestImpactChangeListFactory.h | 29 - .../Code/Source/TestImpactFrameworkPath.cpp | 38 - .../TestImpactChangeListFactoryTest.cpp | 288 ------ .../TestImpactInstrumentedTestRunnerTest.cpp | 822 ---------------- .../Tests/Test/TestImpactTestCoverageTest.cpp | 208 ---- .../Test/TestImpactTestEnumeratorTest.cpp | 890 ------------------ ...TestImpactTestEumerationSerializerTest.cpp | 99 -- .../Test/TestImpactTestRunSerializerTest.cpp | 99 -- .../Tests/Test/TestImpactTestRunnerTest.cpp | 516 ---------- .../Tests/TestImpactFrameworkPathTest.cpp | 137 --- .../Runtime/Code/Tests/TestImpactTestMain.cpp | 33 - 16 files changed, 3460 deletions(-) delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactBitwise.h delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactCallback.h delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFrameworkPath.h delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactChangeList.h delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.h delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactFrameworkPath.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactChangeListFactoryTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactInstrumentedTestRunnerTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestCoverageTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEnumeratorTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEumerationSerializerTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunSerializerTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunnerTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactFrameworkPathTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestMain.cpp diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactBitwise.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactBitwise.h deleted file mode 100644 index 73278f6be6..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactBitwise.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#pragma once - -namespace TestImpact -{ - //! Convinience namespace for allowing bitwise operations on enum classes. - //! @note Any types declared in this namespace will have the bitwise operator overloads declared within. - namespace Bitwise - { - template - Flags operator|(Flags lhs, Flags rhs) - { - return static_cast( - static_cast::type>(lhs) | static_cast::type>(rhs)); - } - - template - bool IsFlagSet(Flags flags, Flags flag) - { - return static_cast( - static_cast::type>(flags) & static_cast::type>(flag)); - } - } // namespace Bitwise -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactCallback.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactCallback.h deleted file mode 100644 index a8c9542b47..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactCallback.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#pragma once - -namespace TestImpact -{ - //! Generic callback result used by test impact systems. - enum class CallbackResult : bool - { - Continue, - Abort - }; -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFrameworkPath.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFrameworkPath.h deleted file mode 100644 index affab6daa7..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFrameworkPath.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#pragma once - -#include - -namespace TestImpact -{ - //! Wrapper for OS paths relative to a specified parent path. - //! @note Mimics path semantics only, makes no guarantees about validity. - class FrameworkPath - { - public: - FrameworkPath() = default; - //! Creates a path with no parent. - explicit FrameworkPath(const AZ::IO::Path& absolutePath); - //! Creates a path with an absolute path and path relative to the specified parent path. - explicit FrameworkPath(const AZ::IO::Path& absolutePath, const FrameworkPath& relativeTo); - - //! Retrieves the absolute path. - const AZ::IO::Path& Absolute() const; - //! Retrieves the path relative to the specified parent path. - const AZ::IO::Path& Relative() const; - - private: - //! The absolute path value. - AZ::IO::Path m_absolutePath; - //! The path value relative to the specified parent path. - AZ::IO::Path m_relativePath; - }; -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactChangeList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactChangeList.h deleted file mode 100644 index 6d966385ac..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactChangeList.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#pragma once - -#include -#include - -namespace TestImpact -{ - //! Artifact produced by the unified diff parsing process representing the file CRUD operations of a given diff. - struct ChangeList - { - AZStd::vector m_createdFiles; - AZStd::vector m_updatedFiles; - AZStd::vector m_deletedFiles; - }; -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.cpp deleted file mode 100644 index 4b2a33355b..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include - -#include - -namespace TestImpact -{ - namespace Utils - { - template - auto split(C&& str, const AZStd::string& delimiter) - { - AZStd::vector strings; - - for (auto p = str.data(), end = p + str.length(); p != end; p += ((p == end) ? 0 : delimiter.length())) - { - const auto pre = p; - p = AZStd::search(pre, end, delimiter.cbegin(), delimiter.cend()); - - if (p != pre) - { - strings.emplace_back(pre, p - pre); - } - } - - return strings; - } - } // namespace Utils - - namespace UnifiedDiff - { - class UnifiedDiffParser - { - public: - ChangeList Parse(const AZStd::string& unifiedDiff); - - private: - AZStd::optional GetTargetFile(const AZStd::string_view& targetFile); - ChangeList GenerateChangelist( - const AZStd::vector>& src, - const AZStd::vector>& dst); - - const AZStd::string m_srcFilePrefix = "--- "; - const AZStd::string m_dstFilePrefix = "+++ "; - const AZStd::string m_gitTargetPrefix = "b/"; - const AZStd::string m_perforceTargetPrefix = "/b/"; - const AZStd::string m_renameFromPrefix = "rename from "; - const AZStd::string m_renameToPrefix = "rename to "; - const AZStd::string m_nullFile = "/dev/null"; - bool m_hasGitHeader = false; - }; - - AZStd::optional UnifiedDiffParser::GetTargetFile(const AZStd::string_view& targetFile) - { - size_t startIndex = 0; - - if (targetFile.starts_with(m_renameFromPrefix)) - { - startIndex = m_renameFromPrefix.length(); - } - else if (targetFile.starts_with(m_renameToPrefix)) - { - startIndex = m_renameToPrefix.length(); - } - else if (targetFile.find(m_nullFile) != AZStd::string::npos) - { - return AZStd::nullopt; - } - else - { - startIndex = m_hasGitHeader ? m_gitTargetPrefix.size() + m_dstFilePrefix.size() - : m_perforceTargetPrefix.size() + m_dstFilePrefix.size(); - } - - const auto endIndex = targetFile.find('\t'); - - if (endIndex != AZStd::string::npos) - { - return targetFile.substr(startIndex, endIndex - startIndex); - } - - return targetFile.substr(startIndex); - } - - ChangeList UnifiedDiffParser::GenerateChangelist( - const AZStd::vector>& src, const AZStd::vector>& dst) - { - AZ_TestImpact_Eval(src.size() == dst.size(), ArtifactException, "Change list source and destination file count mismatch"); - ChangeList changelist; - - for (size_t i = 0; i < src.size(); i++) - { - if (!src[i].has_value()) - { - changelist.m_createdFiles.emplace_back(dst[i].value()); - } - else if (!dst[i].has_value()) - { - changelist.m_deletedFiles.emplace_back(src[i].value()); - } - else if (src[i] != dst[i]) - { - changelist.m_deletedFiles.emplace_back(src[i].value()); - changelist.m_createdFiles.emplace_back(dst[i].value()); - } - else - { - changelist.m_updatedFiles.emplace_back(src[i].value()); - } - } - - return changelist; - } - - ChangeList UnifiedDiffParser::Parse(const AZStd::string& unifiedDiff) - { - const AZStd::string GitHeader = "diff --git"; - const auto lines = Utils::split(unifiedDiff, "\n"); - - AZStd::vector> src; - AZStd::vector> dst; - - for (const auto& line : lines) - { - if (line.starts_with(GitHeader)) - { - m_hasGitHeader = true; - } - else if (line.starts_with(m_srcFilePrefix)) - { - src.emplace_back(GetTargetFile(line)); - } - else if (line.starts_with(m_dstFilePrefix)) - { - dst.emplace_back(GetTargetFile(line)); - } - else if (line.starts_with(m_renameFromPrefix)) - { - src.emplace_back(GetTargetFile(line)); - } - else if (line.starts_with(m_renameToPrefix)) - { - dst.emplace_back(GetTargetFile(line)); - } - } - - return GenerateChangelist(src, dst); - } - - ChangeList ChangeListFactory(const AZStd::string& unifiedDiff) - { - AZ_TestImpact_Eval(!unifiedDiff.empty(), ArtifactException, "Unified diff is empty"); - UnifiedDiffParser diff; - ChangeList changeList = diff.Parse(unifiedDiff); - AZ_TestImpact_Eval( - !changeList.m_createdFiles.empty() || - !changeList.m_updatedFiles.empty() || - !changeList.m_deletedFiles.empty(), - ArtifactException, "The unified diff contained no changes"); - return changeList; - } - } // namespace UnifiedDiff -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.h deleted file mode 100644 index 82074a66ef..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactChangeListFactory.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#pragma once - -#include - -#include -#include - -namespace TestImpact -{ - namespace UnifiedDiff - { - //! Constructs a change list artifact from the specified unified diff data. - //! @param unifiedDiffData The raw change list data in unified diff format. - //! @return The constructed change list artifact. - ChangeList ChangeListFactory(const AZStd::string& unifiedDiffData); - } // namespace UnifiedDiff -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactFrameworkPath.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactFrameworkPath.cpp deleted file mode 100644 index 6b5d671776..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactFrameworkPath.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -namespace TestImpact -{ - FrameworkPath::FrameworkPath(const AZ::IO::Path& absolutePath) - { - m_absolutePath = AZ::IO::Path(absolutePath).MakePreferred(); - m_relativePath = m_absolutePath.LexicallyRelative(m_absolutePath); - } - - FrameworkPath::FrameworkPath(const AZ::IO::Path& absolutePath, const FrameworkPath& relativeTo) - { - m_absolutePath = AZ::IO::Path(absolutePath).MakePreferred(); - m_relativePath = m_absolutePath.LexicallyRelative(relativeTo.Absolute()); - } - - const AZ::IO::Path& FrameworkPath::Absolute() const - { - return m_absolutePath; - } - - const AZ::IO::Path& FrameworkPath::Relative() const - { - return m_relativePath; - } -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactChangeListFactoryTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactChangeListFactoryTest.cpp deleted file mode 100644 index aeb8f118a4..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactChangeListFactoryTest.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include - -#include -#include - -namespace UnitTest -{ - TEST(ChangeListFactoryTest, NoRawData_ExpectArtifactException) - { - // Given an empty unified diff string - const AZStd::string unifiedDiff; - - try - { - // When attempting to construct the change list - const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(ChangeListFactoryTest, NoChanges_ExpectArtifactException) - { - // Given a unified diff string with no changes - const AZStd::string unifiedDiff = "On this day in 1738 absolutely nothing happened"; - - try - { - // When attempting to construct the change list - const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(ChangeListFactoryTest, CreateOnly_ExpectValidChangeListWithFileCreateOperations) - { - // Given a unified diff with only one file creation and no file updates or deletions - const AZStd::string unifiedDiff = - "From f642a2f698452fc18484758b0046132415f09467 Mon Sep 17 00:00:00 2001\n" - "From: user \n" - "Date: Sat, 13 Mar 2021 22:58:07 +0000\n" - "Subject: Test\n" - "\n" - "---\n" - " New.txt | 1 +\n" - " create mode 100644 New.txt\n" - "diff --git a/New.txt b/New.txt\n" - "new file mode 100644\n" - "index 0000000..30d74d2\n" - "--- /dev/null\n" - "+++ b/New.txt\n" - "@@ -0,0 +1 @@\n" - "+test\n" - "\\ No newline at end of file\n" - "-- \n" - "2.30.0.windows.2\n" - "\n" - "\n"; - - // When attempting to construct the change list - const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff); - - // Expect the change list to contain the 1 created file - EXPECT_EQ(changeList.m_createdFiles.size(), 1); - EXPECT_TRUE( - AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "New.txt") != changeList.m_createdFiles.end()); - - // Expect the change list to contain no updated files - EXPECT_TRUE(changeList.m_updatedFiles.empty()); - - // Expect the change list to contain no deleted files - EXPECT_TRUE(changeList.m_deletedFiles.empty()); - } - - TEST(ChangeListFactoryTest, UpdateOnly_ExpectValidChangeListWithFileUpdateOperations) - { - // Given a unified diff with only one file update and no file creations or deletions - const AZStd::string unifiedDiff = - "From f642a2f698452fc18484758b0046132415f09467 Mon Sep 17 00:00:00 2001\n" - "From: user \n" - "Date: Sat, 13 Mar 2021 22:58:07 +0000\n" - "Subject: Test\n" - "\n" - "---\n" - " A.txt | 2 +-\n" - "diff --git a/A.txt b/A.txt\n" - "index 7c4a013..e132db2 100644\n" - "--- a/A.txt\n" - "+++ b/A.txt\n" - "@@ -1 +1 @@\n" - "-aaa\n" - "\\ No newline at end of file\n" - "+zzz\n" - "\\ No newline at end of file\n" - "-- \n" - "2.30.0.windows.2\n" - "\n" - "\n"; - - // When attempting to construct the change list - const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff); - - // Expect the change list to contain no created files - EXPECT_TRUE(changeList.m_createdFiles.empty()); - - // Expect the change list to contain one updated file - EXPECT_EQ(changeList.m_updatedFiles.size(), 1); - EXPECT_TRUE( - AZStd::find(changeList.m_updatedFiles.begin(), changeList.m_updatedFiles.end(), "A.txt") != changeList.m_updatedFiles.end()); - - // Expect the change list to contain no deleted files - EXPECT_TRUE(changeList.m_deletedFiles.empty()); - } - - TEST(ChangeListFactoryTest, DeleteOnly_ExpectValidChangeListWithFileDeleteOperations) - { - // Given a unified diff with only one file deletion and no file creations or updates - const AZStd::string unifiedDiff = - "From f642a2f698452fc18484758b0046132415f09467 Mon Sep 17 00:00:00 2001\n" - "From: user \n" - "Date: Sat, 13 Mar 2021 22:58:07 +0000\n" - "Subject: Test\n" - "\n" - "---\n" - " B.txt | 1 -\n" - " delete mode 100644 B.txt\n" - "diff --git a/B.txt b/B.txt\n" - "deleted file mode 100644\n" - "index 01f02e3..0000000\n" - "--- a/B.txt\n" - "+++ /dev/null\n" - "@@ -1 +0,0 @@\n" - "-bbb\n" - "\\ No newline at end of file\n" - "-- \n" - "2.30.0.windows.2\n" - "\n" - "\n"; - - // When attempting to construct the change list - const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff); - - // Expect the change list to contain no created files - EXPECT_TRUE(changeList.m_createdFiles.empty()); - - // Expect the change list to contain no updated files - EXPECT_TRUE(changeList.m_updatedFiles.empty()); - - // Expect the change list to contain one deleted file - EXPECT_EQ(changeList.m_deletedFiles.size(), 1); - EXPECT_TRUE( - AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "B.txt") != changeList.m_deletedFiles.end()); - } - - TEST(ChangeListFactoryTest, ParseUnifiedDiffWithAllPossibleOperations_ExpectChangeListMatchingOperations) - { - // Given a unified diff with created files, updated files, deleted files, renamed files and moved files - const AZStd::string unifiedDiff = - "From f642a2f698452fc18484758b0046132415f09467 Mon Sep 17 00:00:00 2001\n" - "From: user \n" - "Date: Sat, 13 Mar 2021 22:58:07 +0000\n" - "Subject: Test\n" - "\n" - "---\n" - " A.txt | 2 +-\n" - " B.txt | 1 -\n" - " D.txt => Foo/D.txt | 0\n" - " E.txt => Foo/Y.txt | 0\n" - " New.txt | 1 +\n" - " C.txt => X.txt | 0\n" - " 6 files changed, 2 insertions(+), 2 deletions(-)\n" - " delete mode 100644 B.txt\n" - " rename D.txt => Foo/D.txt (100%)\n" - " rename E.txt => Foo/Y.txt (100%)\n" - " create mode 100644 New.txt\n" - " rename C.txt => X.txt (100%)\n" - "\n" - "diff --git a/A.txt b/A.txt\n" - "index 7c4a013..e132db2 100644\n" - "--- a/A.txt\n" - "+++ b/A.txt\n" - "@@ -1 +1 @@\n" - "-aaa\n" - "\\ No newline at end of file\n" - "+zzz\n" - "\\ No newline at end of file\n" - "diff --git a/B.txt b/B.txt\n" - "deleted file mode 100644\n" - "index 01f02e3..0000000\n" - "--- a/B.txt\n" - "+++ /dev/null\n" - "@@ -1 +0,0 @@\n" - "-bbb\n" - "\\ No newline at end of file\n" - "diff --git a/D.txt b/Foo/D.txt\n" - "similarity index 100%\n" - "rename from D.txt\n" - "rename to Foo/D.txt\n" - "diff --git a/E.txt b/Foo/Y.txt\n" - "similarity index 100%\n" - "rename from E.txt\n" - "rename to Foo/Y.txt\n" - "diff --git a/New.txt b/New.txt\n" - "new file mode 100644\n" - "index 0000000..30d74d2\n" - "--- /dev/null\n" - "+++ b/New.txt\n" - "@@ -0,0 +1 @@\n" - "+test\n" - "\\ No newline at end of file\n" - "diff --git a/C.txt b/X.txt\n" - "similarity index 100%\n" - "rename from C.txt\n" - "rename to X.txt\n" - "-- \n" - "2.30.0.windows.2\n" - "\n" - "\n"; - - // When attempting to construct the change list - const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff); - - // Expect the change list to contain the 4 created files - EXPECT_EQ(changeList.m_createdFiles.size(), 4); - EXPECT_TRUE( - AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "Foo/D.txt") != - changeList.m_createdFiles.end()); - EXPECT_TRUE( - AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "Foo/Y.txt") != - changeList.m_createdFiles.end()); - EXPECT_TRUE( - AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "X.txt") != changeList.m_createdFiles.end()); - EXPECT_TRUE( - AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "New.txt") != changeList.m_createdFiles.end()); - - // Expect the change list to contain the 1 updated file - EXPECT_EQ(changeList.m_updatedFiles.size(), 1); - EXPECT_TRUE( - AZStd::find(changeList.m_updatedFiles.begin(), changeList.m_updatedFiles.end(), "A.txt") != changeList.m_updatedFiles.end()); - - // Expect the change list to contain the 4 deleted files - EXPECT_EQ(changeList.m_deletedFiles.size(), 4); - EXPECT_TRUE( - AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "B.txt") != changeList.m_deletedFiles.end()); - EXPECT_TRUE( - AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "D.txt") != changeList.m_deletedFiles.end()); - EXPECT_TRUE( - AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "E.txt") != changeList.m_deletedFiles.end()); - EXPECT_TRUE( - AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "C.txt") != changeList.m_deletedFiles.end()); - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactInstrumentedTestRunnerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactInstrumentedTestRunnerTest.cpp deleted file mode 100644 index 897eda012c..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactInstrumentedTestRunnerTest.cpp +++ /dev/null @@ -1,822 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace UnitTest -{ - using JobExceptionPolicy = TestImpact::InstrumentedTestRunner::JobExceptionPolicy; - using CoverageExceptionPolicy = TestImpact::InstrumentedTestRunner::CoverageExceptionPolicy; - - struct TargetPaths - { - AZ::IO::Path m_targetBinary; - AZ::IO::Path m_testRunArtifact; - AZ::IO::Path m_testCoverageArtifact; - }; - - // Indices for looking up job command arguments for the different coverage levels - enum CoverageLevel : uint8_t - { - LineLevel = 0, - SourceLevel - }; - - // Get the job command for an instrumented test run - AZStd::string GetRunCommandForTarget(const TargetPaths& testTarget, CoverageLevel coverageLevel, const char* sourcesFilter) - { - AZStd::string args = AZStd::string::format( - "%s " // 1. Instrumented test runner - "--coverage_level %s " // 2. Coverage level - "--export_type cobertura:\"%s\" " // 3. Test coverage artifact path - "--modules \"%s\" " // 4. Modules path - "--excluded_modules \"%s\" " // 5. Exclude modules - "--sources \"%s\" -- " // 6. Sources path - "\"%s\" " // 7. Test runner binary - "\"%s\" " // 8. Test target bin - "AzRunUnitTests " - "--gtest_output=xml:%s", // 9. Test run result artifact - - LY_TEST_IMPACT_INSTRUMENTATION_BIN, // 1. - (coverageLevel == CoverageLevel::LineLevel ? "line" : "source"), // 2. - testTarget.m_testCoverageArtifact.c_str(), // 3. - LY_TEST_IMPACT_MODULES_DIR, // 4. - LY_TEST_IMPACT_AZ_TESTRUNNER_BIN, // 5. - sourcesFilter, // 6. - LY_TEST_IMPACT_AZ_TESTRUNNER_BIN, // 7. - testTarget.m_targetBinary.c_str(), // 8. - testTarget.m_testRunArtifact.c_str() // 9. - ); - - // OpenCppCoverage doesn't support forward slash directory separators so replace all with escaped backslashes - return AZStd::regex_replace(args, AZStd::regex("/"), "\\"); - } - - // Get the job command for an instrumented test run with valid source filters to produce coverage artifact - AZStd::string GetRunCommandForTargetWithSources(const TargetPaths& testTarget, CoverageLevel coverageLevel) - { - return GetRunCommandForTarget(testTarget, coverageLevel, LY_TEST_IMPACT_COVERAGE_SOURCES_DIR); - } - - // Get the job command for an instrumented test run without valid source filters to produce empty coverage artifact - AZStd::string GetRunCommandForTargetWithoutSources(const TargetPaths& testTarget, CoverageLevel coverageLevel) - { - return GetRunCommandForTarget(testTarget, coverageLevel, "C:\\No\\Sources\\Here\\At\\All\\Ever\\Ever\\Ever"); - } - - class InstrumentedTestRunnerFixture - : public AllocatorsTestFixture - { - public: - void SetUp() override; - void TearDown() override; - - protected: - using JobInfo = TestImpact::InstrumentedTestRunner::JobInfo; - using JobData = TestImpact::InstrumentedTestRunner::JobData; - - AZStd::vector m_jobInfos; - AZStd::unique_ptr m_testRunner; - AZStd::vector> m_testTargetJobArgs; - AZStd::vector m_testTargetPaths; - AZStd::vector m_expectedTestTargetRuns; - AZStd::vector> m_expectedTestTargetCoverages; - AZStd::vector m_expectedTestTargetResult; - size_t m_maxConcurrency = 0; - CoverageLevel m_coverageLevel = CoverageLevel::LineLevel; - inline static AZ::u32 s_uniqueTestCaseId = 0; // Unique id for each test case to be used as the suffix for the written files - }; - - void InstrumentedTestRunnerFixture::SetUp() - { - AllocatorsTestFixture::SetUp(); - - // Suffix each artifact file with the unique test case id to ensure that there are no possible race conditions between cleaning - // up the artifact files after each test case and those artifact files being written and read again in future test cases - const AZStd::string fileExtension = AZStd::string::format(".%u.xml", s_uniqueTestCaseId++); - - const AZStd::string runPath = AZStd::string(LY_TEST_IMPACT_TEST_TARGET_RESULTS_DIR) + "/%s.Run" + fileExtension; - const AZStd::string coveragePath = AZStd::string(LY_TEST_IMPACT_TEST_TARGET_COVERAGE_DIR) + "/%s.Coverage" + fileExtension; - - // TestTargetA - m_testTargetPaths.emplace_back(TargetPaths{ - LY_TEST_IMPACT_TEST_TARGET_A_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME), - AZStd::string::format(coveragePath.c_str(), LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME)}); - - // TestTargetB - m_testTargetPaths.emplace_back(TargetPaths{ - LY_TEST_IMPACT_TEST_TARGET_B_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME), - AZStd::string::format(coveragePath.c_str(), LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME)}); - - // TestTargetC - m_testTargetPaths.emplace_back(TargetPaths{ - LY_TEST_IMPACT_TEST_TARGET_C_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME), - AZStd::string::format(coveragePath.c_str(), LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME)}); - - // TestTargetD - m_testTargetPaths.emplace_back(TargetPaths{ - LY_TEST_IMPACT_TEST_TARGET_D_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME), - AZStd::string::format(coveragePath.c_str(), LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME)}); - - m_expectedTestTargetRuns.emplace_back(GetTestTargetATestRunSuites(), AZStd::chrono::milliseconds{500}); // TestTargetA - m_expectedTestTargetRuns.emplace_back(GetTestTargetBTestRunSuites(), AZStd::chrono::milliseconds{500}); // TestTargetB - m_expectedTestTargetRuns.emplace_back(GetTestTargetCTestRunSuites(), AZStd::chrono::milliseconds{500}); // TestTargetC - m_expectedTestTargetRuns.emplace_back(GetTestTargetDTestRunSuites(), AZStd::chrono::milliseconds{500}); // TestTargetD - - // TestTargetA - m_expectedTestTargetCoverages.emplace_back(AZStd::array{ - TestImpact::TestCoverage(GetTestTargetALineModuleCoverages()), - TestImpact::TestCoverage(GetTestTargetASourceModuleCoverages())}); - - // TestTargetB - m_expectedTestTargetCoverages.emplace_back(AZStd::array{ - TestImpact::TestCoverage(GetTestTargetBLineModuleCoverages()), - TestImpact::TestCoverage(GetTestTargetBSourceModuleCoverages())}); - - // TestTargetC - m_expectedTestTargetCoverages.emplace_back(AZStd::array{ - TestImpact::TestCoverage(GetTestTargetCLineModuleCoverages()), - TestImpact::TestCoverage(GetTestTargetCSourceModuleCoverages())}); - - // TestTargetD - m_expectedTestTargetCoverages.emplace_back(AZStd::array{ - TestImpact::TestCoverage(GetTestTargetDLineModuleCoverages()), - TestImpact::TestCoverage(GetTestTargetDSourceModuleCoverages())}); - - m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Failed); // TestTargetA - m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Passed); // TestTargetB - m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Passed); // TestTargetC - m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Passed); // TestTargetD - - // Generate the job command arguments for both line level and source level coverage permutations - for (const auto& testTarget : m_testTargetPaths) - { - m_testTargetJobArgs.emplace_back(AZStd::array{ - GetRunCommandForTargetWithSources(testTarget, CoverageLevel::LineLevel), - GetRunCommandForTargetWithSources(testTarget, CoverageLevel::SourceLevel)}); - } - } - - void InstrumentedTestRunnerFixture::TearDown() - { - DeleteFiles(LY_TEST_IMPACT_TEST_TARGET_COVERAGE_DIR, "*.xml"); - DeleteFiles(LY_TEST_IMPACT_TEST_TARGET_RESULTS_DIR, "*.xml"); - - AllocatorsTestFixture::TearDown(); - } - - using ConcurrencyAndCoveragePermutation = AZStd::tuple - < - size_t, // Max number of concurrent processes - CoverageLevel // Coverage level - >; - - // Fixture parameterized for different max number of concurrent jobs and coverage levels - class InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageParams - : public InstrumentedTestRunnerFixture - , public ::testing::WithParamInterface - { - public: - void SetUp() override - { - InstrumentedTestRunnerFixture::SetUp(); - auto [maxConcurrency, coverageLevel] = GetParam(); - m_maxConcurrency = maxConcurrency; - m_coverageLevel = coverageLevel; - } - }; - - using ConcurrencyAndJobExceptionPermutation = AZStd::tuple - < - size_t, // Max number of concurrent processes - CoverageLevel, // Coverage level - JobExceptionPolicy // Test job exception policy - >; - - // Fixture parameterized for different max number of concurrent jobs, coverage levels and different job exception policies - class InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndJobExceptionParams - : public InstrumentedTestRunnerFixture - , public ::testing::WithParamInterface - { - public: - void SetUp() override - { - InstrumentedTestRunnerFixture::SetUp(); - const auto& [maxConcurrency, coverageLevel, jobExceptionPolicy] = GetParam(); - m_maxConcurrency = maxConcurrency; - m_coverageLevel = coverageLevel; - m_jobExceptionPolicy = jobExceptionPolicy; - } - - protected: - JobExceptionPolicy m_jobExceptionPolicy = JobExceptionPolicy::Never; - }; - - class InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndFailedToLaunchExceptionParams - : public InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndJobExceptionParams - { - }; - - class InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndExecutedWithFailureExceptionParams - : public InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndJobExceptionParams - { - }; - - using ConcurrencyAndCoverageExceptionPermutation = AZStd::tuple - < - size_t, // Max number of concurrent processes - CoverageExceptionPolicy // Test coverage exception policy - >; - - // Fixture parameterized for different max number of concurrent jobs and different coverage exception policies - class InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndCoverageExceptionParams - : public InstrumentedTestRunnerFixture - , public ::testing::WithParamInterface - { - public: - void SetUp() override - { - InstrumentedTestRunnerFixture::SetUp(); - const auto& [maxConcurrency, coverageExceptionPolicy] = GetParam(); - m_maxConcurrency = maxConcurrency; - m_coverageExceptionPolicy = coverageExceptionPolicy; - } - - protected: - CoverageExceptionPolicy m_coverageExceptionPolicy = CoverageExceptionPolicy::Never; - }; - - namespace - { - AZStd::array MaxConcurrentRuns = {1, 2, 3, 4}; - - AZStd::array CoverageLevels = {CoverageLevel::LineLevel, CoverageLevel::SourceLevel}; - - AZStd::array FailedToLaunchExceptionPolicies = { - JobExceptionPolicy::Never, JobExceptionPolicy::OnFailedToExecute}; - - AZStd::array ExecutedWithFailureExceptionPolicies = { - JobExceptionPolicy::Never, JobExceptionPolicy::OnExecutedWithFailure}; - } // namespace - - // Validates that the specified test coverage matches the expected output - void ValidateTestTargetCoverage(const TestImpact::TestCoverage& actualResult, const TestImpact::TestCoverage& expectedResult) - { - EXPECT_TRUE(actualResult == expectedResult); - } - - // Validates that the specified test coverage is empty - void ValidateEmptyTestTargetCoverage(const TestImpact::TestCoverage& actualResult) - { - EXPECT_TRUE(actualResult.GetSourcesCovered().empty()); - EXPECT_TRUE(actualResult.GetModuleCoverages().empty()); - EXPECT_EQ(actualResult.GetNumSourcesCovered(), 0); - EXPECT_EQ(actualResult.GetNumModulesCovered(), 0); - } - - TEST_P( - InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndCoverageExceptionParams, - EmptyTestCoverages_ExpectEmptyTestCoveragesOrTestRunException) - { - // Given a test runner with no client callback or run timeout or runner timeout - m_testRunner = - AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); - - // Given a mixture of instrumented test run jobs with and without coverage sources - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - const AZStd::string args = (jobId % 2) ? GetRunCommandForTargetWithoutSources(m_testTargetPaths[jobId], m_coverageLevel) - : m_testTargetJobArgs[jobId][m_coverageLevel]; - - JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); - m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); - } - - try - { - // When the instrumented test run jobs are executed with different exception policies - const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, m_coverageExceptionPolicy, JobExceptionPolicy::Never); - - // Expect this statement to be reachable only if no exception policy for empty coverages - EXPECT_FALSE(::IsFlagSet(m_coverageExceptionPolicy, CoverageExceptionPolicy::OnEmptyCoverage)); - - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); - - if (jobId % 2) - { - // Expect jobs have a empty test coverages - ValidateEmptyTestTargetCoverage(job.GetPayload().value().second); - } - else - { - // Expect the jobs to successfully result in a test run and coverage that matches the expected test run data - ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); - } - } - } - catch ([[maybe_unused]] const TestImpact::TestRunException& e) - { - // Expect this statement to be reachable only if there is an exception policy for empty coverages - EXPECT_TRUE(::IsFlagSet(m_coverageExceptionPolicy, CoverageExceptionPolicy::OnEmptyCoverage)); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_P( - InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndFailedToLaunchExceptionParams, - InvalidCommandArgument_ExpectJobResulFailedToExecuteeOrTestJobException) - { - // Given a test runner with no client callback or run timeout or runner timeout - m_testRunner = - AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); - - // Given a mixture of instrumented test run jobs with valid and invalid command arguments - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - const AZStd::string args = (jobId % 2) ? InvalidProcessPath : m_testTargetJobArgs[jobId][m_coverageLevel]; - JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); - m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); - } - - try - { - // When the instrumented test run jobs are executed with different exception policies - const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, m_jobExceptionPolicy); - - // Expect this statement to be reachable only if no exception policy for launch failures - EXPECT_FALSE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)); - - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - if (jobId % 2) - { - // Expect invalid jobs have a job result of FailedToExecute - ValidateJobFailedToExecute(job); - } - else - { - // Expect the valid jobs to successfully result in a test run that matches the expected test run data - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); - ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); - } - } - } - catch ([[maybe_unused]] const TestImpact::TestJobException& e) - { - // Expect this statement to be reachable only if there is an exception policy for launch failures - EXPECT_TRUE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_P( - InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndExecutedWithFailureExceptionParams, - ErroneousReturnCode_ExpectJobResultExecutedWithFailureOrTestJobException) - { - // Given a test runner with no client callback or run timeout or runner timeout - m_testRunner = - AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); - - // Given a mixture of instrumented test run jobs that execute and return either successfully or with failure - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); - m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId][m_coverageLevel], AZStd::move(jobData))); - } - - try - { - // When the instrumented test run jobs are executed with different exception policies - const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, m_jobExceptionPolicy); - - // Expect this statement to be reachable only if no exception policy for jobs that return with error - EXPECT_FALSE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)); - - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - - // Expect the valid jobs to successfully result in a test run that matches the expected test run data - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); - ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); - } - } - catch ([[maybe_unused]] const TestImpact::TestJobException& e) - { - // Expect this statement to be reachable only if there is an exception policy for jobs that return with error - EXPECT_TRUE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)); - } - catch ([[maybe_unused]] const TestImpact::Exception& e) - { - FAIL(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(InstrumentedTestRunnerFixture, EmptyRunRawData_ExpectTestRunnerException) - { - // Given a test runner with no client callback, concurrency, run timeout or runner timeout - m_testRunner = - AZStd::make_unique(AZStd::nullopt, OneConcurrentProcess, AZStd::nullopt, AZStd::nullopt); - - // Given an test runner job that will return successfully but with an empty artifact string - JobData jobData("", m_testTargetPaths[TestTargetA].m_testCoverageArtifact); - m_jobInfos.emplace_back(JobInfo({TestTargetA}, m_testTargetJobArgs[TestTargetA][LineLevel], AZStd::move(jobData))); - - try - { - // When the test runner job is executed - const auto runnerJobs = - m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::TestRunException& e) - { - // Expect an runner exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(InstrumentedTestRunnerFixture, EmptyCoverageRawData_ExpectTestRunnerException) - { - // Given a test runner with no client callback, concurrency, run timeout or runner timeout - m_testRunner = - AZStd::make_unique(AZStd::nullopt, OneConcurrentProcess, AZStd::nullopt, AZStd::nullopt); - - // Given an test runner job that will return successfully but with an empty artifact string - JobData jobData(m_testTargetPaths[TestTargetA].m_testRunArtifact, ""); - m_jobInfos.emplace_back(JobInfo({TestTargetA}, m_testTargetJobArgs[TestTargetA][LineLevel], AZStd::move(jobData))); - - try - { - // When the test runner job is executed - const auto runnerJobs = - m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::TestRunException& e) - { - // Expect an runner exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(InstrumentedTestRunnerFixture, InvalidRunArtifact_ExpectArtifactException) - { - // Given a run artifact with invalid contents - WriteTextToFile("There is nothing valid here", m_testTargetPaths[TestTargetA].m_testRunArtifact); - - // Given a job command that will write the run artifact to a different location that what we will read from - TargetPaths invalidRunArtifact = m_testTargetPaths[TestTargetA]; - invalidRunArtifact.m_testRunArtifact /= ".xml"; - const AZStd::string args = GetRunCommandForTargetWithSources(invalidRunArtifact, LineLevel); - - // Given a test runner with no client callback, concurrency, run timeout or runner timeout - m_testRunner = - AZStd::make_unique(AZStd::nullopt, OneConcurrentProcess, AZStd::nullopt, AZStd::nullopt); - - // Given an test runner job that will return successfully but not produce a run artifact - JobData jobData(m_testTargetPaths[TestTargetA].m_testRunArtifact, m_testTargetPaths[TestTargetA].m_testCoverageArtifact); - m_jobInfos.emplace_back(JobInfo({TestTargetA}, args, AZStd::move(jobData))); - - try - { - // When the test runner job is executed - const auto runnerJobs = - m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an runner exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(InstrumentedTestRunnerFixture, InvalidCoverageArtifact_ExpectArtifactException) - { - // Given a coverage artifact with invalid contents - WriteTextToFile("There is nothing valid here", m_testTargetPaths[TestTargetA].m_testCoverageArtifact); - - // Given a job command that will write the coverage artifact to a different location that what we will read from - TargetPaths invalidCoverageArtifact = m_testTargetPaths[TestTargetA]; - invalidCoverageArtifact.m_testCoverageArtifact /= ".xml"; - const AZStd::string args = GetRunCommandForTargetWithSources(invalidCoverageArtifact, LineLevel); - - // Given a test runner with no client callback, concurrency, run timeout or runner timeout - m_testRunner = - AZStd::make_unique(AZStd::nullopt, OneConcurrentProcess, AZStd::nullopt, AZStd::nullopt); - - // Given an test runner job that will return successfully but not produce a run artifact - JobData jobData(m_testTargetPaths[TestTargetA].m_testRunArtifact, m_testTargetPaths[TestTargetA].m_testCoverageArtifact); - m_jobInfos.emplace_back(JobInfo({TestTargetA}, args, AZStd::move(jobData))); - - try - { - // When the test runner job is executed - const auto runnerJobs = - m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an runner exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_P(InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageParams, RunTestTargets_RunsAndCoverageMatchTestSuitesInTarget) - { - // Given a test runner with no client callback, runner timeout or runner timeout - m_testRunner = - AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); - - // Given an test runner job for each test target with no runner caching - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); - m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId][m_coverageLevel], AZStd::move(jobData))); - } - - // When the test runner jobs are executed - const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Expect each job to successfully result in a test runner that matches the expected test runner data for that test target - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); - ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); - } - } - - TEST_P( - InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageParams, - RunTestTargetsWithArbitraryJobIds_RunsAndCoverageMatchTestSuitesInTarget) - { - // Given a set of arbitrary job ids to be used for the test target jobs - enum - { - ArbitraryA = 36, - ArbitraryB = 890, - ArbitraryC = 19, - ArbitraryD = 1 - }; - - const AZStd::unordered_map sequentialToArbitrary = - { - {TestTargetA, ArbitraryA}, - {TestTargetB, ArbitraryB}, - {TestTargetC, ArbitraryC}, - {TestTargetD, ArbitraryD}, - }; - - const AZStd::unordered_map arbitraryToSequential = - { - {ArbitraryA, TestTargetA}, - {ArbitraryB, TestTargetB}, - {ArbitraryC, TestTargetC}, - {ArbitraryD, TestTargetD}, - }; - - // Given a test runner with no client callback, run timeout or runner timeout - m_testRunner = - AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); - - // Given an test run job for each test target - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); - m_jobInfos.emplace_back( - JobInfo({sequentialToArbitrary.at(jobId)}, m_testTargetJobArgs[jobId][m_coverageLevel], AZStd::move(jobData))); - } - - // When the instrumented test run jobs are executed - const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Expect each job to successfully result in a test run that matches the expected test run data for that test target - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = arbitraryToSequential.at(job.GetJobInfo().GetId().m_value); - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); - ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); - } - } - - TEST_P(InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageParams, RunTestTargetsWithCallback_RunsAndCoverageMatchTestSuitesInTarget) - { - // Given a client callback function that tracks the number of successful runs - size_t numSuccesses = 0; - const auto jobCallback = - [&numSuccesses]([[maybe_unused]] const TestImpact::InstrumentedTestRunner::JobInfo& jobInfo, const TestImpact::JobMeta& meta) - { - if (meta.m_result == TestImpact::JobResult::ExecutedWithSuccess) - { - numSuccesses++; - } - }; - - // Given a test runner with no run timeout or runner timeout - m_testRunner = - AZStd::make_unique(jobCallback, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); - - // Given an test run job for each test target - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); - m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId][m_coverageLevel], AZStd::move(jobData))); - } - - // When the instrumented test run jobs are executed - const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Expect the number of successful runs tracked in the callback to match the number of test targets run with no failures - EXPECT_EQ( - numSuccesses, - AZStd::count_if(m_expectedTestTargetResult.begin(), m_expectedTestTargetResult.end(), [](TestImpact::TestRunResult result) { - return result == TestImpact::TestRunResult::Passed; - })); - - // Expect each job to successfully result in a test run that matches the expected test run data for that test target - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); - ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); - } - } - - TEST_P(InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageParams, JobRunnerTimeout_InFlightJobsTimeoutAndQueuedJobsUnlaunched) - { - // Given a test runner with no client callback or runner timeout and 2 second run timeout - m_testRunner = AZStd::make_unique( - AZStd::nullopt, m_maxConcurrency, AZStd::chrono::seconds(2), AZStd::nullopt); - - // Given an test run job for each test target where half will sleep indefinitely - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); - const AZStd::string args = (jobId % 2) - ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, LongSleep).c_str()) - : m_testTargetJobArgs[jobId][m_coverageLevel]; - m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); - } - - // When the instrumented test run jobs are executed - const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Expect half the jobs to successfully result in a test run that matches the expected test run data for that test target - // with the other half having timed out - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - if (jobId % 2) - { - ValidateJobTimeout(job); - } - else - { - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); - ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); - } - } - } - - TEST_F(InstrumentedTestRunnerFixture, JobTimeout_InFlightJobTimeoutAndQueuedJobsUnlaunched) - { - // Given a test runner with no client callback or run timeout and a 5 second runner timeout - m_testRunner = AZStd::make_unique( - AZStd::nullopt, FourConcurrentProcesses, AZStd::nullopt, AZStd::chrono::seconds(5)); - - // Given an test run job for each test target where half will sleep indefinitely - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].m_testRunArtifact, m_testTargetPaths[jobId].m_testCoverageArtifact); - const AZStd::string args = (jobId % 2) - ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, LongSleep).c_str()) - : m_testTargetJobArgs[jobId][m_coverageLevel]; - m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); - } - - // When the instrumented test run jobs are executed - const auto runnerJobs = m_testRunner->RunInstrumentedTests(m_jobInfos, CoverageExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Expect half the jobs to successfully result in a test run that matches the expected test run data for that test target - // with the other half having timed out - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - if (jobId % 2) - { - ValidateJobTimeout(job); - } - else - { - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value().first, m_expectedTestTargetRuns[jobId]); - ValidateTestTargetCoverage(job.GetPayload().value().second, m_expectedTestTargetCoverages[jobId][m_coverageLevel]); - } - } - } - - INSTANTIATE_TEST_CASE_P( - , - InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndCoverageExceptionParams, - ::testing::Combine( - ::testing::ValuesIn(MaxConcurrentRuns), - ::testing::Values(CoverageExceptionPolicy::Never, CoverageExceptionPolicy::OnEmptyCoverage))); - - INSTANTIATE_TEST_CASE_P( - , - InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndFailedToLaunchExceptionParams, - ::testing::Combine( - ::testing::ValuesIn(MaxConcurrentRuns), ::testing::ValuesIn(CoverageLevels), - ::testing::ValuesIn(FailedToLaunchExceptionPolicies))); - - INSTANTIATE_TEST_CASE_P( - , - InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageAndExecutedWithFailureExceptionParams, - ::testing::Combine( - ::testing::ValuesIn(MaxConcurrentRuns), ::testing::ValuesIn(CoverageLevels), - ::testing::ValuesIn(ExecutedWithFailureExceptionPolicies))); - - INSTANTIATE_TEST_CASE_P( - , - InstrumentedTestRunnerFixtureWithConcurrencyAndCoverageParams, - ::testing::Combine(::testing::ValuesIn(MaxConcurrentRuns), ::testing::ValuesIn(CoverageLevels))); -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestCoverageTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestCoverageTest.cpp deleted file mode 100644 index c1d7ba3e4b..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestCoverageTest.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include - -namespace UnitTest -{ - namespace - { - AZStd::string GenerateSourcePath(AZ::u32 index) - { - return AZStd::string::format("SourceFile%u", index); - } - - AZStd::string GenerateModulePath(AZ::u32 index) - { - return AZStd::string::format("Module%u", index); - } - - AZStd::vector GenerateLineCoverages(AZ::u32 numLines) - { - AZStd::vector lineCoverages; - for (size_t i = 0; i < numLines; i++) - { - // Fudge some superficially different but trivially checkable line coverage data - lineCoverages.emplace_back(TestImpact::LineCoverage{i, i * 2}); - } - - return lineCoverages; - } - - TestImpact::SourceCoverage GenerateSourceCoverage(AZ::u32 index, TestImpact::CoverageLevel coverageLevel) - { - TestImpact::SourceCoverage sourceCoverage; - sourceCoverage.m_path = GenerateSourcePath(index); - if (coverageLevel == TestImpact::CoverageLevel::Line) - { - sourceCoverage.m_coverage = GenerateLineCoverages(index + 1); - } - - return sourceCoverage; - } - - AZStd::vector GenerateSourceCoverages(AZ::u32 numSources, TestImpact::CoverageLevel coverageLevel) - { - AZStd::vector sourceCoverages; - for (AZ::u32 i = 0; i < numSources; i++) - { - sourceCoverages.emplace_back(GenerateSourceCoverage(i, coverageLevel)); - } - - return sourceCoverages; - } - - TestImpact::ModuleCoverage GenerateModuleCoverage(AZ::u32 index, AZ::u32 numSources, TestImpact::CoverageLevel coverageLevel) - { - TestImpact::ModuleCoverage moduleCoverage; - moduleCoverage.m_path = GenerateModulePath(index); - moduleCoverage.m_sources = GenerateSourceCoverages(numSources, coverageLevel); - return moduleCoverage; - } - - AZStd::vector GenerateModuleCoverages(AZ::u32 numModules, TestImpact::CoverageLevel coverageLevel) - { - AZStd::vector moduleCoverages; - for (AZ::u32 i = 0; i < numModules; i++) - { - // Fudge some superficially different but trivially deducible module coverage data - moduleCoverages.emplace_back(GenerateModuleCoverage(i, i + 1, coverageLevel)); - } - - return moduleCoverages; - } - } // namespace - - using CoveragePermutation = AZStd::tuple - < - unsigned, // Number of modules covered - TestImpact::CoverageLevel // Test coverage level - >; - - // Fixture parameterized for different max number of concurrent jobs - class TestCoverageFixtureWithCoverageParams - : public AllocatorsTestFixture - , public ::testing::WithParamInterface - { - public: - void SetUp() override; - - protected: - void ValidateTestCoverage(const TestImpact::TestCoverage& testCoverage); - - size_t m_numModulesCovered; - TestImpact::CoverageLevel m_coverageLevel; - }; - - void TestCoverageFixtureWithCoverageParams::SetUp() - { - AllocatorsTestFixture::SetUp(); - const auto& [numModulesCovered, coverageLevel] = GetParam(); - m_numModulesCovered = numModulesCovered; - m_coverageLevel = coverageLevel; - } - - void TestCoverageFixtureWithCoverageParams::ValidateTestCoverage(const TestImpact::TestCoverage& testCoverage) - { - // Expect the coverage level to match that which was used to generate the module coverages generated - EXPECT_EQ(testCoverage.GetCoverageLevel(), m_coverageLevel); - - // Expect the number of modules covered to match the number of module coverages generated - EXPECT_EQ(testCoverage.GetNumModulesCovered(), m_numModulesCovered); - - // Expect the number of unique sources covered to match the number of modules coverages generated - EXPECT_EQ(testCoverage.GetNumSourcesCovered(), m_numModulesCovered); - - // Expect the unique sources covered to match the procedurally generated source paths - for (size_t sourceIndex = 0; sourceIndex < testCoverage.GetNumSourcesCovered(); sourceIndex++) - { - EXPECT_EQ(testCoverage.GetSourcesCovered()[sourceIndex], GenerateSourcePath(sourceIndex)); - } - - // Expect each module covered to match that of the corresponding procedurally generated modules - for (size_t moduleIndex = 0; moduleIndex < testCoverage.GetNumModulesCovered(); moduleIndex++) - { - const TestImpact::ModuleCoverage& moduleCoverage = testCoverage.GetModuleCoverages()[moduleIndex]; - - // Expect the module path to match that of the corresponding procedurally generated module - EXPECT_EQ(moduleCoverage.m_path, GenerateModulePath(moduleIndex)); - - // Expect the module's number of sources to match that of the corresponding procedurally generated module - EXPECT_EQ(moduleCoverage.m_sources.size(), moduleIndex + 1); - - for (size_t sourceIndex = 0; sourceIndex < moduleCoverage.m_sources.size(); sourceIndex++) - { - const TestImpact::SourceCoverage& sourceCoverage = moduleCoverage.m_sources[sourceIndex]; - - // Expect the source path to match the procedurally generated source path - EXPECT_EQ(sourceCoverage.m_path, GenerateSourcePath(sourceIndex)); - - if (m_coverageLevel == TestImpact::CoverageLevel::Line) - { - // Expect there to actually be line coverage data if this coverage was procedurally generated with line data - EXPECT_FALSE(sourceCoverage.m_coverage.empty()); - - const AZStd::vector& lineCoverages = sourceCoverage.m_coverage; - - // Expect the source's number of lines to match that of the corresponding procedurally generated source - EXPECT_EQ(lineCoverages.size(), sourceIndex + 1); - - for (size_t lineIndex = 0; lineIndex < lineCoverages.size(); lineIndex++) - { - // The expected line number and hit count are deduced as follows: - // Line number: line index - // Hit count: 2x line index - EXPECT_EQ(lineCoverages[lineIndex].m_lineNumber, lineIndex); - EXPECT_EQ(lineCoverages[lineIndex].m_hitCount, lineIndex * 2); - } - } - else - { - // Do not expect there to actually be line coverage data if this coverage was not procedurally generated with line data - EXPECT_TRUE(sourceCoverage.m_coverage.empty()); - } - } - } - } - - TEST(TestCoverage, EmptyCoverage_ExpectTestRunException) - { - // When constructing a test coverage from the empty module coverages - TestImpact::TestCoverage testCoverage(AZStd::vector{}); - - // Expect the test coverage fields to be empty - EXPECT_EQ(testCoverage.GetNumModulesCovered(), 0); - EXPECT_EQ(testCoverage.GetNumSourcesCovered(), 0); - EXPECT_TRUE(testCoverage.GetModuleCoverages().empty()); - EXPECT_TRUE(testCoverage.GetSourcesCovered().empty()); - } - - TEST_P(TestCoverageFixtureWithCoverageParams, AllCoveragePermutations_ExpectTestCoverageMetaDatasToMatchPermutations) - { - // Given a procedurally generated test coverage - const TestImpact::TestCoverage testCoverage(GenerateModuleCoverages(m_numModulesCovered, m_coverageLevel)); - - // Expect the test coverage data and meta-data to match that of the rules used to procedurally generate the coverage data - ValidateTestCoverage(testCoverage); - } - - INSTANTIATE_TEST_CASE_P( - , - TestCoverageFixtureWithCoverageParams, - ::testing::Combine( - ::testing::Range(1u, 11u), - ::testing::Values(TestImpact::CoverageLevel::Line, TestImpact::CoverageLevel::Source)) - ); -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEnumeratorTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEnumeratorTest.cpp deleted file mode 100644 index 9b67eafa54..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEnumeratorTest.cpp +++ /dev/null @@ -1,890 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace UnitTest -{ - using JobExceptionPolicy = TestImpact::TestEnumerator::JobExceptionPolicy; - using CacheExceptionPolicy = TestImpact::TestEnumerator::CacheExceptionPolicy; - - // Generates the command to run the given test target through AzTestRunner and get gtest to output the enumeration file - AZStd::string GetEnumerateCommandForTarget(AZStd::pair testTarget) - { - return AZStd::string::format( - "%s %s AzRunUnitTests --gtest_list_tests --gtest_output=xml:%s", - LY_TEST_IMPACT_AZ_TESTRUNNER_BIN, - testTarget.first.c_str(), // Path to test target bin - testTarget.second.c_str()); // Path to test target gtest enumeration file - } - - class TestEnumeratorFixture - : public AllocatorsTestFixture - { - public: - void SetUp() override; - - protected: - using JobInfo = TestImpact::TestEnumerator::JobInfo; - using JobData = TestImpact::TestEnumerator::JobData; - - AZStd::vector m_jobInfos; - AZStd::unique_ptr m_testEnumerator; - AZStd::vector m_testTargetJobArgs; - AZStd::vector> m_testTargetPaths; - AZStd::vector m_expectedTestTargetEnumerations; - AZStd::vector m_cacheFiles; - size_t m_maxConcurrency = 0; - }; - - void TestEnumeratorFixture::SetUp() - { - UnitTest::AllocatorsTestFixture::SetUp(); - - DeleteFiles(LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR, "*.cache"); - DeleteFiles(LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR, "*.xml"); - - // first: path to test target bin - // second: path to test target gtest enumeration file in XML format - const AZStd::string enumPath = AZStd::string(LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR) + "/%s.Enumeration.xml"; - m_testTargetPaths.emplace_back( - LY_TEST_IMPACT_TEST_TARGET_A_BIN, AZStd::string::format(enumPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME)); - m_testTargetPaths.emplace_back( - LY_TEST_IMPACT_TEST_TARGET_B_BIN, AZStd::string::format(enumPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME)); - m_testTargetPaths.emplace_back( - LY_TEST_IMPACT_TEST_TARGET_C_BIN, AZStd::string::format(enumPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME)); - m_testTargetPaths.emplace_back( - LY_TEST_IMPACT_TEST_TARGET_D_BIN, AZStd::string::format(enumPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME)); - - m_expectedTestTargetEnumerations.emplace_back(GetTestTargetATestEnumerationSuites()); - m_expectedTestTargetEnumerations.emplace_back(GetTestTargetBTestEnumerationSuites()); - m_expectedTestTargetEnumerations.emplace_back(GetTestTargetCTestEnumerationSuites()); - m_expectedTestTargetEnumerations.emplace_back(GetTestTargetDTestEnumerationSuites()); - - // Path to enumeration file in TIAF internal JSON format - m_cacheFiles.emplace_back( - AZStd::string::format("%s/%s.cache", LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR, LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME)); - m_cacheFiles.emplace_back( - AZStd::string::format("%s/%s.cache", LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR, LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME)); - m_cacheFiles.emplace_back( - AZStd::string::format("%s/%s.cache", LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR, LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME)); - m_cacheFiles.emplace_back( - AZStd::string::format("%s/%s.cache", LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR, LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME)); - - for (const auto& testTarget : m_testTargetPaths) - { - m_testTargetJobArgs.emplace_back(GetEnumerateCommandForTarget(testTarget)); - } - } - - // Fixture parameterized for different max number of concurrent jobs - class TestEnumeratorFixtureWithConcurrencyParams - : public TestEnumeratorFixture - , public ::testing::WithParamInterface - { - public: - void SetUp() override - { - TestEnumeratorFixture::SetUp(); - m_maxConcurrency = GetParam(); - } - }; - - using ConcurrencyAndJobExceptionPermutation = AZStd::tuple - < - size_t, // Max number of concurrent processes - JobExceptionPolicy // Test job exception policy - >; - - // Fixture parameterized for different max number of concurrent jobs and different job exception policies - class TestEnumeratorFixtureWithConcurrencyAndJobExceptionParams - : public TestEnumeratorFixture - , public ::testing::WithParamInterface - { - public: - void SetUp() override - { - TestEnumeratorFixture::SetUp(); - const auto& [maxConcurrency, jobExceptionPolicy] = GetParam(); - m_maxConcurrency = maxConcurrency; - m_jobExceptionPolicy = jobExceptionPolicy; - } - - protected: - JobExceptionPolicy m_jobExceptionPolicy = JobExceptionPolicy::Never; - }; - - using ConcurrencyAndCacheExceptionPermutation = AZStd::tuple - < - size_t, // Max number of concurrent processes - CacheExceptionPolicy // Test enumeration exception policy - >; - - // Fixture parameterized for different max number of concurrent jobs and different enumeration exception policies - class TestEnumeratorFixtureWithConcurrencyAndCacheExceptionParams - : public TestEnumeratorFixture - , public ::testing::WithParamInterface - { - public: - void SetUp() override - { - TestEnumeratorFixture::SetUp(); - const auto& [maxConcurrency, cacheExceptionPolicy] = GetParam(); - m_maxConcurrency = maxConcurrency; - m_cacheExceptionPolicy = cacheExceptionPolicy; - } - - protected: - CacheExceptionPolicy m_cacheExceptionPolicy = CacheExceptionPolicy::Never; - }; - - namespace - { - AZStd::array MaxConcurrentEnumerations = {{1, 2, 3, 4}}; - - AZStd::array JobExceptionPolicies = - { - JobExceptionPolicy::Never, - JobExceptionPolicy::OnExecutedWithFailure, - JobExceptionPolicy::OnFailedToExecute - }; - - AZStd::array CacheExceptionPolicies = - { - CacheExceptionPolicy::Never, - CacheExceptionPolicy::OnCacheNotExist, - CacheExceptionPolicy::OnCacheReadFailure, - CacheExceptionPolicy::OnCacheWriteFailure - }; - } - - // Validates that the specified job successfully read from its test enumeration cache - void ValidateJobSuccessfulCacheRead(const TestImpact::TestEnumerator::Job& job) - { - EXPECT_EQ(job.GetResult(), TestImpact::JobResult::NotExecuted); - EXPECT_EQ(job.GetStartTime(), AZStd::chrono::high_resolution_clock::time_point()); - EXPECT_EQ(job.GetEndTime(), AZStd::chrono::high_resolution_clock::time_point()); - EXPECT_EQ(job.GetDuration(), AZStd::chrono::milliseconds(0)); - EXPECT_FALSE(job.GetReturnCode().has_value()); - EXPECT_TRUE(job.GetPayload().has_value()); - } - - // Validates that the specified test enumeration matched the expected output - void ValidateTestTargetEnumeration(const TestImpact::TestEnumeration& actualResult, const TestImpact::TestEnumeration& expectedResult) - { - EXPECT_TRUE(actualResult == expectedResult); - EXPECT_EQ(actualResult.GetNumTestSuites(), CalculateNumTestSuites(expectedResult.GetTestSuites())); - EXPECT_EQ(actualResult.GetNumTests(), CalculateNumTests(expectedResult.GetTestSuites())); - EXPECT_EQ(actualResult.GetNumEnabledTests(), CalculateNumEnabledTests(expectedResult.GetTestSuites())); - EXPECT_EQ(actualResult.GetNumDisabledTests(), CalculateNumDisabledTests(expectedResult.GetTestSuites())); - } - - // Validates that the specified test enumeration cache matches the expected output - void ValidateTestEnumerationCache(const AZ::IO::Path& cacheFile, const TestImpact::TestEnumeration& expectedEnumeration) - { - // Cache file must exist - const auto fileSize = AZ::IO::SystemFile::Length(cacheFile.c_str()); - EXPECT_GT(fileSize, 0); - - // Read raw byte data from cache - AZStd::vector buffer(fileSize + 1); - buffer[fileSize] = 0; - EXPECT_TRUE(AZ::IO::SystemFile::Read(cacheFile.c_str(), buffer.data())); - - // Transform raw byte data to raw string data and attempt to construct the test enumeration - AZStd::string rawEnum(buffer.begin(), buffer.end()); - TestImpact::TestEnumeration actualEnumeration = TestImpact::DeserializeTestEnumeration(rawEnum); - - // Check that the constructed test enumeration matches the expected enumeration - ValidateTestTargetEnumeration(actualEnumeration, expectedEnumeration); - } - - // Validates that the specified cache file does not exist - void ValidateInvalidTestEnumerationCache(const AZ::IO::Path& cacheFile) - { - EXPECT_FALSE(AZ::IO::SystemFile::Exists(cacheFile.c_str())); - } - - TEST_P( - TestEnumeratorFixtureWithConcurrencyAndJobExceptionParams, InvalidCommandArgument_ExpectJobResulFailedToExecuteeOrTestJobException) - { - // Given a test enumerator with no client callback or enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - m_maxConcurrency, - AZStd::nullopt, - AZStd::nullopt); - - // Given a mixture of test enumeration jobs with valid and invalid command arguments - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - const AZStd::string args = (jobId % 2) ? InvalidProcessPath : m_testTargetJobArgs[jobId]; - JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); - m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); - } - - try - { - // When the test enumeration jobs are executed with different exception policies - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, m_jobExceptionPolicy); - - // Expect this statement to be reachable only if no exception policy for launch failures - EXPECT_FALSE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)); - - for (const auto& job : enumerationJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - if (jobId % 2) - { - // Expect invalid jobs have a job result of FailedToExecute - ValidateJobFailedToExecute(job); - } - else - { - // Expect the valid jobs job to successfully result in a test enumeration that matches the expected test enumeration data - ValidateJobExecutedSuccessfully(job); - ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); - } - } - } - catch ([[maybe_unused]] const TestImpact::TestJobException& e) - { - // Expect this statement to be reachable only if there is an exception policy for launch failures - EXPECT_TRUE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_P( - TestEnumeratorFixtureWithConcurrencyAndJobExceptionParams, ErroneousReturnCode_ExpectJobResultExecutedWithFailureOrTestJobException) - { - // Given a test enumerator with no client callback or enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - m_maxConcurrency, - AZStd::nullopt, - AZStd::nullopt); - - // Given a mixture of test enumeration jobs that execute and return either successfully or with failure - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); - const AZStd::string args = (jobId % 2) - ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, AZStd::chrono::milliseconds(0)).c_str()) - : m_testTargetJobArgs[jobId]; - m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); - } - - try - { - // When the test enumeration jobs are executed with different exception policies - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, m_jobExceptionPolicy); - - // Expect this statement to be reachable only if no exception policy for jobs that return with error - EXPECT_FALSE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)); - - for (const auto& job : enumerationJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - if (jobId % 2) - { - // Expect failed jobs to have job result ExecutedWithFailure and a non-zero return code - ValidateJobExecutedWithFailure(job); - } - else - { - // Expect the valid jobs job to successfully result in a test enumeration that matches the expected test enumeration data - ValidateJobExecutedSuccessfully(job); - ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); - } - } - } - catch ([[maybe_unused]] const TestImpact::TestJobException& e) - { - // Expect this statement to be reachable only if there is an exception policy for jobs that return with error - EXPECT_TRUE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_P(TestEnumeratorFixtureWithConcurrencyAndCacheExceptionParams, EmptyCacheRead_NoCacheDataButEnumerationsMatchTestSuitesInTarget) - { - // Given a test enumerator with no client callback, enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - m_maxConcurrency, - AZStd::nullopt, - AZStd::nullopt); - - // Given an test enumeration job for each test target that reads from an enumeration caching - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second, JobData::Cache{JobData::CachePolicy::Read, m_cacheFiles[jobId]}); - m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); - } - - try - { - // When the test enumeration jobs are executed with different exception policies - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, m_cacheExceptionPolicy, JobExceptionPolicy::Never); - - // Expect this statement to be reachable only if no exception policy for read attempts of non-existent caches - EXPECT_FALSE(::IsFlagSet(m_cacheExceptionPolicy, CacheExceptionPolicy::OnCacheNotExist)); - - // Expect each job to successfully result in a test enumeration that matches the expected test enumeration data for that test target - // even though the cache files could not be read - for (const auto& job : enumerationJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - ValidateJobExecutedSuccessfully(job); - ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); - ValidateInvalidTestEnumerationCache(job.GetJobInfo().GetCache()->m_file); - } - } - catch ([[maybe_unused]] const TestImpact::TestEnumerationException& e) - { - // Expect this statement to be reachable only if there is an exception policy for read attempts of non-existent caches - EXPECT_TRUE(::IsFlagSet(m_cacheExceptionPolicy, CacheExceptionPolicy::OnCacheNotExist)); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // Note: this test only cues up one test for enumeration but still runs the permutations for max concurrency so there is duplicated work - TEST_P( - TestEnumeratorFixtureWithConcurrencyAndCacheExceptionParams, - EmptyCacheDataRead_ExpectEnumerationsMatchTestSuitesInTargetOrTestEnumerationException) - { - // Given an enumeration cache for Test Target A with invalid JSON data - WriteTextToFile("", m_cacheFiles[TestTargetA]); - - // Given a test enumerator with no client callback, enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - m_maxConcurrency, - AZStd::nullopt, - AZStd::nullopt); - - // Given an test enumeration job that will attempt to read an invalid enumeration cache - JobData jobData(m_testTargetPaths[TestTargetA].second, JobData::Cache{JobData::CachePolicy::Read, m_cacheFiles[TestTargetA]}); - m_jobInfos.emplace_back(JobInfo({TestTargetA}, m_testTargetJobArgs[TestTargetA], AZStd::move(jobData))); - - try - { - // When the test enumeration jobs are executed with different exception policies - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, m_cacheExceptionPolicy, JobExceptionPolicy::Never); - - // Expect this statement to be reachable only if no exception policy for cache reads that fail - EXPECT_FALSE(::IsFlagSet(m_cacheExceptionPolicy, CacheExceptionPolicy::OnCacheReadFailure)); - - for (const auto& job : enumerationJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - - // Expect the valid jobs job to successfully result in a test enumeration that matches the expected test enumeration data - ValidateJobExecutedSuccessfully(job); - ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); - } - } - catch ([[maybe_unused]] const TestImpact::TestEnumerationException& e) - { - // Expect this statement to be reachable only if there is an exception policy for cache reads that fail - EXPECT_TRUE(::IsFlagSet(m_cacheExceptionPolicy, CacheExceptionPolicy::OnCacheReadFailure)); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_P( - TestEnumeratorFixtureWithConcurrencyAndCacheExceptionParams, - InvalidCacheWrite_ExpectEnumerationsMatchTestSuitesInTargetOrTestEnumerationException) - { - // Given a test enumerator with no client callback,, enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - m_maxConcurrency, - AZStd::nullopt, - AZStd::nullopt); - - // Given an test enumeration job that will attempt to write to an invalid enumeration cache - JobData jobData(m_testTargetPaths[TestTargetA].second, JobData::Cache{JobData::CachePolicy::Write, InvalidProcessPath}); - m_jobInfos.emplace_back(JobInfo({TestTargetA}, m_testTargetJobArgs[TestTargetA], AZStd::move(jobData))); - - try - { - // When the test enumeration job is executed - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, m_cacheExceptionPolicy, JobExceptionPolicy::Never); - - // Expect this statement to be reachable only if no exception policy for cache writes that fail - EXPECT_FALSE(::IsFlagSet(m_cacheExceptionPolicy, CacheExceptionPolicy::OnCacheWriteFailure)); - - for (const auto& job : enumerationJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - - // Expect the valid jobs job to successfully result in a test enumeration that matches the expected test enumeration data - ValidateJobExecutedSuccessfully(job); - ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); - } - } - catch ([[maybe_unused]] const TestImpact::TestEnumerationException& e) - { - // Expect this statement to be reachable only if there is an exception policy for cache writes that fail - EXPECT_TRUE(::IsFlagSet(m_cacheExceptionPolicy, CacheExceptionPolicy::OnCacheWriteFailure)); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_P(TestEnumeratorFixtureWithConcurrencyParams, ValidAndInvalidCacheRead_CachedEnumerationsMatchTestSuitesInTarget) - { - // Given the cache file written for only test target B - WriteTextToFile(TestImpact::SerializeTestEnumeration(m_expectedTestTargetEnumerations[TestTargetB]), m_cacheFiles[TestTargetB]); - - // Given a test enumerator with no client callback, enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - m_maxConcurrency, - AZStd::nullopt, - AZStd::nullopt); - - // Given test enumeration jobs test target A and D with no enumeration caching - m_jobInfos.emplace_back( - JobInfo({TestTargetA}, m_testTargetJobArgs[TestTargetA], JobData{m_testTargetPaths[TestTargetA].second, AZStd::nullopt})); - m_jobInfos.emplace_back( - JobInfo({TestTargetD}, m_testTargetJobArgs[TestTargetD], JobData{m_testTargetPaths[TestTargetD].second, AZStd::nullopt})); - - // Given test target B with enumeration cache reading and a valid cache file - m_jobInfos.emplace_back(JobInfo( - {TestTargetB}, m_testTargetJobArgs[TestTargetB], - JobData{m_testTargetPaths[TestTargetB].second, JobInfo::Cache{JobData::CachePolicy::Read, m_cacheFiles[TestTargetB]}})); - - // Given test target C with enumeration cache reading and an invalid cache file - m_jobInfos.emplace_back(JobInfo( - {TestTargetC}, m_testTargetJobArgs[TestTargetC], - JobData{m_testTargetPaths[TestTargetC].second, JobInfo::Cache{JobData::CachePolicy::Read, "nothing"}})); - - // When the test enumeration jobs are executed - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Expect each job to successfully result in a test enumeration that matches the expected test enumeration data for that test target - for (const auto& job : enumerationJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - - switch (jobId) - { - case TestTargetA: // No cache read - case TestTargetC: // Cache read, but invalid cache, so re-enumerate anyway - case TestTargetD: // No cache read - { - ValidateJobExecutedSuccessfully(job); - break; - } - case TestTargetB: // Cache read, successful cache read, so job not executed - { - ValidateJobSuccessfulCacheRead(job); - break; - } - default: - { - FAIL(); - } - } - - // Regardless of cache policy and cache failures all targets should still produce the expected test enumerations - ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); - } - } - - TEST_F(TestEnumeratorFixture, InvalidCacheDataRead_TestEnumerationException) - { - // Given an enumeration cache for Test Target A with invalid JSON data - WriteTextToFile("There is no valid cache data here", m_cacheFiles[TestTargetA]); - - // Given a test enumerator with no client callback, concurrency, enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - OneConcurrentProcess, - AZStd::nullopt, - AZStd::nullopt); - - // Given an test enumeration job that will attempt to read an invalid enumeration cache - JobData jobData(m_testTargetPaths[TestTargetA].second, JobData::Cache{JobData::CachePolicy::Read, m_cacheFiles[TestTargetA]}); - m_jobInfos.emplace_back(JobInfo({TestTargetA}, m_testTargetJobArgs[TestTargetA], AZStd::move(jobData))); - - try - { - // When the test enumeration jobs are executed with different exception policies - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); - } - catch ([[maybe_unused]] const TestImpact::TestEnumerationException& e) - { - // Expect this statement to be reachable only if there is an exception policy for jobs that return with error - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_P(TestEnumeratorFixtureWithConcurrencyParams, ValidCacheWrite_CachedEnumerationsMatchTestSuitesInTarget) - { - // Given a test enumerator with no client callback, enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - m_maxConcurrency, - AZStd::nullopt, - AZStd::nullopt); - - // Given an test enumeration job for each test target with write enumeration caching - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second, JobData::Cache{JobData::CachePolicy::Write, m_cacheFiles[jobId]}); - m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); - } - - // When the test enumeration jobs are executed - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Expect each job to successfully result in a test enumeration and cache that matches the expected test enumeration data for that - // test target - for (const auto& job : enumerationJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - ValidateJobExecutedSuccessfully(job); - ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); - ValidateTestEnumerationCache(job.GetJobInfo().GetCache()->m_file, m_expectedTestTargetEnumerations[jobId]); - } - } - - TEST_F(TestEnumeratorFixture, EmptyArtifact_ExpectTestEnumerationException) - { - // Given a test enumerator with no client callback, concurrency, enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - OneConcurrentProcess, - AZStd::nullopt, - AZStd::nullopt); - - // Given an test enumeration job that will return successfully but with an empty artifact string - m_jobInfos.emplace_back(JobInfo({0}, m_testTargetJobArgs[0], JobData("", AZStd::nullopt))); - - try - { - // When the test enumeration job is executed - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::TestEnumerationException& e) - { - // Expect an enumeration exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(TestEnumeratorFixture, InvalidArtifact_ExpectTestEnumerationException) - { - // Given a test enumerator with no client callback, concurrency, enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - OneConcurrentProcess, - AZStd::nullopt, - AZStd::nullopt); - - // Given an test enumeration job that will return successfully but not produce an artifact - m_jobInfos.emplace_back(JobInfo( - {0}, AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(0, AZStd::chrono::milliseconds(0)).c_str()), - JobData("", AZStd::nullopt))); - - try - { - // When the test enumeration job is executed - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::TestEnumerationException& e) - { - // Expect an enumeration exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_P(TestEnumeratorFixtureWithConcurrencyParams, EnumerateTestTargets_EnumerationsMatchTestSuitesInTarget) - { - // Given a test enumerator with no client callback, enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - m_maxConcurrency, - AZStd::nullopt, - AZStd::nullopt); - - // Given an test enumeration job for each test target with no enumeration caching - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); - m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); - } - - // When the test enumeration jobs are executed - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Expect each job to successfully result in a test enumeration that matches the expected test enumeration data for that test target - for (const auto& job : enumerationJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - ValidateJobExecutedSuccessfully(job); - ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); - } - } - - TEST_P(TestEnumeratorFixtureWithConcurrencyParams, EnumerateTestTargetsWithArbitraryJobIds_EnumerationsMatchTestSuitesInTarget) - { - // Given a set of arbitrary job ids to be used for the test target jobs - enum - { - ArbitraryA = 36, - ArbitraryB = 890, - ArbitraryC = 19, - ArbitraryD = 1 - }; - - const AZStd::unordered_map sequentialToArbitrary = - { - {TestTargetA, ArbitraryA}, - {TestTargetB, ArbitraryB}, - {TestTargetC, ArbitraryC}, - {TestTargetD, ArbitraryD}, - }; - - const AZStd::unordered_map arbitraryToSequential = - { - {ArbitraryA, TestTargetA}, - {ArbitraryB, TestTargetB}, - {ArbitraryC, TestTargetC}, - {ArbitraryD, TestTargetD}, - }; - - // Given a test enumerator with no client callback, enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - m_maxConcurrency, - AZStd::nullopt, - AZStd::nullopt); - - // Given an test enumeration job for each test target with no enumeration caching - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); - m_jobInfos.emplace_back(JobInfo({sequentialToArbitrary.at(jobId)}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); - } - - // When the test enumeration jobs are executed - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Expect each job to successfully result in a test enumeration that matches the expected test enumeration data for that test target - for (const auto& job : enumerationJobs) - { - const JobInfo::IdType jobId = arbitraryToSequential.at(job.GetJobInfo().GetId().m_value); - ValidateJobExecutedSuccessfully(job); - ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); - } - } - - TEST_P(TestEnumeratorFixtureWithConcurrencyParams, EnumerateTestTargetsWithCallback_EnumerationsMatchTestSuitesInTarget) - { - // Given a client callback function that tracks the number of successful enumerations - size_t numSuccesses = 0; - const auto jobCallback = [&numSuccesses]([[maybe_unused]] const TestImpact::TestEnumerator::JobInfo& jobInfo, const TestImpact::JobMeta& meta) - { - if (meta.m_result == TestImpact::JobResult::ExecutedWithSuccess) - { - numSuccesses++; - } - }; - - // Given a test enumerator with no enumeration timeout or enumerator timeout - m_testEnumerator = AZStd::make_unique( - jobCallback, - m_maxConcurrency, - AZStd::nullopt, - AZStd::nullopt); - - // Given an test enumeration job for each test target with no enumeration caching - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); - m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); - } - - // When the test enumeration jobs are executed - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Expect the number of successful enumerations tracked in the callback to match the number of test targets enumerated - EXPECT_EQ(numSuccesses, enumerationJobs.size()); - - // Expect each job to successfully result in a test enumeration that matches the expected test enumeration data for that test target - for (const auto& job : enumerationJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - ValidateJobExecutedSuccessfully(job); - ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); - } - } - - TEST_P(TestEnumeratorFixtureWithConcurrencyParams, JobRunnerTimeout_InFlightJobsTimeoutAndQueuedJobsUnlaunched) - { - // Given a test enumerator with no client callback or enumerator timeout and 500ms enumeration timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - m_maxConcurrency, - AZStd::chrono::milliseconds(500), - AZStd::nullopt); - - // Given an test enumeration job for each test target with no enumeration caching where half will sleep indefinitely - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); - const AZStd::string args = (jobId % 2) - ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, LongSleep).c_str()) - : m_testTargetJobArgs[jobId]; - m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); - } - - // When the test enumeration jobs are executed - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Expect half the jobs to successfully result in a test enumeration that matches the expected test enumeration data for that test - // target with the other half having timed out - for (const auto& job : enumerationJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - if (jobId % 2) - { - ValidateJobTimeout(job); - } - else - { - ValidateJobExecutedSuccessfully(job); - ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); - } - } - } - - TEST_F(TestEnumeratorFixture, JobTimeout_InFlightJobTimeoutAndQueuedJobsUnlaunched) - { - // Given a test enumerator with no client callback or enumeration timeout and a 5 second enumerator timeout - m_testEnumerator = AZStd::make_unique( - AZStd::nullopt, - FourConcurrentProcesses, - AZStd::nullopt, - AZStd::chrono::milliseconds(5000)); - - // Given an test enumeration job for each test target with no enumeration caching where half will sleep indefinitely - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second, AZStd::nullopt); - const AZStd::string args = (jobId % 2) - ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, LongSleep).c_str()) - : m_testTargetJobArgs[jobId]; - m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); - } - - // When the test enumeration jobs are executed - const auto enumerationJobs = m_testEnumerator->Enumerate(m_jobInfos, CacheExceptionPolicy::Never, JobExceptionPolicy::Never); - - // Expect half the jobs to successfully result in a test enumeration that matches the expected test enumeration data for that test - // target with the other half having timed out - for (const auto& job : enumerationJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - if (jobId % 2) - { - ValidateJobTimeout(job); - } - else - { - ValidateJobExecutedSuccessfully(job); - ValidateTestTargetEnumeration(job.GetPayload().value(), m_expectedTestTargetEnumerations[jobId]); - } - } - } - - INSTANTIATE_TEST_CASE_P( - , - TestEnumeratorFixtureWithConcurrencyAndJobExceptionParams, - ::testing::Combine( - ::testing::ValuesIn(MaxConcurrentEnumerations), - ::testing::ValuesIn(JobExceptionPolicies)) - ); - - INSTANTIATE_TEST_CASE_P( - , - TestEnumeratorFixtureWithConcurrencyAndCacheExceptionParams, - ::testing::Combine( - ::testing::ValuesIn(MaxConcurrentEnumerations), - ::testing::ValuesIn(CacheExceptionPolicies)) - ); - - INSTANTIATE_TEST_CASE_P( - , - TestEnumeratorFixtureWithConcurrencyParams, - ::testing::ValuesIn(MaxConcurrentEnumerations) - ); -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEumerationSerializerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEumerationSerializerTest.cpp deleted file mode 100644 index aa94657378..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestEumerationSerializerTest.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include - -#include -#include - -namespace UnitTest -{ - const TestImpact::TestEnumeration EmptyTestEnum(AZStd::vector{}); - - TEST(TestEnumerationSerializer, EmptyTestEnumerationSuites_ExpectTemptyTestEnumeration) - { - // Given an empty set of test enumeration suites - // When the test enumeration is serialized and deserialized back again - const AZStd::string serializedString = TestImpact::SerializeTestEnumeration(EmptyTestEnum); - const TestImpact::TestEnumeration actualEnumeration = TestImpact::DeserializeTestEnumeration(serializedString); - - // Expect the actual test enumeration to match the expected test enumeration - EXPECT_TRUE(actualEnumeration == EmptyTestEnum); - } - - TEST(TestEnumerationSerializer, SerializeAndDeserializeSuitesForTestTargetA_ActualSuiteDataMatchesExpectedSuiteData) - { - // Given the test of test enumeration suites for Test target A - const TestImpact::TestEnumeration expectedEnumeration = TestImpact::TestEnumeration(GetTestTargetATestEnumerationSuites()); - - // When the test enumeration is serialized and deserialized back again - const AZStd::string serializedString = TestImpact::SerializeTestEnumeration(expectedEnumeration); - const TestImpact::TestEnumeration actualEnumeration = TestImpact::DeserializeTestEnumeration(serializedString); - - // Expect the actual test enumeration to match the expected test enumeration - EXPECT_TRUE(actualEnumeration == expectedEnumeration); - - // Do not expect the actual test enumeration to match the empty test enumeration - EXPECT_FALSE(actualEnumeration == EmptyTestEnum); - } - - TEST(TestEnumerationSerializer, SerializeAndDeserializeSuitesForTestTargetB_ActualSuiteDataMatchesExpectedSuiteData) - { - // Given the test of test enumeration suites for Test target B - const TestImpact::TestEnumeration expectedEnumeration = TestImpact::TestEnumeration(GetTestTargetBTestEnumerationSuites()); - - // When the test enumeration is serialized and deserialized back again - const AZStd::string serializedString = TestImpact::SerializeTestEnumeration(expectedEnumeration); - const TestImpact::TestEnumeration actualEnumeration = TestImpact::DeserializeTestEnumeration(serializedString); - - // Expect the actual test enumeration to match the expected test enumeration - EXPECT_TRUE(actualEnumeration == expectedEnumeration); - - // Do not expect the actual test enumeration to match the empty test enumeration - EXPECT_FALSE(actualEnumeration == EmptyTestEnum); - } - - TEST(TestEnumerationSerializer, SerializeAndDeserializeSuitesForTestTargetC_ActualSuiteDataMatchesExpectedSuiteData) - { - // Given the test of test enumeration suites for Test target B - const TestImpact::TestEnumeration expectedEnumeration = TestImpact::TestEnumeration(GetTestTargetCTestEnumerationSuites()); - - // When the test enumeration is serialized and deserialized back again - const AZStd::string serializedString = TestImpact::SerializeTestEnumeration(expectedEnumeration); - const TestImpact::TestEnumeration actualEnumeration = TestImpact::DeserializeTestEnumeration(serializedString); - - // Expect the actual test enumeration to match the expected test enumeration - EXPECT_TRUE(actualEnumeration == expectedEnumeration); - - // Do not expect the actual test enumeration to match the empty test enumeration - EXPECT_FALSE(actualEnumeration == EmptyTestEnum); - } - - TEST(TestEnumerationSerializer, SerializeAndDeserializeSuitesForTestTargetD_ActualSuiteDataMatchesExpectedSuiteData) - { - // Given the test of test enumeration suites for Test target B - const TestImpact::TestEnumeration expectedEnumeration = TestImpact::TestEnumeration(GetTestTargetDTestEnumerationSuites()); - - // When the test enumeration is serialized and deserialized back again - const AZStd::string serializedString = TestImpact::SerializeTestEnumeration(expectedEnumeration); - const TestImpact::TestEnumeration actualEnumeration = TestImpact::DeserializeTestEnumeration(serializedString); - - // Expect the actual test enumeration to match the expected test enumeration - EXPECT_TRUE(actualEnumeration == expectedEnumeration); - - // Do not expect the actual test enumeration to match the empty test enumeration - EXPECT_FALSE(actualEnumeration == EmptyTestEnum); - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunSerializerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunSerializerTest.cpp deleted file mode 100644 index 1fc7c62b3d..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunSerializerTest.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include - -#include -#include - -namespace UnitTest -{ - const TestImpact::TestRun EmptyTestRun(AZStd::vector{}, AZStd::chrono::milliseconds{0}); - - TEST(TestRunSerializer, EmptyTestRunSuites_ExpectTemptyTestRun) - { - // Given an empty set of test run suites - // When the test run is serialized and deserialized back again - const auto serializedString = TestImpact::SerializeTestRun(EmptyTestRun); - const auto actualRun = TestImpact::DeserializeTestRun(serializedString); - - // Expect the actual test run to match the expected test run - EXPECT_TRUE(actualRun == EmptyTestRun); - } - - TEST(TestRunSerializer, SerializeAndDeserializeSuitesForTestTargetA_ActualSuiteDataMatchesExpectedSuiteData) - { - // Given the test of test run suites for Test target A - const auto expectedRun = TestImpact::TestRun(GetTestTargetATestRunSuites(), AZStd::chrono::milliseconds{500}); - - // When the test run is serialized and deserialized back again - const auto serializedString = TestImpact::SerializeTestRun(expectedRun); - const auto actualRun = TestImpact::DeserializeTestRun(serializedString); - - // Expect the actual test run to match the expected test run - EXPECT_TRUE(actualRun == expectedRun); - - // Do not expect the actual test run to match the empty test run - EXPECT_FALSE(actualRun == EmptyTestRun); - } - - TEST(TestRunSerializer, SerializeAndDeserializeSuitesForTestTargetB_ActualSuiteDataMatchesExpectedSuiteData) - { - // Given the test of test run suites for Test target B - const auto expectedRun = TestImpact::TestRun(GetTestTargetBTestRunSuites(), AZStd::chrono::milliseconds{500}); - - // When the test run is serialized and deserialized back again - const auto serializedString = TestImpact::SerializeTestRun(expectedRun); - const auto actualRun = TestImpact::DeserializeTestRun(serializedString); - - // Expect the actual test run to match the expected test run - EXPECT_TRUE(actualRun == expectedRun); - - // Do not expect the actual test run to match the empty test run - EXPECT_FALSE(actualRun == EmptyTestRun); - } - - TEST(TestRunSerializer, SerializeAndDeserializeSuitesForTestTargetC_ActualSuiteDataMatchesExpectedSuiteData) - { - // Given the test of test run suites for Test target B - const auto expectedRun = TestImpact::TestRun(GetTestTargetCTestRunSuites(), AZStd::chrono::milliseconds{500}); - - // When the test run is serialized and deserialized back again - const auto serializedString = TestImpact::SerializeTestRun(expectedRun); - const auto actualRun = TestImpact::DeserializeTestRun(serializedString); - - // Expect the actual test run to match the expected test run - EXPECT_TRUE(actualRun == expectedRun); - - // Do not expect the actual test run to match the empty test run - EXPECT_FALSE(actualRun == EmptyTestRun); - } - - TEST(TestRunSerializer, SerializeAndDeserializeSuitesForTestTargetD_ActualSuiteDataMatchesExpectedSuiteData) - { - // Given the test of test run suites for Test target B - const auto expectedRun = TestImpact::TestRun(GetTestTargetDTestRunSuites(), AZStd::chrono::milliseconds{500}); - - // When the test run is serialized and deserialized back again - const auto serializedString = TestImpact::SerializeTestRun(expectedRun); - const auto actualRun = TestImpact::DeserializeTestRun(serializedString); - - // Expect the actual test run to match the expected test run - EXPECT_TRUE(actualRun == expectedRun); - - // Do not expect the actual test run to match the empty test run - EXPECT_FALSE(actualRun == EmptyTestRun); - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunnerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunnerTest.cpp deleted file mode 100644 index 08a0306e7f..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Test/TestImpactTestRunnerTest.cpp +++ /dev/null @@ -1,516 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace UnitTest -{ - using JobExceptionPolicy = TestImpact::TestRunner::JobExceptionPolicy; - - AZStd::string GetRunCommandForTarget(AZStd::pair testTarget) - { - return AZStd::string::format( - "%s %s AzRunUnitTests --gtest_output=xml:%s", LY_TEST_IMPACT_AZ_TESTRUNNER_BIN, testTarget.first.c_str(), - testTarget.second.c_str()); - } - - class TestRunnerFixture - : public AllocatorsTestFixture - { - public: - void SetUp() override; - - protected: - using JobInfo = TestImpact::TestRunner::JobInfo; - using JobData = TestImpact::TestRunner::JobData; - - AZStd::vector m_jobInfos; - AZStd::unique_ptr m_testRunner; - AZStd::vector m_testTargetJobArgs; - AZStd::vector> m_testTargetPaths; - AZStd::vector m_expectedTestTargetRuns; - AZStd::vector m_expectedTestTargetResult; - size_t m_maxConcurrency = 0; - }; - - void TestRunnerFixture::SetUp() - { - UnitTest::AllocatorsTestFixture::SetUp(); - - DeleteFiles(LY_TEST_IMPACT_TEST_TARGET_RESULTS_DIR, "*.xml"); - - // first: path to test target bin - // second: path to test target gtest results file in XML format - const AZStd::string runPath = AZStd::string(LY_TEST_IMPACT_TEST_TARGET_RESULTS_DIR) + "/%s.Run.xml"; - m_testTargetPaths.emplace_back( - LY_TEST_IMPACT_TEST_TARGET_A_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME)); - m_testTargetPaths.emplace_back( - LY_TEST_IMPACT_TEST_TARGET_B_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME)); - m_testTargetPaths.emplace_back( - LY_TEST_IMPACT_TEST_TARGET_C_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME)); - m_testTargetPaths.emplace_back( - LY_TEST_IMPACT_TEST_TARGET_D_BIN, AZStd::string::format(runPath.c_str(), LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME)); - - m_expectedTestTargetRuns.emplace_back(GetTestTargetATestRunSuites(), AZStd::chrono::milliseconds{500}); - m_expectedTestTargetRuns.emplace_back(GetTestTargetBTestRunSuites(), AZStd::chrono::milliseconds{500}); - m_expectedTestTargetRuns.emplace_back(GetTestTargetCTestRunSuites(), AZStd::chrono::milliseconds{500}); - m_expectedTestTargetRuns.emplace_back(GetTestTargetDTestRunSuites(), AZStd::chrono::milliseconds{500}); - - m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Failed); - m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Passed); - m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Passed); - m_expectedTestTargetResult.emplace_back(TestImpact::TestRunResult::Passed); - - for (const auto& testTarget : m_testTargetPaths) - { - m_testTargetJobArgs.emplace_back(GetRunCommandForTarget(testTarget)); - } - } - - // Fixture parameterized for different max number of concurrent jobs - class TestRunnerFixtureWithConcurrencyParams - : public TestRunnerFixture - , public ::testing::WithParamInterface - { - public: - void SetUp() override - { - TestRunnerFixture::SetUp(); - m_maxConcurrency = GetParam(); - } - }; - - using ConcurrencyAndJobExceptionPermutation = AZStd::tuple - < - size_t, // Max number of concurrent processes - JobExceptionPolicy // Test job exception policy - >; - - // Fixture parameterized for different max number of concurrent jobs and different job exception policies - class TestRunnerFixtureWithConcurrencyAndJobExceptionParams - : public TestRunnerFixture - , public ::testing::WithParamInterface - { - public: - void SetUp() override - { - TestRunnerFixture::SetUp(); - const auto& [maxConcurrency, jobExceptionPolicy] = GetParam(); - m_maxConcurrency = maxConcurrency; - m_jobExceptionPolicy = jobExceptionPolicy; - } - - protected: - JobExceptionPolicy m_jobExceptionPolicy = JobExceptionPolicy::Never; - }; - - class TestRunnerFixtureWithConcurrencyAndFailedToLaunchExceptionParams - : public TestRunnerFixtureWithConcurrencyAndJobExceptionParams - { - }; - - class TestRunnerFixtureWithConcurrencyAndExecutedWithFailureExceptionParams - : public TestRunnerFixtureWithConcurrencyAndJobExceptionParams - { - }; - - namespace - { - AZStd::array MaxConcurrentRuns = {1, 2, 3, 4}; - - AZStd::array FailedToLaunchExceptionPolicies = { - JobExceptionPolicy::Never, JobExceptionPolicy::OnFailedToExecute}; - - AZStd::array ExecutedWithFailureExceptionPolicies = { - JobExceptionPolicy::Never, JobExceptionPolicy::OnExecutedWithFailure}; - } // namespace - - TEST_P( - TestRunnerFixtureWithConcurrencyAndFailedToLaunchExceptionParams, - InvalidCommandArgument_ExpectJobResulFailedToExecuteeOrTestJobException) - { - // Given a test runner with no client callback or run timeout or runner timeout - m_testRunner = AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); - - // Given a mixture of test run jobs with valid and invalid command arguments - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - const AZStd::string args = (jobId % 2) ? InvalidProcessPath : m_testTargetJobArgs[jobId]; - JobData jobData(m_testTargetPaths[jobId].second); - m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); - } - - try - { - // When the test run jobs are executed with different exception policies - const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, m_jobExceptionPolicy); - - // Expect this statement to be reachable only if no exception policy for launch failures - EXPECT_FALSE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)); - - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - if (jobId % 2) - { - // Expect invalid jobs have a job result of FailedToExecute - ValidateJobFailedToExecute(job); - } - else - { - // Expect the valid jobs to successfully result in a test run that matches the expected test run data - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); - } - } - } - catch ([[maybe_unused]] const TestImpact::TestJobException& e) - { - // Expect this statement to be reachable only if there is an exception policy for launch failures - EXPECT_TRUE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_P( - TestRunnerFixtureWithConcurrencyAndExecutedWithFailureExceptionParams, - ErroneousReturnCode_ExpectJobResultExecutedWithFailureOrTestJobException) - { - // Given a test runner with no client callback or run timeout or runner timeout - m_testRunner = AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); - - // Given a mixture of test run jobs that execute and return either successfully or with failure - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second); - m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); - } - - try - { - // When the test run jobs are executed with different exception policies - const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, m_jobExceptionPolicy); - - // Expect this statement to be reachable only if no exception policy for jobs that return with error - EXPECT_FALSE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)); - - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - - // Expect the valid jobs to successfully result in a test run that matches the expected test run data - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); - } - } - catch ([[maybe_unused]] const TestImpact::TestJobException& e) - { - // Expect this statement to be reachable only if there is an exception policy for jobs that return with error - EXPECT_TRUE(::IsFlagSet(m_jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)); - } - catch ([[maybe_unused]] const TestImpact::Exception& e) - { - FAIL(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(TestRunnerFixture, EmptyArtifact_ExpectTestRunnerException) - { - // Given a test runner with no client callback, concurrency, run timeout or runner timeout - m_testRunner = AZStd::make_unique(AZStd::nullopt, OneConcurrentProcess, AZStd::nullopt, AZStd::nullopt); - - // Given an test runner job that will return successfully but with an empty artifact string - m_jobInfos.emplace_back(JobInfo({0}, m_testTargetJobArgs[0], JobData(""))); - - try - { - // When the test runner job is executed - const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::TestRunException& e) - { - // Expect an runner exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(TestRunnerFixture, InvalidRunArtifact_ExpectArtifactException) - { - // Given a test run artifact with invalid contents - WriteTextToFile("There is nothing valid here", m_testTargetPaths[TestTargetA].second); - - // Given a job command that will write the test run artifact to a different location that what we will read from - auto invalidRunArtifact = m_testTargetPaths[TestTargetA]; - invalidRunArtifact.second /= ".xml"; - const AZStd::string args = GetRunCommandForTarget(invalidRunArtifact); - - // Given a test runner with no client callback, concurrency, run timeout or runner timeout - m_testRunner = AZStd::make_unique(AZStd::nullopt, OneConcurrentProcess, AZStd::nullopt, AZStd::nullopt); - - // Given an test runner job that will return successfully but not produce an artifact - JobData jobData(m_testTargetPaths[TestTargetA].second); - m_jobInfos.emplace_back(JobInfo({TestTargetA}, args, AZStd::move(jobData))); - - try - { - // When the test runner job is executed - const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an runner exception - SUCCEED(); - } - catch (const TestImpact::Exception& e) - { - std::cout << e.what(); - FAIL(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_P(TestRunnerFixtureWithConcurrencyParams, RunTestTargets_RunsMatchTestSuitesInTarget) - { - // Given a test runner with no client callback, runner timeout or runner timeout - m_testRunner = AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); - - // Given an test runner job for each test target with no runner caching - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second); - m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); - } - - // When the test runner jobs are executed - const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); - - // Expect each job to successfully result in a test runner that matches the expected test runner data for that test target - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); - } - } - - TEST_P(TestRunnerFixtureWithConcurrencyParams, RunTestTargetsWithArbitraryJobIds_RunsMatchTestSuitesInTarget) - { - // Given a set of arbitrary job ids to be used for the test target jobs - enum - { - ArbitraryA = 36, - ArbitraryB = 890, - ArbitraryC = 19, - ArbitraryD = 1 - }; - - const AZStd::unordered_map sequentialToArbitrary = - { - {TestTargetA, ArbitraryA}, - {TestTargetB, ArbitraryB}, - {TestTargetC, ArbitraryC}, - {TestTargetD, ArbitraryD}, - }; - - const AZStd::unordered_map arbitraryToSequential = - { - {ArbitraryA, TestTargetA}, - {ArbitraryB, TestTargetB}, - {ArbitraryC, TestTargetC}, - {ArbitraryD, TestTargetD}, - }; - - // Given a test runner with no client callback, run timeout or runner timeout - m_testRunner = AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); - - // Given an test run job for each test target - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second); - m_jobInfos.emplace_back(JobInfo({sequentialToArbitrary.at(jobId)}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); - } - - // When the test run jobs are executed - const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); - - // Expect each job to successfully result in a test run that matches the expected test run data for that test target - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = arbitraryToSequential.at(job.GetJobInfo().GetId().m_value); - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); - } - } - - TEST_P(TestRunnerFixtureWithConcurrencyParams, RunTestTargetsWithCallback_RunsMatchTestSuitesInTarget) - { - // Given a client callback function that tracks the number of successful runs - size_t numSuccesses = 0; - const auto jobCallback = - [&numSuccesses]([[maybe_unused]] const TestImpact::TestRunner::JobInfo& jobInfo, const TestImpact::JobMeta& meta) { - if (meta.m_result == TestImpact::JobResult::ExecutedWithSuccess) - { - numSuccesses++; - } - }; - - // Given a test runner with no run timeout or runner timeout - m_testRunner = AZStd::make_unique(jobCallback, m_maxConcurrency, AZStd::nullopt, AZStd::nullopt); - - // Given an test run job for each test target - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second); - m_jobInfos.emplace_back(JobInfo({jobId}, m_testTargetJobArgs[jobId], AZStd::move(jobData))); - } - - // When the test run jobs are executed - const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); - - // Expect the number of successful runs tracked in the callback to match the number of test targets run with no failures - EXPECT_EQ( - numSuccesses, - AZStd::count_if(m_expectedTestTargetResult.begin(), m_expectedTestTargetResult.end(), [](TestImpact::TestRunResult result) { - return result == TestImpact::TestRunResult::Passed; - })); - - // Expect each job to successfully result in a test run that matches the expected test run data for that test target - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); - } - } - - TEST_P(TestRunnerFixtureWithConcurrencyParams, JobRunnerTimeout_InFlightJobsTimeoutAndQueuedJobsUnlaunched) - { - // Given a test runner with no client callback or runner timeout and 500ms run timeout - m_testRunner = - AZStd::make_unique(AZStd::nullopt, m_maxConcurrency, AZStd::chrono::milliseconds(500), AZStd::nullopt); - - // Given an test run job for each test target where half will sleep indefinitely - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second); - const AZStd::string args = (jobId % 2) - ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, LongSleep).c_str()) - : m_testTargetJobArgs[jobId]; - m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); - } - - // When the test run jobs are executed - const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); - - // Expect half the jobs to successfully result in a test run that matches the expected test run data for that test target - // with the other half having timed out - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - if (jobId % 2) - { - ValidateJobTimeout(job); - } - else - { - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); - } - } - } - - TEST_F(TestRunnerFixture, JobTimeout_InFlightJobTimeoutAndQueuedJobsUnlaunched) - { - // Given a test runner with no client callback or run timeout and a 5 second runner timeout - m_testRunner = AZStd::make_unique( - AZStd::nullopt, FourConcurrentProcesses, AZStd::nullopt, AZStd::chrono::milliseconds(5000)); - - // Given an test run job for each test target where half will sleep indefinitely - for (size_t jobId = 0; jobId < m_testTargetJobArgs.size(); jobId++) - { - JobData jobData(m_testTargetPaths[jobId].second); - const AZStd::string args = (jobId % 2) - ? AZStd::string::format("%s %s", ValidProcessPath, ConstructTestProcessArgs(jobId, LongSleep).c_str()) - : m_testTargetJobArgs[jobId]; - m_jobInfos.emplace_back(JobInfo({jobId}, args, AZStd::move(jobData))); - } - - // When the test run jobs are executed - const auto runnerJobs = m_testRunner->RunTests(m_jobInfos, JobExceptionPolicy::Never); - - // Expect half the jobs to successfully result in a test run that matches the expected test run data for that test target - // with the other half having timed out - for (const auto& job : runnerJobs) - { - const JobInfo::IdType jobId = job.GetJobInfo().GetId().m_value; - if (jobId % 2) - { - ValidateJobTimeout(job); - } - else - { - ValidateTestRunCompleted(job, m_expectedTestTargetResult[jobId]); - ValidateTestTargetRun(job.GetPayload().value(), m_expectedTestTargetRuns[jobId]); - } - } - } - - INSTANTIATE_TEST_CASE_P( - , - TestRunnerFixtureWithConcurrencyAndFailedToLaunchExceptionParams, - ::testing::Combine(::testing::ValuesIn(MaxConcurrentRuns), ::testing::ValuesIn(FailedToLaunchExceptionPolicies))); - - INSTANTIATE_TEST_CASE_P( - , - TestRunnerFixtureWithConcurrencyAndExecutedWithFailureExceptionParams, - ::testing::Combine(::testing::ValuesIn(MaxConcurrentRuns), ::testing::ValuesIn(ExecutedWithFailureExceptionPolicies))); - - INSTANTIATE_TEST_CASE_P(, TestRunnerFixtureWithConcurrencyParams, ::testing::ValuesIn(MaxConcurrentRuns)); -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactFrameworkPathTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactFrameworkPathTest.cpp deleted file mode 100644 index 2982cb0551..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactFrameworkPathTest.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include -#include - -namespace UnitTest -{ - class FrameworkPathTestFixture - : public AllocatorsTestFixture - { - public: - void SetUp() override - { - UnitTest::AllocatorsTestFixture::SetUp(); - - m_parentPathAbs = AZStd::string(AZStd::string("parent") + AZ_TRAIT_OS_PATH_SEPARATOR + "path"); - m_childPathRel = AZStd::string(AZStd::string("child") + AZ_TRAIT_OS_PATH_SEPARATOR + "path"); - m_childPathAbs = AZStd::string(m_parentPathAbs + AZ_TRAIT_OS_PATH_SEPARATOR + m_childPathRel); - - m_pathComponentA = AZStd::string("DirA"); - m_pathComponentB = AZStd::string("DirB"); - m_pathComponentC = AZStd::string("DirC"); - - m_posixPath = m_pathComponentA + AZ::IO::PosixPathSeparator + m_pathComponentB + AZ::IO::PosixPathSeparator + m_pathComponentC; - - m_windowsPath = - m_pathComponentA + AZ::IO::WindowsPathSeparator + m_pathComponentB + AZ::IO::WindowsPathSeparator + m_pathComponentC; - - m_mixedPath = - m_pathComponentA + AZ::IO::WindowsPathSeparator + m_pathComponentB + AZ::IO::PosixPathSeparator + m_pathComponentC; - - m_referredPath = - m_pathComponentA + AZ_TRAIT_OS_PATH_SEPARATOR + m_pathComponentB + AZ_TRAIT_OS_PATH_SEPARATOR + m_pathComponentC; - } - - void TearDown() override - { - UnitTest::AllocatorsTestFixture::TearDown(); - } - - AZStd::string m_parentPathAbs; - AZStd::string m_childPathRel; - AZStd::string m_childPathAbs; - - AZStd::string m_pathComponentA; - AZStd::string m_pathComponentB; - AZStd::string m_pathComponentC; - - AZ::IO::Path m_posixPath; - AZ::IO::Path m_windowsPath; - AZ::IO::Path m_mixedPath; - AZ::IO::Path m_referredPath; - }; - - TEST_F(FrameworkPathTestFixture, DefaultConstructor_HasEmptyAbsAndRelPaths) - { - // Given an empty framework path - TestImpact::FrameworkPath path; - - // Expect the absolute path to be empty - EXPECT_TRUE(path.Absolute().empty()); - - // Expect the relative path to be empty - EXPECT_TRUE(path.Relative().empty()); - } - - TEST_F(FrameworkPathTestFixture, OrphanConstructor_HasAbsAndEmptyRelPaths) - { - // Given an orhpan framework path - TestImpact::FrameworkPath path(m_parentPathAbs); - - // Expect the absolute path to be equal to the specified path - EXPECT_EQ(path.Absolute().String(), m_parentPathAbs); - - // Expect the relative path to be current directory symbol - EXPECT_STREQ(path.Relative().c_str(), "."); - } - - TEST_F(FrameworkPathTestFixture, ParentConstructor_HasAbsAndRelPaths) - { - // Given a child framework path - TestImpact::FrameworkPath path(m_childPathAbs, TestImpact::FrameworkPath(m_parentPathAbs)); - - // Expect the absolute path to be equal to the concatenation of the parent and child path - EXPECT_EQ(path.Absolute().String(), m_childPathAbs); - - // Expect the relative path to equal to the specified path - EXPECT_EQ(path.Relative().String(), m_childPathRel); - } - - TEST_F(FrameworkPathTestFixture, PosixSeperators_HasUniformPreferredSeperators) - { - // Given an orhpan framework path with Posix separators - TestImpact::FrameworkPath path(m_posixPath); - - // Expect the absolute path to be equal to the specified path with preferred separators - EXPECT_EQ(path.Absolute(), m_referredPath); - - // Expect the relative path to be current directory symbol - EXPECT_STREQ(path.Relative().c_str(), "."); - } - - TEST_F(FrameworkPathTestFixture, WindowsSeperators_HasUniformPreferredSeperators) - { - // Given an orhpan framework path with Windows separators - TestImpact::FrameworkPath path(m_windowsPath); - - // Expect the absolute path to be equal to the specified path with preferred separators - EXPECT_EQ(path.Absolute(), m_referredPath); - - // Expect the relative path to be current directory symbol - EXPECT_STREQ(path.Relative().c_str(), "."); - } - - TEST_F(FrameworkPathTestFixture, MixedSeperators_HasUniformPreferredSeperators) - { - // Given an orhpan framework path with mixed separators - TestImpact::FrameworkPath path(m_windowsPath); - - // Expect the absolute path to be equal to the specified path with preferred separators - EXPECT_EQ(path.Absolute(), m_referredPath); - - // Expect the relative path to be current directory symbol - EXPECT_STREQ(path.Relative().c_str(), "."); - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestMain.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestMain.cpp deleted file mode 100644 index 632e84ad9f..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestMain.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include -#include - -class TestImpactTestEnvironment : public AZ::Test::ITestEnvironment -{ -protected: - void SetupEnvironment() override - { - AZ::AllocatorInstance::Create(); - AZ::AllocatorInstance::Create(); - } - - void TeardownEnvironment() override - { - AZ::AllocatorInstance::Destroy(); - AZ::AllocatorInstance::Destroy(); - } -}; - -AZ_UNIT_TEST_HOOK(new TestImpactTestEnvironment); From c011c4c64c12c470c02a2abe743cf30ff97fb1f7 Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 19 May 2021 16:59:58 +0100 Subject: [PATCH 036/233] Address PR comments --- cmake/TestImpactFramework/ConsoleFrontendConfig.in | 10 +++++++--- cmake/TestImpactFramework/LYTestImpactFramework.cmake | 8 +++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cmake/TestImpactFramework/ConsoleFrontendConfig.in b/cmake/TestImpactFramework/ConsoleFrontendConfig.in index 73e90ea0ce..f6af3d801d 100644 --- a/cmake/TestImpactFramework/ConsoleFrontendConfig.in +++ b/cmake/TestImpactFramework/ConsoleFrontendConfig.in @@ -9,12 +9,16 @@ "workspace": { "temp": { "root": "${temp_dir}", - "artifact_dir": "RuntimeArtifact" + "relative_paths": { + "artifact_dir": "RuntimeArtifact" + } }, "persistent": { "root": "${persistent_dir}", - "test_impact_data_file": "TestImpactData.spartia", - "enumeration_cache_dir": "EnumerationCache" + "relative_paths": { + "test_impact_data_file": "TestImpactData.spartia", + "enumeration_cache_dir": "EnumerationCache" + } } }, "artifacts": { diff --git a/cmake/TestImpactFramework/LYTestImpactFramework.cmake b/cmake/TestImpactFramework/LYTestImpactFramework.cmake index fc3ba0af44..c85b5ff1e9 100644 --- a/cmake/TestImpactFramework/LYTestImpactFramework.cmake +++ b/cmake/TestImpactFramework/LYTestImpactFramework.cmake @@ -99,7 +99,7 @@ endfunction() # \arg:TEST_NAME name of test function(ly_test_impact_extract_google_test COMPOSITE_TEST TEST_NAMESPACE TEST_NAME) get_property(test_components GLOBAL PROPERTY LY_ALL_TESTS_${COMPOSITE_TEST}_TEST_NAME) - # Namespace and test are mandetory + # Namespace and test are mandatory string(REPLACE "::" ";" test_components ${test_components}) list(LENGTH test_components num_test_components) if(num_test_components LESS 2) @@ -134,13 +134,11 @@ endfunction() #! ly_test_impact_extract_google_test_params: extracts the google test name and command parameters. # # \arg:COMPOSITE_TEST test in the form 'namespace::test' -# \arg:TEST_NAMESPACE namespace for the test # \arg:TEST_NAME name of test -# \arg:TEST_NAME optional command arguments to run the test -# \arg:TEST_NAME test timeout value +# \arg:TEST_COMMAND optional command arguments to run the test function(ly_test_impact_extract_google_test_params COMPOSITE_TEST TEST_NAME TEST_COMMAND) get_property(test_command GLOBAL PROPERTY LY_ALL_TESTS_${COMPOSITE_TEST}_TEST_COMMAND) - # Namespace and test are mandetory + # Namespace and test are mandatory string(REPLACE "::" ";" test_components ${COMPOSITE_TEST}) list(LENGTH test_components num_test_components) if(num_test_components LESS 2) From e70d86501cb95e6ead6c1bf785a5026af8c4626d Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 19 May 2021 20:35:13 +0100 Subject: [PATCH 037/233] Add runtime api headers --- .../TestImpactChangeList.h | 28 +++ .../TestImpactChangeListException.h | 26 +++ .../TestImpactChangeListSerializer.h | 26 +++ .../TestImpactClientFailureReport.h | 177 +++++++++++++++++ .../TestImpactClientTestRun.h | 46 +++++ .../TestImpactClientTestSelection.h | 47 +++++ .../TestImpactConfiguration.h | 137 +++++++++++++ .../TestImpactConfigurationException.h | 26 +++ .../TestImpactFramework/TestImpactRepoPath.h | 42 ++++ .../TestImpactFramework/TestImpactRuntime.h | 183 ++++++++++++++++++ .../TestImpactRuntimeException.h | 26 +++ .../TestImpactTestSequence.h | 91 +++++++++ .../TestImpactFramework/TestImpactUtils.h | 60 ++++++ 13 files changed, 915 insertions(+) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactChangeList.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactChangeListException.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactChangeListSerializer.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestRun.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfigurationException.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntimeException.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactChangeList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactChangeList.h new file mode 100644 index 0000000000..e36b343544 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactChangeList.h @@ -0,0 +1,28 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +namespace TestImpact +{ + //! Representation of the file CRUD operations of a given set of source changes. + struct ChangeList + { + AZStd::vector m_createdFiles; //!< Files that were newly created. + AZStd::vector m_updatedFiles; //!< Files that were updated. + AZStd::vector m_deletedFiles; //!< Files that were deleted. + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactChangeListException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactChangeListException.h new file mode 100644 index 0000000000..d9139cf47f --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactChangeListException.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Exception for change list operations. + class ChangeListException + : public Exception + { + public: + using Exception::Exception; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactChangeListSerializer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactChangeListSerializer.h new file mode 100644 index 0000000000..13c41db564 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactChangeListSerializer.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +namespace TestImpact +{ + //! Serializes the specified change list to JSON format. + AZStd::string SerializeChangeList(const ChangeList& changeList); + + //! Deserializes a change list from the specified test run data in JSON format. + ChangeList DeserializeChangeList(const AZStd::string& changeListString); +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h new file mode 100644 index 0000000000..e5cadcbbd2 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h @@ -0,0 +1,177 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + namespace Client + { + //! Represents a test target that failed, either due to failing to execute, completing in an abnormal state or completing with failing tests. + class TargetFailure + { + public: + TargetFailure(const AZStd::string& targetName); + + //! Returns the name of the test target this failure pertains to. + const AZStd::string& GetTargetName() const; + private: + AZStd::string m_targetName; + }; + + //! Represents a test target that failed to execute. + class ExecutionFailure + : public TargetFailure + { + public: + ExecutionFailure(const AZStd::string& targetName, const AZStd::string& command); + + //! Returns the command string used to execute this test target. + const AZStd::string& GetCommandString() const; + private: + AZStd::string m_commandString; + }; + + //! Represents a test target that terminated abnormally. + class LauncherFailure + : public ExecutionFailure + { + public: + LauncherFailure(const AZStd::string& targetName, const AZStd::string& command, int returnCode); + + //! The return code of the test target that terminated abnormally. + int GetReturnCode() const; + private: + int m_returnCode; + }; + + //! Represents an individual test of a test target that failed. + class TestFailure + { + public: + TestFailure(const AZStd::string& testName, const AZStd::string& errorMessage); + + //! Returns the name of the test that failed. + const AZStd::string& GetName() const; + + //! Returns the error message of the test that failed. + const AZStd::string& GetErrorMessage() const; + + private: + AZStd::string m_name; + AZStd::string m_errorMessage; + }; + + //! Represents a collection of tests that failed. + //! @note Only the failing tests are included in the collection. + class TestCaseFailure + { + public: + TestCaseFailure(const AZStd::string& testCaseName, AZStd::vector&& testFailures); + + //! Returns the name of the test case containing the failing tests. + const AZStd::string& GetName() const; + + //! Returns the collection of tests in this test case that failed. + const AZStd::vector& GetTestFailures() const; + + private: + AZStd::string m_name; + AZStd::vector m_testFailures; + }; + + //! Represents a test target that launched successfully but contains failing tests. + class TestRunFailure + : public TargetFailure + { + public: + TestRunFailure(const AZStd::string& targetName, AZStd::vector&& testFailures); + + //! Returns the total number of failing tests in this run. + size_t GetNumTestFailures() const; + + //! Returns the test cases in this run containing failing tests. + const AZStd::vector& GetTestCaseFailures() const; + + private: + AZStd::vector m_testCaseFailures; + }; + + //! Base class for reporting failing test sequences. + class SequenceFailure + { + public: + SequenceFailure( + AZStd::vector&& executionFailures, + AZStd::vector&& launcherFailures, + AZStd::vector&& unexecutionTests); + + //! Returns the test targets in this sequence that failed to execute. + const AZStd::vector& GetExecutionFailures() const; + + //! Returns the test targets in this sequence that terminated abnormally. + const AZStd::vector& GetLauncherFailures() const; + + //! Returns the test targets in this sequence that were not executed due to the sequence terminating prematurely. + const AZStd::vector& GetUnexecutedTest() const; + + private: + AZStd::vector m_executionFailures; + AZStd::vector m_launcherFailures; + AZStd::vector m_unexecutionTests; + }; + + //! Represents the report for a failed regular test sequence run without test impact analysis. + class RegularSequenceFailure + : public SequenceFailure + { + public: + RegularSequenceFailure( + AZStd::vector&& executionFailures, + AZStd::vector&& launcherFailures, + AZStd::vector&& testRunFailures, + AZStd::vector&& unexecutionTests); + + //! Returns the test targets that contain failing tests. + const AZStd::vector& GetTestRunFailures() const; + + private: + AZStd::vector m_testRunFailures; + }; + + //! Represents the report for a failed test sequence run with test impact analysis. + class ImpactAnalysisSequenceFailure + : public SequenceFailure + { + public: + ImpactAnalysisSequenceFailure( + AZStd::vector&& executionFailures, + AZStd::vector&& launcherFailures, + AZStd::vector&& selectedTestRunFailures, + AZStd::vector&& discardedTestRunFailures, + AZStd::vector&& unexecutionTests); + + //! Returns the test targets that were selected to run but contain failing tests. + const AZStd::vector GetSelectedTestRunFailures() const; + + //! Returns the test targets that were not selected but still run but contain failing tests. + const AZStd::vector GetDiscardedTestRunFailures() const; + + private: + AZStd::vector m_selectedTestRunFailures; + AZStd::vector m_discardedTestRunFailures; + }; + } // namespace Client +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestRun.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestRun.h new file mode 100644 index 0000000000..7c525e00ba --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestRun.h @@ -0,0 +1,46 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#pragma once + +namespace TestImpact +{ + namespace Client + { + //! Result of a test run. + enum class TestRunResult + { + NotRun, //!< The test run was not executed due to the test sequence terminating prematurely. + FailedToExecute, //!< The test run failed to execute either due to the target binary missing or incorrect arguments. + Timeout, //!< The test run timed out whilst in flight before being able to complete its run. + TestFailures, //!< The test run completed its run but there were failing tests. + AllTestsPass //!< The test run completed its run and all tests passed. + }; + + class TestRun + { + public: + TestRun(const AZStd::string& name, TestRunResult result, AZStd::chrono::milliseconds duration); + const AZStd::string& GetTargetName() const; + TestRunResult GetResult() const; + AZStd::chrono::milliseconds GetDuration() const; + + private: + AZStd::string m_targetName; + TestRunResult m_result; + AZStd::chrono::milliseconds m_duration; + }; + } // namespace Client +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h new file mode 100644 index 0000000000..a42a67f0d7 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h @@ -0,0 +1,47 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#pragma once + +namespace TestImpact +{ + namespace Client + { + //! The set of test targets selected to run regardless of whether or not the test targets are to be exclude either for being on the master exclude + //! list and/or being part of a test suite excluded from this run. + //! @note Only the included test targets will be run. The excluded test targetss, although selected, will not be run. + class TestRunSelection + { + public: + TestRunSelection(AZStd::vector&& includedTests, AZStd::vector&& excludedTests); + + //! Returns the test runs that were selected to be run and will actually be run. + const AZStd::vector& GetIncludededTestRuns() const; + + //! Returns the test runs that were selected to be run but will not actually be run. + const AZStd::vector& GetExcludedTestRuns() const; + + //! Returns the number of selected test runs that will be run. + size_t GetNumIncludedTestRuns() const; + + //! Returns the number of selected test runs that will not be run. + size_t GetNumNumExcludedTestRuns() const; + + private: + AZStd::vector m_includedTestRuns; + AZStd::vector m_excludedTestRuns; + }; + } // namespace Client +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h new file mode 100644 index 0000000000..1a4ae73ce0 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h @@ -0,0 +1,137 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +#include +#include + +namespace TestImpact +{ + //! Meta-data about the configuration. + struct ConfigMeta + { + AZStd::string m_platform; //!< The platform for which the configuration pertains to. + }; + + //! Repository configuration. + struct RepoConfig + { + RepoPath m_root; //!< The absolute path to the repository root. + }; + + //! Test impact analysis framework workspace configuration. + struct WorkspaceConfig + { + //! Temporary workspace configuration. + struct Temp + { + //! Paths relative to root. + struct RelativePaths + { + RepoPath m_artifactDirectory; //!< Path to read and write runtime artifacts to and from. + }; + + RepoPath m_root; //!< Path to the temporary workspace (cleaned prior to use). + RelativePaths m_relativePaths; + }; + + struct Persistent + { + //! Paths relative to root. + struct RelativePaths + { + RepoPath m_sparTIAFile; //!< Path to the test impact analysis data. + RepoPath m_enumerationCacheDirectory; //!< Path to the test enumerations cache. + }; + + RepoPath m_root; //!< Path to the persistent workspace tracked by the repository. + RelativePaths m_relativePaths; + }; + + Temp m_temp; + Persistent m_persistent; + }; + + //! Build target descriptor configuration. + struct BuildTargetDescriptorConfig + { + RepoPath m_mappingDirectory; //!< Path to the source to target mapping files. + AZStd::vector m_staticInclusionFilters; //!< File extensions to include for static files. + AZStd::string m_inputOutputPairer; //!< Regex for matching autogen input files with autogen outputs files. + AZStd::vector m_inputInclusionFilters; //!< File extensions fo include for autogen input files. + }; + + //! Dependency graph configuration. + struct DependencyGraphDataConfig + { + RepoPath m_graphDirectory; //!< Path to the dependency graph files. + AZStd::string m_targetDependencyFileMatcher; //!< Regex for matching dependency graph files to build targets. + AZStd::string m_targetVertexMatcher; //!< Regex form matching dependency graph vertices to build targets. + }; + + //! Test target meta configuration. + struct TestTargetMetaConfig + { + RepoPath m_metaFile; //!< Path to the master test target meta file. + }; + + //! Test engine configuration. + struct TestEngineConfig + { + //!< Test runner configuration. + struct TestRunner + { + RepoPath m_binary; //!< Path to the test runner binary. + }; + + //!< Test instrumentation configuration. + struct Instrumentation + { + RepoPath m_binary; //!< Path to the test instrumentation binary. + }; + + TestRunner m_testRunner; + Instrumentation m_instrumentation; + }; + + //!< Build target configuration. + struct TargetConfig + { + //!< Test target sharding configuration. + struct ShardedTarget + { + AZStd::string m_name; //!< Name of test target this sharding configuration applies to. + ShardConfiguration m_configuration; //!< The shard configuration to use. + }; + + RepoPath m_outputDirectory; //!< Path to the test target binary directory. + AZStd::vector m_excludedTestTargets; //!< Test targets to always exclude from test run sequences. + AZStd::vector m_shardedTestTargets; //!< Test target shard configurations (opt-in). + }; + + //! Runtime configuration. + struct RuntimeConfig + { + ConfigMeta m_meta; + RepoConfig m_repo; + WorkspaceConfig m_workspace; + BuildTargetDescriptorConfig m_buildTargetDescriptor; + DependencyGraphDataConfig m_dependencyGraphData; + TestTargetMetaConfig m_testTargetMeta; + TestEngineConfig m_testEngine; + TargetConfig m_target; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfigurationException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfigurationException.h new file mode 100644 index 0000000000..15d7913997 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfigurationException.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Exception for configuration operations. + class ConfigurationException + : public Exception + { + public: + using Exception::Exception; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h new file mode 100644 index 0000000000..e7e0ba685f --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h @@ -0,0 +1,42 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Wrapper class to ensure that all paths have the same path separator regardless of how they are sourced. This is critical + //! to the test impact analysis data as otherwise querying/retrieving test impact analysis data for the same source albeit + //! with different path separators will be considered different files entirely. + class RepoPath + : public AZ::IO::Path + { + public: + constexpr RepoPath() = default; + constexpr RepoPath(const RepoPath&) = default; + constexpr RepoPath(RepoPath&&) = default; + constexpr RepoPath(const string_type&) noexcept; + constexpr RepoPath(const value_type*) noexcept; + constexpr RepoPath(const AZ::IO::PathView&); + constexpr RepoPath(const AZ::IO::Path&); + + RepoPath& operator=(const RepoPath&) noexcept = default; + RepoPath& operator=(const string_type&) noexcept; + RepoPath& operator=(const value_type*) noexcept; + RepoPath& operator=(const AZ::IO::Path& str) noexcept; + + using AZ::IO::Path::operator AZ::IO::PathView; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h new file mode 100644 index 0000000000..a42da8dc38 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h @@ -0,0 +1,183 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace TestImpact +{ + class DynamicDependencyMap; + class TestEngine; + class TestTarget; + + //! Callback for a test sequence that isn't using test impact analysis to determine selected tests. + //! @param tests The tests that will be run for this sequence. + using TestSequenceStartCallback = AZStd::function; + + //! Callback for a test sequence using test impact analysis. + //! @param selectedTests The tests that have been selected for this run by test impact analysis. + //! @param discardedTests The tests that have been rejected for this run by test impact analysis. + //! @param draftedTests The tests that have been drafted in for this run due to requirements outside of test impact analysis + //! (e.g. test targets that have been added to the repository since the last test impact analysis sequence or test that failed + //! to execute previously). + //! These tests will be run with coverage instrumentation. + //! @note discardedTests and draftedTests may contain overlapping tests. + using ImpactAnalysisTestSequenceStartCallback = AZStd::function&& discardedTests, + AZStd::vector&& draftedTests)>; + + //! Callback for a test sequence using test impact analysis. + //! @param selectedTests The tests that have been selected for this run by test impact analysis. + //! @param discardedTests The tests that have been rejected for this run by test impact analysis. + //! These tests will not be run without coverage instrumentation unless there is an entry in the draftedTests list. + //! @param draftedTests The tests that have been drafted in for this run due to requirements outside of test impact analysis + //! (e.g. test targets that have been added to the repository since the last test impact analysis sequence or test that failed + //! to execute previously). + //! @note discardedTests and draftedTests may contain overlapping tests. + using SafeImpactAnalysisTestSequenceStartCallback = AZStd::function&& draftedTests)>; + + //! Callback for end of a test sequence. + //! @param failureReport The test runs that failed for any reason during this sequence. + //! @param duration The total duration of this test sequence. + using TestSequenceCompleteCallback = AZStd::function; + + //! Callback for end of a test impact analysis test sequence. + //! @param failureReport The test runs that failed for any reason during this sequence. + //! @param duration The total duration of this test sequence. + using ImpactAnalysisTestSequenceCompleteCallback = AZStd::function; + + //! Callback for test runs that have completed for any reason + //! test The test that has completed. + using TestCompleteCallback = AZStd::function; + + //! The API exposed to the client responsible for all test runs and persistent data management. + class Runtime + { + public: + //! Constructs a runtime with the specified configuration and policies. + //! @param config The configuration used for this runtime instance. + //! @param executionFailurePolicy Determines how to handle test targets that fail to execute. + //! @param executionFailureDraftingPolicy Determines how test targets that previously failed to execute are drafted into subsequent test sequences. + //! @param testFailurePolicy Determines how to handle test targets that report test failures. + //! @param integrationFailurePolicy Determines how to handle instances where the build system model and/or test impact analysis data is compromised. + //! @param testShardingPolicy Determines how to handle test targets that have opted in to test sharding. + Runtime( + RuntimeConfig&& config, + Policy::ExecutionFailure executionFailurePolicy, + Policy::ExecutionFailureDrafting executionFailureDraftingPolicy, + Policy::TestFailure testFailurePolicy, + Policy::IntegrityFailure integrationFailurePolicy, + Policy::TestSharding testShardingPolicy, + TargetOutputCapture targetOutputCapture, + AZStd::optional maxConcurrency = AZStd::nullopt); + + ~Runtime(); + + //! Runs a test sequence where all tests with a matching suite in the suite filter and also not on the excluded list are selected. + //! @param suitesFilter The test suites that will be included in the test selection. + //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). + //! @param testTargetTimeout The maximum duration the entire test sequence may run for (infinite if empty). + //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. + //! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed. + //! @param testRunCompleteCallback The client function to be called after an individual test run has compelted. + TestSequenceResult RegularTestSequence( + const AZStd::unordered_set suitesFilter, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional testSequenceStartCallback, + AZStd::optional testSequenceCompleteCallback, + AZStd::optional testRunCompleteCallback); + + //! Runs a test sequence where tests are selected according to test impact analysis so long as they are not on the excluded list. + //! @param changeList The change list used to determine the tests to select. + //! @param testPrioritizationPolicy Determines how selected tests will be prioritized. + //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). + //! @param testTargetTimeout The maximum duration the entire test sequence may run for (infinite if empty). + //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. + //! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed. + //! @param testRunCompleteCallback The client function to be called after an individual test run has compelted. + TestSequenceResult ImpactAnalysisTestSequence( + const ChangeList& changeList, + Policy::TestPrioritization testPrioritizationPolicy, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional testSequenceStartCallback, + AZStd::optional testSequenceCompleteCallback, + AZStd::optional testRunCompleteCallback); + + //! Runs a test sequence as per the ImpactAnalysisTestSequence where the tests not selected are also run (albeit without instrumentation). + //! @param changeList The change list used to determine the tests to select. + //! @param suitesFilter The test suites that will be included in the test selection. + //! @param testPrioritizationPolicy Determines how selected tests will be prioritized. + //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). + //! @param testTargetTimeout The maximum duration the entire test sequence may run for (infinite if empty). + //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. + //! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed. + //! @param testRunCompleteCallback The client function to be called after an individual test run has compelted. + TestSequenceResult SafeImpactAnalysisTestSequence( + const ChangeList& changeList, + const AZStd::unordered_set suitesFilter, + Policy::TestPrioritization testPrioritizationPolicy, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional testSequenceStartCallback, + AZStd::optional testSequenceCompleteCallback, + AZStd::optional testRunCompleteCallback); + + //! Runs all tests not on the excluded list and uses their coverage data to seed the test impact analysis data (ant existing data will be overwritten). + //! @param testTargetTimeout The maximum duration the entire test sequence may run for (infinite if empty). + //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. + //! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed. + //! @param testRunCompleteCallback The client function to be called after an individual test run has compelted. + TestSequenceResult SeededTestSequence( + AZStd::optional globalTimeout, + AZStd::optional testSequenceStartCallback, + AZStd::optional testSequenceCompleteCallback, + AZStd::optional testRunCompleteCallback); + + //! Returns true if the runtime has test impact analysis data (either preexisting or generated). + bool HasImpactAnalysisData() const; + + private: + RuntimeConfig m_config; + Policy::ExecutionFailure m_executionFailurePolicy; + Policy::ExecutionFailureDrafting m_executionFailureDraftingPolicy; + Policy::TestFailure m_testFailurePolicy; + Policy::IntegrityFailure m_integrationFailurePolicy; + Policy::TestSharding m_testShardingPolicy; + TargetOutputCapture m_targetOutputCapture; + size_t m_maxConcurrency; + AZStd::unique_ptr m_dynamicDependencyMap; + AZStd::unique_ptr m_testEngine; + AZStd::unordered_set m_testTargetExcludeList; + AZStd::unordered_set m_testTargetShardList; + bool m_hasImpactAnalysisData = false; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntimeException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntimeException.h new file mode 100644 index 0000000000..f178df685d --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntimeException.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + //! Exception for runtime related exceptions. + class RuntimeException + : public Exception + { + public: + using Exception::Exception; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h new file mode 100644 index 0000000000..b9030d67e0 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h @@ -0,0 +1,91 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +namespace TestImpact +{ + namespace Policy + { + //! Policy for handling of test targets that fail to execute (e.g. due to the binary not being found). + //! @note Test targets that fail to execute will be tagged such that their execution can be attempted at a later date. This is + //! important as otherwise it would be erroneously assumed that they cover no sources due to having no entries in the dynamic + //! dependency map. + enum class ExecutionFailure + { + Abort, //!< Abort the test sequence and report a failure. + Continue, //!< Continue the test sequence but treat the execution failures as test failures after the run. + Ignore //!< Continue the test sequence and ignore the execution failures. + }; + + //! Policy for reattempting the execution of test targets that failed to execute in previous runs. + enum class ExecutionFailureDrafting + { + Never, //!< Do not attempt to execute historic execution failures. + Always //!< Reattempt the exectution of historic execution failures. + }; + + //! Policy for prioritizing selected tests. + enum class TestPrioritization + { + None, //!< Do not attempt any test prioritization. + DependencyLocality //!< Prioritize test targets according to the locality of the production targets they cover in the build dependency graph. + }; + + //! Policy for handling test targets that report failing tests. + enum class TestFailure + { + Abort, //!< Abort the test sequence and report the test failure. + Continue //!< Continue the test sequence and report the test failures after the run. + }; + + //! Policy for handling integrity failures of the dynamic dependency map and the source to target mappings. + enum class IntegrityFailure + { + Abort, //!< Abort the test sequence and report the test failure. + Continue //!< Continue the test sequence and report the test failures after the run. + }; + + //! Policy for sharding test targets that have been marked for test sharding. + enum class TestSharding + { + Never, //!< Do not shard any test targets. + Always //!< Shard all test targets that have been marked for test sharding. + }; + } + + //! Standard output capture of test target runs. + enum class TargetOutputCapture + { + None, //!< Do not capture any output. + StdOut, //!< Send captured output to standard output + File, //!< Write captured output to file. + StdOutAndFile //!< Send captured output to standard output and write to file. + }; + + enum class ShardConfiguration + { + Never, //!< Never shard this test target. + FixtureContiguous, //!< Each shard contains contiguous fixtures of tests (safest but least optimal). + TestContiguous, //!< Each shard contains contiguous tests agnostic of fixtures. + FixtureInterleaved, //!< Fixtures of tests are interleaved across shards. + TestInterleaved //!< Tests are interlaced across shards agnostic of fixtures (fastest but prone to inter-test dependency problems). + }; + + //! Result of a test sequence that was run. + enum class TestSequenceResult + { + Success, //! All tests ran with no failures. + Failure, //! One or more tests failed and/or timed out and/or failed to launch (if execution failure policy is not Ignore) and/or an integrity failure was encountered. + Timeout //! The global timeout for the sequence was exceeded. + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h new file mode 100644 index 0000000000..bd62e70a0d --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h @@ -0,0 +1,60 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include + +#pragma once + +namespace TestImpact +{ + //! Attempts to read the contents of the specified file into a string. + //! @tparam ExceptionType The exception type to throw upon failure. + //! @param path The path to the file to read the contents of. + //! @returns The contents of the file. + template + AZStd::string ReadFileContents(const RepoPath& path) + { + const auto fileSize = AZ::IO::SystemFile::Length(path.c_str()); + AZ_TestImpact_Eval(fileSize > 0, ExceptionType, AZStd::string::format("File %s does not exist", path.c_str())); + + AZStd::vector buffer(fileSize + 1); + buffer[fileSize] = '\0'; + AZ_TestImpact_Eval(AZ::IO::SystemFile::Read(path.c_str(), buffer.data()), ExceptionType, AZStd::string::format("Could not read contents of file %s", path.c_str())); + + return AZStd::string(buffer.begin(), buffer.end()); + } + + //! Attempts to write the contents of the specified string to a file. + //! @tparam ExceptionType The exception type to throw upon failure. + //! @param contents The contents to write to the file. + //! @param path The path to the file to write the contents to. + template + void WriteFileContents(const AZStd::string& contents, const RepoPath& path) + { + AZ::IO::SystemFile file; + const AZStd::vector bytes(contents.begin(), contents.end()); + AZ_TestImpact_Eval( + file.Open(path.c_str(), + AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY), + ExceptionType, + AZStd::string::format("Couldn't open file %s for writing", path.c_str())); + + AZ_TestImpact_Eval(file.Write(bytes.data(), bytes.size()), ExceptionType, AZStd::string::format("Couldn't write contents for file %s", path.c_str())); + + return; + } +} // namespace TestImpact From b33569638e9a377a13dbcd8058f11afda5dd462f Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 20 May 2021 10:06:14 +0100 Subject: [PATCH 038/233] Address PR comments --- .../TestImpactClientTestSelection.h | 4 ++-- .../TestImpactFramework/TestImpactConfiguration.h | 11 +++++------ .../TestImpactFramework/TestImpactTestSequence.h | 1 + .../Include/TestImpactFramework/TestImpactUtils.h | 5 ++++- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h index a42a67f0d7..f251e7117d 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h @@ -19,9 +19,9 @@ namespace TestImpact { namespace Client { - //! The set of test targets selected to run regardless of whether or not the test targets are to be exclude either for being on the master exclude + //! The set of test targets selected to run regardless of whether or not the test targets are to be excluded either for being on the primary exclude //! list and/or being part of a test suite excluded from this run. - //! @note Only the included test targets will be run. The excluded test targetss, although selected, will not be run. + //! @note Only the included test targets will be run. The excluded test targets, although selected, will not be run. class TestRunSelection { public: diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h index 1a4ae73ce0..1327ddbdff 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h @@ -85,19 +85,19 @@ namespace TestImpact //! Test target meta configuration. struct TestTargetMetaConfig { - RepoPath m_metaFile; //!< Path to the master test target meta file. + RepoPath m_metaFile; //!< Path to the test target meta file. }; //! Test engine configuration. struct TestEngineConfig { - //!< Test runner configuration. + //! Test runner configuration. struct TestRunner { RepoPath m_binary; //!< Path to the test runner binary. }; - //!< Test instrumentation configuration. + //! Test instrumentation configuration. struct Instrumentation { RepoPath m_binary; //!< Path to the test instrumentation binary. @@ -107,10 +107,10 @@ namespace TestImpact Instrumentation m_instrumentation; }; - //!< Build target configuration. + //! Build target configuration. struct TargetConfig { - //!< Test target sharding configuration. + //! Test target sharding configuration. struct ShardedTarget { AZStd::string m_name; //!< Name of test target this sharding configuration applies to. @@ -122,7 +122,6 @@ namespace TestImpact AZStd::vector m_shardedTestTargets; //!< Test target shard configurations (opt-in). }; - //! Runtime configuration. struct RuntimeConfig { ConfigMeta m_meta; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h index b9030d67e0..8b1b54e7f4 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h @@ -72,6 +72,7 @@ namespace TestImpact StdOutAndFile //!< Send captured output to standard output and write to file. }; + //! Configuration for test targets that opt in to test sharding. enum class ShardConfiguration { Never, //!< Never shard this test target. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h index bd62e70a0d..8580582a12 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h @@ -33,7 +33,10 @@ namespace TestImpact AZStd::vector buffer(fileSize + 1); buffer[fileSize] = '\0'; - AZ_TestImpact_Eval(AZ::IO::SystemFile::Read(path.c_str(), buffer.data()), ExceptionType, AZStd::string::format("Could not read contents of file %s", path.c_str())); + AZ_TestImpact_Eval( + AZ::IO::SystemFile::Read(path.c_str(), buffer.data()), + ExceptionType, + AZStd::string::format("Could not read contents of file %s", path.c_str())); return AZStd::string(buffer.begin(), buffer.end()); } From feb2909508a19dbe61f3917d185f34591bd4a476 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 20 May 2021 16:38:56 +0100 Subject: [PATCH 039/233] Address PR comments --- .../TestImpactClientFailureReport.h | 8 ++++---- .../TestImpactFramework/TestImpactConfiguration.h | 5 +++-- .../Include/TestImpactFramework/TestImpactRepoPath.h | 2 +- .../Include/TestImpactFramework/TestImpactRuntime.h | 12 ++++++------ .../TestImpactFramework/TestImpactTestSequence.h | 6 +++--- .../Include/TestImpactFramework/TestImpactUtils.h | 5 ++--- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h index e5cadcbbd2..d2ab863d86 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h @@ -116,7 +116,7 @@ namespace TestImpact SequenceFailure( AZStd::vector&& executionFailures, AZStd::vector&& launcherFailures, - AZStd::vector&& unexecutionTests); + AZStd::vector&& unexecutedTests); //! Returns the test targets in this sequence that failed to execute. const AZStd::vector& GetExecutionFailures() const; @@ -130,7 +130,7 @@ namespace TestImpact private: AZStd::vector m_executionFailures; AZStd::vector m_launcherFailures; - AZStd::vector m_unexecutionTests; + AZStd::vector m_unexecutedTestsTests; }; //! Represents the report for a failed regular test sequence run without test impact analysis. @@ -142,7 +142,7 @@ namespace TestImpact AZStd::vector&& executionFailures, AZStd::vector&& launcherFailures, AZStd::vector&& testRunFailures, - AZStd::vector&& unexecutionTests); + AZStd::vector&& unexecutedTests); //! Returns the test targets that contain failing tests. const AZStd::vector& GetTestRunFailures() const; @@ -161,7 +161,7 @@ namespace TestImpact AZStd::vector&& launcherFailures, AZStd::vector&& selectedTestRunFailures, AZStd::vector&& discardedTestRunFailures, - AZStd::vector&& unexecutionTests); + AZStd::vector&& unexecutedTests); //! Returns the test targets that were selected to run but contain failing tests. const AZStd::vector GetSelectedTestRunFailures() const; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h index 1327ddbdff..3406789c9e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h @@ -48,7 +48,8 @@ namespace TestImpact RelativePaths m_relativePaths; }; - struct Persistent + //! Active persistent data workspace configuration. + struct Active { //! Paths relative to root. struct RelativePaths @@ -62,7 +63,7 @@ namespace TestImpact }; Temp m_temp; - Persistent m_persistent; + Active m_active; }; //! Build target descriptor configuration. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h index e7e0ba685f..ccba3f677d 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h @@ -26,7 +26,7 @@ namespace TestImpact public: constexpr RepoPath() = default; constexpr RepoPath(const RepoPath&) = default; - constexpr RepoPath(RepoPath&&) = default; + constexpr RepoPath(RepoPath&&) noexcept = default; constexpr RepoPath(const string_type&) noexcept; constexpr RepoPath(const value_type*) noexcept; constexpr RepoPath(const AZ::IO::PathView&); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h index a42da8dc38..8221eba834 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h @@ -73,7 +73,7 @@ namespace TestImpact //! @param duration The total duration of this test sequence. using ImpactAnalysisTestSequenceCompleteCallback = AZStd::function; - //! Callback for test runs that have completed for any reason + //! Callback for test runs that have completed for any reason. //! test The test that has completed. using TestCompleteCallback = AZStd::function; @@ -106,7 +106,7 @@ namespace TestImpact //! @param testTargetTimeout The maximum duration the entire test sequence may run for (infinite if empty). //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. //! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed. - //! @param testRunCompleteCallback The client function to be called after an individual test run has compelted. + //! @param testRunCompleteCallback The client function to be called after an individual test run has completed. TestSequenceResult RegularTestSequence( const AZStd::unordered_set suitesFilter, AZStd::optional testTargetTimeout, @@ -122,7 +122,7 @@ namespace TestImpact //! @param testTargetTimeout The maximum duration the entire test sequence may run for (infinite if empty). //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. //! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed. - //! @param testRunCompleteCallback The client function to be called after an individual test run has compelted. + //! @param testRunCompleteCallback The client function to be called after an individual test run has completed. TestSequenceResult ImpactAnalysisTestSequence( const ChangeList& changeList, Policy::TestPrioritization testPrioritizationPolicy, @@ -140,7 +140,7 @@ namespace TestImpact //! @param testTargetTimeout The maximum duration the entire test sequence may run for (infinite if empty). //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. //! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed. - //! @param testRunCompleteCallback The client function to be called after an individual test run has compelted. + //! @param testRunCompleteCallback The client function to be called after an individual test run has completed. TestSequenceResult SafeImpactAnalysisTestSequence( const ChangeList& changeList, const AZStd::unordered_set suitesFilter, @@ -155,7 +155,7 @@ namespace TestImpact //! @param testTargetTimeout The maximum duration the entire test sequence may run for (infinite if empty). //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. //! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed. - //! @param testRunCompleteCallback The client function to be called after an individual test run has compelted. + //! @param testRunCompleteCallback The client function to be called after an individual test run has completed. TestSequenceResult SeededTestSequence( AZStd::optional globalTimeout, AZStd::optional testSequenceStartCallback, @@ -173,7 +173,7 @@ namespace TestImpact Policy::IntegrityFailure m_integrationFailurePolicy; Policy::TestSharding m_testShardingPolicy; TargetOutputCapture m_targetOutputCapture; - size_t m_maxConcurrency; + size_t m_maxConcurrency = 0; AZStd::unique_ptr m_dynamicDependencyMap; AZStd::unique_ptr m_testEngine; AZStd::unordered_set m_testTargetExcludeList; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h index 8b1b54e7f4..751db43987 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h @@ -85,8 +85,8 @@ namespace TestImpact //! Result of a test sequence that was run. enum class TestSequenceResult { - Success, //! All tests ran with no failures. - Failure, //! One or more tests failed and/or timed out and/or failed to launch (if execution failure policy is not Ignore) and/or an integrity failure was encountered. - Timeout //! The global timeout for the sequence was exceeded. + Success, //!< All tests ran with no failures. + Failure, //!< One or more tests failed and/or timed out and/or failed to launch and/or an integrity failure was encountered. + Timeout //!< The global timeout for the sequence was exceeded. }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h index 8580582a12..d734d7a70e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h @@ -56,8 +56,7 @@ namespace TestImpact ExceptionType, AZStd::string::format("Couldn't open file %s for writing", path.c_str())); - AZ_TestImpact_Eval(file.Write(bytes.data(), bytes.size()), ExceptionType, AZStd::string::format("Couldn't write contents for file %s", path.c_str())); - - return; + AZ_TestImpact_Eval( + file.Write(bytes.data(), bytes.size()), ExceptionType, AZStd::string::format("Couldn't write contents for file %s", path.c_str())); } } // namespace TestImpact From d732b7d0ced6a8b01defbd19c331ad286194ce76 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 21 May 2021 09:00:57 +0100 Subject: [PATCH 040/233] General refactor of runtime classes. --- ...TestImpactBuildTargetDescriptorFactory.cpp | 31 ++++--- .../TestImpactBuildTargetDescriptorFactory.h | 8 +- .../TestImpactTestTargetMetaMapFactory.cpp | 10 +- .../Static/TestImpactBuildTargetDescriptor.h | 11 ++- .../Static/TestImpactTestTargetMeta.h | 3 + .../TestImpactDynamicDependencyMap.cpp | 32 ++++--- .../TestImpactDynamicDependencyMap.h | 9 +- .../TestImpactSourceCoveringTestsList.cpp | 24 ++++- .../TestImpactSourceCoveringTestsList.h | 13 ++- .../Dependency/TestImpactSourceDependency.cpp | 4 +- .../Dependency/TestImpactSourceDependency.h | 6 +- .../Process/TestImpactWin32_Process.cpp | 2 +- .../Process/JobRunner/TestImpactProcessJob.h | 92 +++---------------- .../JobRunner/TestImpactProcessJobInfo.h | 24 +++-- .../JobRunner/TestImpactProcessJobMeta.cpp | 61 ++++++++++++ .../JobRunner/TestImpactProcessJobMeta.h | 68 ++++++++++++++ .../JobRunner/TestImpactProcessJobRunner.h | 13 ++- .../Scheduler/TestImpactProcessScheduler.cpp | 14 +-- .../Scheduler/TestImpactProcessScheduler.h | 18 ++-- .../Source/Process/TestImpactProcessInfo.cpp | 6 +- .../Source/Process/TestImpactProcessInfo.h | 11 ++- .../Source/Target/TestImpactBuildTarget.cpp | 2 +- .../Source/Target/TestImpactBuildTarget.h | 2 +- .../Source/Target/TestImpactBuildTargetList.h | 11 ++- .../Source/Target/TestImpactTestTarget.cpp | 10 ++ .../Code/Source/Target/TestImpactTestTarget.h | 6 ++ 26 files changed, 318 insertions(+), 173 deletions(-) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp index 5e1f2bc890..1bb88912d1 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp @@ -10,18 +10,19 @@ * */ +#include + #include #include -#include #include #include namespace TestImpact { AutogenSources PairAutogenSources( - const AZStd::vector& inputSources, - const AZStd::vector& outputSources, + const AZStd::vector& inputSources, + const AZStd::vector& outputSources, const AZStd::string& autogenMatcher) { AutogenSources autogenSources; @@ -65,8 +66,8 @@ namespace TestImpact BuildTargetDescriptor BuildTargetDescriptorFactory( const AZStd::string& buildTargetData, - const AZStd::vector& staticSourceExtensionExcludes, - const AZStd::vector& autogenInputExtensionExcludes, + const AZStd::vector& staticSourceExtensionIncludes, + const AZStd::vector& autogenInputExtensionIncludes, const AZStd::string& autogenMatcher) { // Keys for pertinent JSON node and attribute names @@ -118,14 +119,14 @@ namespace TestImpact const auto& staticSources = sources[Keys[StaticKey]].GetArray(); if (!staticSources.Empty()) { - buildTargetDescriptor.m_sources.m_staticSources = AZStd::vector(); + buildTargetDescriptor.m_sources.m_staticSources = AZStd::vector(); for (const auto& source : staticSources) { - const AZ::IO::Path sourcePath = AZ::IO::Path(source.GetString()); + const RepoPath sourcePath = RepoPath(source.GetString()); if (AZStd::find( - staticSourceExtensionExcludes.begin(), staticSourceExtensionExcludes.end(), sourcePath.Extension().Native()) == - staticSourceExtensionExcludes.end()) + staticSourceExtensionIncludes.begin(), staticSourceExtensionIncludes.end(), sourcePath.Extension().Native()) != + staticSourceExtensionIncludes.end()) { buildTargetDescriptor.m_sources.m_staticSources.emplace_back(AZStd::move(sourcePath)); } @@ -139,17 +140,17 @@ namespace TestImpact AZ_TestImpact_Eval( !inputSources.Empty() && !outputSources.Empty(), ArtifactException, "Autogen malformed, input or output sources are empty"); - AZStd::vector inputPaths; - AZStd::vector outputPaths; + AZStd::vector inputPaths; + AZStd::vector outputPaths; inputPaths.reserve(inputSources.Size()); outputPaths.reserve(outputSources.Size()); for (const auto& source : inputSources) { - const AZ::IO::Path sourcePath = AZ::IO::Path(source.GetString()); + const RepoPath sourcePath = RepoPath(source.GetString()); if (AZStd::find( - autogenInputExtensionExcludes.begin(), autogenInputExtensionExcludes.end(), sourcePath.Extension().Native()) == - autogenInputExtensionExcludes.end()) + autogenInputExtensionIncludes.begin(), autogenInputExtensionIncludes.end(), sourcePath.Extension().Native()) != + autogenInputExtensionIncludes.end()) { inputPaths.emplace_back(AZStd::move(sourcePath)); } @@ -157,7 +158,7 @@ namespace TestImpact for (const auto& source : outputSources) { - outputPaths.emplace_back(AZStd::move(AZ::IO::Path(source.GetString()))); + outputPaths.emplace_back(AZStd::move(RepoPath(source.GetString()))); } buildTargetDescriptor.m_sources.m_autogenSources = PairAutogenSources(inputPaths, outputPaths, autogenMatcher); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h index b23e00478a..cf4e431a14 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h @@ -21,13 +21,13 @@ namespace TestImpact { //! Constructs a build target artifact from the specified build target data. //! @param buildTargetData The raw build target data in JSON format. - //! @param staticSourceExcludes The list of file extensions to exclude for static sources. - //! @param autogenInputExtentsionExcludes The list of file extensions to exclude for autogen input sources. + //! @param staticSourceIncludes The list of file extensions to include for static sources. + //! @param autogenInputExtentsionIncludes The list of file extensions to include for autogen input sources. //! @param autogenMatcher The regex pattern used to match autogen input filenames with output filenames. //! @return The constructed build target artifact. BuildTargetDescriptor BuildTargetDescriptorFactory( const AZStd::string& buildTargetData, - const AZStd::vector& staticSourceExtentsionExcludes, - const AZStd::vector& autogenInputExtentsionExcludes, + const AZStd::vector& staticSourceExtentsionIncludes, + const AZStd::vector& autogenInputExtentsionIncludes, const AZStd::string& autogenMatcher); } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp index db4d5f8563..8bbb5200bb 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp @@ -31,7 +31,9 @@ namespace TestImpact "launch_method", "test_runner", "stand_alone", - "name" + "name", + "command", + "timeout" }; enum @@ -43,7 +45,9 @@ namespace TestImpact LaunchMethodKey, TestRunnerKey, StandAloneKey, - NameKey + NameKey, + CommandKey, + TimeoutKey }; AZ_TestImpact_Eval(!masterTestListData.empty(), ArtifactException, "test meta-data cannot be empty"); @@ -61,6 +65,8 @@ namespace TestImpact { TestTargetMeta testMeta; testMeta.m_suite = test[Keys[SuiteKey]].GetString(); + testMeta.m_customArgs = test[Keys[CommandKey]].GetString(); + testMeta.m_timeout = AZStd::chrono::seconds{ test[Keys[TimeoutKey]].GetUint() }; if (const auto buildTypeString = test[Keys[LaunchMethodKey]].GetString(); strcmp(buildTypeString, Keys[TestRunnerKey]) == 0) { diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.h index e247b663f2..49820a4bdb 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.h @@ -12,7 +12,8 @@ #pragma once -#include +#include + #include #include #include @@ -22,8 +23,8 @@ namespace TestImpact //! Pairing between a given autogen input source and the generated output source(s). struct AutogenPairs { - AZStd::string m_input; - AZStd::vector m_outputs; + RepoPath m_input; + AZStd::vector m_outputs; }; using AutogenSources = AZStd::vector; @@ -31,7 +32,7 @@ namespace TestImpact //! Representation of a given built target's source list. struct TargetSources { - AZStd::vector m_staticSources; //!< Source files used to build this target (if any). + AZStd::vector m_staticSources; //!< Source files used to build this target (if any). AutogenSources m_autogenSources; //!< Autogen source files (if any). }; @@ -40,7 +41,7 @@ namespace TestImpact { AZStd::string m_name; //!< Build target name. AZStd::string m_outputName; //!< Output name (sans extension) of build target binary. - AZ::IO::Path m_path; //!< Path to build target location in source tree (relative to repository root). + RepoPath m_path; //!< Path to build target location in source tree (relative to repository root). }; //! Artifact produced by the build system for each build target. Contains source and output information about said targets. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetMeta.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetMeta.h index 57ae2fe894..bef9d9a44f 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetMeta.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetMeta.h @@ -14,6 +14,7 @@ #include #include +#include namespace TestImpact { @@ -28,6 +29,8 @@ namespace TestImpact struct TestTargetMeta { AZStd::string m_suite; + AZStd::string m_customArgs; + AZStd::chrono::milliseconds m_timeout = AZStd::chrono::milliseconds{ 0 }; LaunchMethod m_launchMethod = LaunchMethod::TestRunner; }; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp index f99300ad4e..93fb5ee366 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp @@ -25,7 +25,7 @@ namespace TestImpact { for (const auto& source : target->GetSources().m_staticSources) { - if (auto mapping = m_sourceDependencyMap.find(source); + if (auto mapping = m_sourceDependencyMap.find(source.String()); mapping != m_sourceDependencyMap.end()) { // This is an existing entry in the dependency map so update the parent build targets with this target @@ -43,7 +43,7 @@ namespace TestImpact { for (const auto& output : autogen.m_outputs) { - m_autogenInputToOutputMap[autogen.m_input].push_back(output); + m_autogenInputToOutputMap[autogen.m_input.String()].push_back(output.String()); } } }; @@ -138,11 +138,11 @@ namespace TestImpact { // Autogen input files are not compiled sources and thus supplying coverage data for them makes no sense AZ_TestImpact_Eval( - m_autogenInputToOutputMap.find(sourceCoverage.GetPath()) == m_autogenInputToOutputMap.end(), + m_autogenInputToOutputMap.find(sourceCoverage.GetPath().c_str()) == m_autogenInputToOutputMap.end(), DependencyException, AZStd::string::format("Couldn't replace source coverage for %s, source file is an autogen input file", sourceCoverage.GetPath().c_str()).c_str()); - auto [it, inserted] = m_sourceDependencyMap.insert(sourceCoverage.GetPath()); + auto [it, inserted] = m_sourceDependencyMap.insert(sourceCoverage.GetPath().String()); auto& [key, sourceDependency] = *it; // Clear any existing coverage for the delta @@ -178,22 +178,26 @@ namespace TestImpact } } - void DynamicDependencyMap::ClearSourceCoverage(const AZStd::vector& paths) + void DynamicDependencyMap::ClearSourceCoverage(const AZStd::vector& paths) { for (const auto& path : paths) { - if (const auto outputSources = m_autogenInputToOutputMap.find(path); + if (const auto outputSources = m_autogenInputToOutputMap.find(path.String()); outputSources != m_autogenInputToOutputMap.end()) { // Clearing the coverage data of an autogen input source instead clears the coverage data of its output sources for (const auto& outputSource : outputSources->second) { - ReplaceSourceCoverage(SourceCoveringTestsList({ SourceCoveringTests(outputSource) })); + AZStd::vector coverage; + coverage.emplace_back(RepoPath(outputSource)); + ReplaceSourceCoverage(SourceCoveringTestsList(AZStd::move(coverage))); } } else { - ReplaceSourceCoverage(SourceCoveringTestsList({ SourceCoveringTests(path, { }) })); + AZStd::vector coverage; + coverage.emplace_back(RepoPath(path)); + ReplaceSourceCoverage(SourceCoveringTestsList(AZStd::move(coverage))); } } } @@ -221,7 +225,7 @@ namespace TestImpact return coveringTestTargets; } - AZStd::optional DynamicDependencyMap::GetSourceDependency(const AZStd::string& path) const + AZStd::optional DynamicDependencyMap::GetSourceDependency(const RepoPath& path) const { AZStd::unordered_set parentTargets; AZStd::unordered_set coveringTestTargets; @@ -243,7 +247,7 @@ namespace TestImpact } }; - if (const auto outputSources = m_autogenInputToOutputMap.find(path); outputSources != m_autogenInputToOutputMap.end()) + if (const auto outputSources = m_autogenInputToOutputMap.find(path.String()); outputSources != m_autogenInputToOutputMap.end()) { // Consolidate the parentage and coverage of each of the autogen input file's generated output files for (const auto& outputSource : outputSources->second) @@ -253,7 +257,7 @@ namespace TestImpact } else { - getSourceDependency(path); + getSourceDependency(path.String()); } if (!parentTargets.empty() || !coveringTestTargets.empty()) @@ -264,7 +268,7 @@ namespace TestImpact return AZStd::nullopt; } - SourceDependency DynamicDependencyMap::GetSourceDependencyOrThrow(const AZStd::string& path) const + SourceDependency DynamicDependencyMap::GetSourceDependencyOrThrow(const RepoPath& path) const { auto sourceDependency = GetSourceDependency(path); AZ_TestImpact_Eval(sourceDependency.has_value(), DependencyException, AZStd::string::format("Couldn't find source %s", path.c_str()).c_str()); @@ -282,7 +286,7 @@ namespace TestImpact souceCoveringTests.push_back(testTarget->GetName()); } - coverage.push_back(SourceCoveringTests(path, AZStd::move(souceCoveringTests))); + coverage.push_back(SourceCoveringTests(RepoPath(path), AZStd::move(souceCoveringTests))); } return SourceCoveringTestsList(AZStd::move(coverage)); @@ -310,7 +314,7 @@ namespace TestImpact // Keep track of the coverage to delete as a post step rather than deleting it in situ so that erroneous change lists // do not corrupt the dynamic dependency map - AZStd::vector coverageToDelete; + AZStd::vector coverageToDelete; // Create operations for (const auto& createdFile : changeList.m_createdFiles) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h index 527a6555c6..6faef76452 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h @@ -12,7 +12,8 @@ #pragma once -#include +#include + #include #include #include @@ -74,10 +75,10 @@ namespace TestImpact //! Gets the source dependency for the specified source file. //! @note Autogen input source dependencies are the consolidated source dependencies of all of their generated output sources. //! @returns If found, the source dependency information for the specified source file, otherwise empty. - AZStd::optional GetSourceDependency(const AZStd::string& path) const; + AZStd::optional GetSourceDependency(const RepoPath& path) const; //! Gets the source dependency for the specified source file or throw DependencyException. - SourceDependency GetSourceDependencyOrThrow(const AZStd::string& path) const; + SourceDependency GetSourceDependencyOrThrow(const RepoPath& path) const; //! Replaces the source coverage of the specified sources with the specified source coverage. //! @note The covering targets for the parent test target(s) will not be pruned if those covering targets are removed. @@ -99,7 +100,7 @@ namespace TestImpact private: //! Clears the source coverage of the specified sources. //! @note The covering targets for the parent test target(s) will not be pruned if those covering targets are removed. - void ClearSourceCoverage(const AZStd::vector& paths); + void ClearSourceCoverage(const AZStd::vector& paths); //! The sorted list of unique production targets in the repository. ProductionTargetList m_productionTargets; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp index 792eb1f0b2..101bdf862c 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp @@ -16,18 +16,36 @@ namespace TestImpact { - SourceCoveringTests::SourceCoveringTests(const AZStd::string& path) + AZStd::vector ExtractTargetsFromSet(AZStd::unordered_set&& coveringTestTargets) + { + AZStd::vector testTargets; + testTargets.reserve(coveringTestTargets.size()); + for (auto it = coveringTestTargets.begin(); it != coveringTestTargets.end(); ) + { + testTargets.push_back(std::move(coveringTestTargets.extract(it++).value())); + } + + return testTargets; + } + + SourceCoveringTests::SourceCoveringTests(const RepoPath& path) : m_path(path) { } - SourceCoveringTests::SourceCoveringTests(const AZStd::string& path, AZStd::vector&& coveringTestTargets) + SourceCoveringTests::SourceCoveringTests(const RepoPath& path, AZStd::vector&& coveringTestTargets) : m_path(path) , m_coveringTestTargets(AZStd::move(coveringTestTargets)) { } - const AZStd::string& SourceCoveringTests::GetPath() const + SourceCoveringTests::SourceCoveringTests(const RepoPath& path, AZStd::unordered_set&& coveringTestTargets) + : m_path(path) + , m_coveringTestTargets(ExtractTargetsFromSet(AZStd::move(coveringTestTargets))) + { + } + + const RepoPath& SourceCoveringTests::GetPath() const { return m_path; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h index 52f3a7c3f6..f501ccefd8 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h @@ -12,7 +12,10 @@ #pragma once +#include + #include +#include #include namespace TestImpact @@ -21,11 +24,13 @@ namespace TestImpact class SourceCoveringTests { public: - explicit SourceCoveringTests(const AZStd::string& path); - SourceCoveringTests(const AZStd::string& path, AZStd::vector&& coveringTestTargets); + //SourceCoveringTests(const SourceCoveringTests&); + explicit SourceCoveringTests(const RepoPath& path); + SourceCoveringTests(const RepoPath& path, AZStd::vector&& coveringTestTargets); + SourceCoveringTests(const RepoPath& path, AZStd::unordered_set&& coveringTestTargets); //! Returns the path of this source file. - const AZStd::string& GetPath() const; + const RepoPath& GetPath() const; //! Returns the number of unresolved test targets covering this source file. size_t GetNumCoveringTestTargets() const; @@ -33,7 +38,7 @@ namespace TestImpact //! Returns the unresolved test targets covering this source file. const AZStd::vector& GetCoveringTestTargets() const; private: - AZStd::string m_path; //!< The path of this source file. + RepoPath m_path; //!< The path of this source file. AZStd::vector m_coveringTestTargets; //!< The unresolved test targets that cover this source file. }; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp index 39193d4dd9..7d4e76acd1 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp @@ -52,14 +52,14 @@ namespace TestImpact } SourceDependency::SourceDependency( - const AZStd::string& path, + const RepoPath& path, DependencyData&& dependencyData) : m_path(path) , m_dependencyData(AZStd::move(dependencyData)) { } - const AZStd::string& SourceDependency::GetPath() const + const RepoPath& SourceDependency::GetPath() const { return m_path; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h index a3b6783d13..d80c4a0540 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h @@ -71,11 +71,11 @@ namespace TestImpact { public: SourceDependency( - const AZStd::string& path, + const RepoPath& path, DependencyData&& dependencyData); //! Returns the path of this source file. - const AZStd::string& GetPath() const; + const RepoPath& GetPath() const; //! Returns the number of parent build targets this source belongs to. size_t GetNumParentTargets() const; @@ -89,7 +89,7 @@ namespace TestImpact //! Returns the test targets covering this source file. const AZStd::unordered_set& GetCoveringTestTargets() const; private: - AZStd::string m_path; //!< The path of this source file. + RepoPath m_path; //!< The path of this source file. DependencyData m_dependencyData; //!< The dependency data for this source file. }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.cpp index 0a8a8643f3..06dd4e846f 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.cpp @@ -69,7 +69,7 @@ namespace TestImpact NULL, NULL, &si, &pi)) { - throw ProcessException("Couldn't create process"); + throw ProcessException(AZStd::string::format("Couldn't create process with args: %s", args.c_str())); } ReleaseChildPipes(); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h index d9dd4870dd..4710cf2141 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h @@ -13,77 +13,42 @@ #pragma once #include -#include - -#include -#include +#include namespace TestImpact { - //! Result of a job that was run. - enum class JobResult - { - NotExecuted, //!< The job was not executed (e.g. the job runner terminated before the job could be executed). - FailedToExecute, //!< The job failed to execute (e.g. due to the arguments used to execute the job being invalid). - Terminated, //!< The job was terminated by the job runner (e.g. job or runner timeout exceeded while job was in-flight). - ExecutedWithFailure, //!< The job was executed but exited in an erroneous state (the underlying process returned non-zero). - ExecutedWithSuccess //!< The job was executed and exited in a successful state (the underlying processes returned zero). - }; - - //! The meta-data for a given job. - struct JobMeta - { - JobResult m_result = JobResult::NotExecuted; - AZStd::optional - m_startTime; //!< The time, relative to the job runner start, that this job started. - AZStd::optional m_duration; //!< The duration that this job took to complete. - AZStd::optional m_returnCode; //!< The return code of the underlying processes of this job. - }; - //! Representation of a unit of work to be performed by a process. //! @tparam JobInfoT The JobInfo structure containing the information required to run this job. //! @tparam JobPayloadT The resulting output of the processed artifact produced by this job. template class Job + : public JobMetaContainer { public: using Info = JobInfoT; using Payload = JobPayloadT; //! Constructor with r-values for the specific use case of the job runner. - Job(Info jobInfo, JobMeta&& jobMeta, AZStd::optional&& payload); + Job(const Info& jobInfo, JobMeta&& jobMeta, AZStd::optional&& payload); //! Returns the job info associated with this job. const Info& GetJobInfo() const; - //! Returns the result of this job. - JobResult GetResult() const; - - //! Returns the start time, relative to the job runner start, that this job started. - AZStd::chrono::high_resolution_clock::time_point GetStartTime() const; - - //! Returns the end time, relative to the job runner start, that this job ended. - AZStd::chrono::high_resolution_clock::time_point GetEndTime() const; - - //! Returns the duration that this job took to complete. - AZStd::chrono::milliseconds GetDuration() const; - - //! Returns the return code of the underlying processes of this job. - AZStd::optional GetReturnCode() const; - //! Returns the payload produced by this job. const AZStd::optional& GetPayload() const; + //! Facilitates the client consuming the payload. + AZStd::optional&& ReleasePayload(); + private: Info m_jobInfo; - JobMeta m_meta; AZStd::optional m_payload; }; template - Job::Job(Info jobInfo, JobMeta&& jobMeta, AZStd::optional&& payload) - : m_jobInfo(jobInfo) - , m_meta(AZStd::move(jobMeta)) + Job::Job(const Info& jobInfo, JobMeta&& jobMeta, AZStd::optional&& payload) + : JobMetaContainer(AZStd::move(jobMeta)) + , m_jobInfo(jobInfo) , m_payload(AZStd::move(payload)) { } @@ -95,45 +60,14 @@ namespace TestImpact } template - JobResult Job::GetResult() const - { - return m_meta.m_result; - } - - template - AZStd::optional Job::GetReturnCode() const - { - return m_meta.m_returnCode; - } - - template - AZStd::chrono::high_resolution_clock::time_point Job::GetStartTime() const - { - return m_meta.m_startTime.value_or(AZStd::chrono::high_resolution_clock::time_point()); - } - - template - AZStd::chrono::high_resolution_clock::time_point Job::GetEndTime() const - { - if (m_meta.m_startTime.has_value() && m_meta.m_duration.has_value()) - { - return m_meta.m_startTime.value() + m_meta.m_duration.value(); - } - else - { - return AZStd::chrono::high_resolution_clock::time_point(); - } - } - - template - AZStd::chrono::milliseconds Job::GetDuration() const + const AZStd::optional& Job::GetPayload() const { - return m_meta.m_duration.value_or(AZStd::chrono::milliseconds{0}); + return m_payload; } template - const AZStd::optional& Job::GetPayload() const + AZStd::optional&& Job::ReleasePayload() { - return m_payload; + return AZStd::move(m_payload); } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobInfo.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobInfo.h index fd9d76652a..fbdad82c12 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobInfo.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobInfo.h @@ -24,6 +24,7 @@ namespace TestImpact { public: using IdType = size_t; + using CommandType = AZStd::string; //! Client-provided identifier to distinguish between different jobs. //! @note Ids of different job types are not interchangeable. @@ -32,30 +33,37 @@ namespace TestImpact IdType m_value; }; + //! Command used my ProcessScheduler to execute this job. + //! @note Commands of different job types are not interchangeable. + struct Command + { + CommandType m_args; + }; + //! Constructs the job information with any additional information required by the job. //! @param jobId The client-provided unique identifier for the job. - //! @param args The arguments used to launch the process running the job. + //! @param command The command used to launch the process running the job. //! @param additionalInfo The arguments to be provided to the additional information data structure. template - JobInfo(Id jobId, const AZStd::string& args, AdditionalInfoArgs&&... additionalInfo); + JobInfo(Id jobId, const Command& command, AdditionalInfoArgs&&... additionalInfo); //! Returns the id of this job. Id GetId() const; //! Returns the command arguments used to execute this job. - const AZStd::string& GetArgs() const; + const Command& GetCommand() const; private: Id m_id; - AZStd::string m_args; + Command m_command; }; template template - JobInfo::JobInfo(Id jobId, const AZStd::string& args, AdditionalInfoArgs&&... additionalInfo) + JobInfo::JobInfo(Id jobId, const Command& command, AdditionalInfoArgs&&... additionalInfo) : AdditionalInfo{std::forward(additionalInfo)...} , m_id(jobId) - , m_args(args) + , m_command(command) { } @@ -66,8 +74,8 @@ namespace TestImpact } template - const AZStd::string& JobInfo::GetArgs() const + const typename JobInfo::Command& JobInfo::GetCommand() const { - return m_args; + return m_command; } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.cpp new file mode 100644 index 0000000000..ba6ad4c585 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.cpp @@ -0,0 +1,61 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + JobMetaContainer::JobMetaContainer(const JobMeta& jobMeta) + : m_meta(jobMeta) + { + } + + JobMetaContainer::JobMetaContainer(JobMeta&& jobMeta) + : m_meta(AZStd::move(jobMeta)) + { + } + + JobResult JobMetaContainer::GetJobResult() const + { + return m_meta.m_result; + } + + AZStd::optional JobMetaContainer::GetReturnCode() const + { + return m_meta.m_returnCode; + } + + AZStd::chrono::high_resolution_clock::time_point JobMetaContainer::GetStartTime() const + { + return m_meta.m_startTime.value_or(AZStd::chrono::high_resolution_clock::time_point()); + } + + AZStd::chrono::high_resolution_clock::time_point JobMetaContainer::GetEndTime() const + { + if (m_meta.m_startTime.has_value() && m_meta.m_duration.has_value()) + { + return m_meta.m_startTime.value() + m_meta.m_duration.value(); + } + else + { + return AZStd::chrono::high_resolution_clock::time_point(); + } + } + + AZStd::chrono::milliseconds JobMetaContainer::GetDuration() const + { + return m_meta.m_duration.value_or(AZStd::chrono::milliseconds{ 0 }); + } + +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h new file mode 100644 index 0000000000..6e7ce97468 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h @@ -0,0 +1,68 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include +#include + +namespace TestImpact +{ + //! Result of a job that was run. + enum class JobResult + { + NotExecuted, //!< The job was not executed (e.g. the job runner terminated before the job could be executed). + FailedToExecute, //!< The job failed to execute (e.g. due to the arguments used to execute the job being invalid). + Timeout, //!< The job was terminated by the job runner (e.g. job timeout exceeded while job was in-flight). + Terminated, //!< The job was terminated by the job runner (e.g. global timeout exceeded while job was in-flight). + ExecutedWithFailure, //!< The job was executed but exited in an erroneous state (the underlying process returned non-zero). + ExecutedWithSuccess //!< The job was executed and exited in a successful state (the underlying processes returned zero). + }; + + //! The meta-data for a given job. + struct JobMeta + { + JobResult m_result = JobResult::NotExecuted; + AZStd::optional + m_startTime; //!< The time, relative to the job runner start, that this job started. + AZStd::optional m_duration; //!< The duration that this job took to complete. + AZStd::optional m_returnCode; //!< The return code of the underlying processes of this job. + }; + + class JobMetaContainer + { + public: + JobMetaContainer(const JobMeta& jobMeta); + JobMetaContainer(JobMeta&& jobMeta); + + //! Returns the result of this job. + JobResult GetJobResult() const; + + //! Returns the start time, relative to the job runner start, that this job started. + AZStd::chrono::high_resolution_clock::time_point GetStartTime() const; + + //! Returns the end time, relative to the job runner start, that this job ended. + AZStd::chrono::high_resolution_clock::time_point GetEndTime() const; + + //! Returns the duration that this job took to complete. + AZStd::chrono::milliseconds GetDuration() const; + + //! Returns the return code of the underlying processes of this job. + AZStd::optional GetReturnCode() const; + + private: + JobMeta m_meta; + }; +} // namespace TestImpact + diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h index 8b9e4fa69e..0c30accc84 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h @@ -13,7 +13,6 @@ #pragma once #include -#include #include #include @@ -27,7 +26,7 @@ namespace TestImpact //! @param meta The meta-data about the job run. //! @param std The standard output and standard error of the process running the job. template - using JobCallback = AZStd::function; + using JobCallback = AZStd::function; //! The payloads produced by the job-specific payload producer in the form of a map associating each job id with the job's payload. template @@ -116,7 +115,7 @@ namespace TestImpact const auto* jobInfo = &jobInfos[jobIndex]; const auto jobId = jobInfo->GetId().m_value; metas.emplace(jobId, AZStd::pair{JobMeta{}, jobInfo}); - processes.emplace_back(jobId, m_stdOutRouting, m_stdErrRouting, jobInfo->GetArgs()); + processes.emplace_back(jobId, m_stdOutRouting, m_stdErrRouting, jobInfo->GetCommand().m_args); } // Wrapper around low-level process launch callback to gather job meta-data and present a simplified callback interface to the client @@ -134,7 +133,7 @@ namespace TestImpact else { meta.m_startTime = createTime; - return CallbackResult::Continue; + return ProcessCallbackResult::Continue; } }; @@ -153,10 +152,14 @@ namespace TestImpact { meta.m_result = JobResult::ExecutedWithSuccess; } - else if (exitCondition == ExitCondition::Terminated || exitCondition == ExitCondition::Timeout) + else if (exitCondition == ExitCondition::Terminated) { meta.m_result = JobResult::Terminated; } + else if (exitCondition == ExitCondition::Timeout) + { + meta.m_result = JobResult::Timeout; + } else { meta.m_result = JobResult::ExecutedWithFailure; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp index 15230b1a46..04a68bf612 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp @@ -58,7 +58,7 @@ namespace TestImpact for (auto& process : m_processPool) { - if (PopAndLaunch(process) == CallbackResult::Abort) + if (PopAndLaunch(process) == ProcessCallbackResult::Abort) { TerminateAllProcesses(ExitCondition::Terminated); return; @@ -110,7 +110,7 @@ namespace TestImpact const auto exitTime = AZStd::chrono::high_resolution_clock::now(); // Inform the client that the processes has exited - if (CallbackResult::Abort == m_processExitCallback( + if (ProcessCallbackResult::Abort == m_processExitCallback( processId, ExitCondition::Gracefull, returnCode, @@ -124,7 +124,7 @@ namespace TestImpact else if (!m_processQueue.empty()) { // This slot in the pool is free so launch one of the processes waiting in the queue - if (PopAndLaunch(processInFlight) == CallbackResult::Abort) + if (PopAndLaunch(processInFlight) == ProcessCallbackResult::Abort) { // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); @@ -150,7 +150,7 @@ namespace TestImpact const ReturnCode returnCode = processInFlight.m_process->GetReturnCode().value(); processInFlight.m_process.reset(); - if (CallbackResult::Abort == m_processExitCallback( + if (ProcessCallbackResult::Abort == m_processExitCallback( processId, ExitCondition::Timeout, returnCode, @@ -172,7 +172,7 @@ namespace TestImpact // Queue is empty, no more processes to launch if (!m_processQueue.empty()) { - if (PopAndLaunch(processInFlight) == CallbackResult::Abort) + if (PopAndLaunch(processInFlight) == ProcessCallbackResult::Abort) { // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); @@ -194,7 +194,7 @@ namespace TestImpact } } - CallbackResult ProcessScheduler::PopAndLaunch(ProcessInFlight& processInFlight) + ProcessCallbackResult ProcessScheduler::PopAndLaunch(ProcessInFlight& processInFlight) { auto processInfo = m_processQueue.front(); m_processQueue.pop(); @@ -251,7 +251,7 @@ namespace TestImpact if (isCallingBackToClient) { const auto exitTime = AZStd::chrono::high_resolution_clock::now(); - if (CallbackResult::Abort == m_processExitCallback( + if (ProcessCallbackResult::Abort == m_processExitCallback( processInFlight.m_process->GetProcessInfo().GetId(), exitStatus, returnCode, diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h index 5531c78397..27171d25ee 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h @@ -12,11 +12,10 @@ #pragma once -#include +#include -#include +#include -#include #include #include #include @@ -43,12 +42,19 @@ namespace TestImpact Timeout = ProcessTimeoutErrorCode //!< The process was terminated by the scheduler due to exceeding runtime limit. }; + //! Client result for process scheduler callbacks. + enum class ProcessCallbackResult : bool + { + Continue, //!< Continune scheduling. + Abort //!< Abort scheduling immediately. + }; + //! Callback for process launch attempt. //! @param processId The id of the process that attempted to launch. //! @param launchResult The result of the process launch attempt. //! @param createTime The timestamp of the process launch attempt. using ProcessLaunchCallback = - AZStd::function; @@ -60,7 +66,7 @@ namespace TestImpact //! @param std The standard output and standard error of the process. //! @param createTime The timestamp of the process exit. using ProcessExitCallback = - AZStd::function +#include + #include #include @@ -64,9 +65,9 @@ namespace TestImpact ProcessId processId, StdOutputRouting stdOut, StdErrorRouting stdErr, - const AZ::IO::Path& processPath, + const RepoPath& processPath, const AZStd::string& startupArgs = ""); - ProcessInfo(ProcessId processId, const AZ::IO::Path& processPath, const AZStd::string& startupArgs = ""); + ProcessInfo(ProcessId processId, const RepoPath& processPath, const AZStd::string& startupArgs = ""); //! Returns the identifier of this process. ProcessId GetId() const; @@ -78,7 +79,7 @@ namespace TestImpact bool ParentHasStdError() const; // Returns the path to the process binary. - const AZ::IO::Path& GetProcessPath() const; + const RepoPath& GetProcessPath() const; //! Returns the command line arguments used to launch the process. const AZStd::string& GetStartupArgs() const; @@ -87,7 +88,7 @@ namespace TestImpact const ProcessId m_id; const bool m_parentHasStdOutput; const bool m_parentHasStdErr; - const AZ::IO::Path m_processPath; + const RepoPath m_processPath; const AZStd::string m_startupArgs; }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.cpp index bce6fa25d6..54161899ae 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.cpp @@ -31,7 +31,7 @@ namespace TestImpact return m_buildMetaData.m_outputName; } - const AZ::IO::Path& BuildTarget::GetPath() const + const RepoPath& BuildTarget::GetPath() const { return m_buildMetaData.m_path; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h index 0cd6369472..d5d32b8ff5 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h @@ -49,7 +49,7 @@ namespace TestImpact const AZStd::string& GetOutputName() const; //! Returns the path in the source tree to the build target location. - const AZ::IO::Path& GetPath() const; + const RepoPath& GetPath() const; //! Returns the build target's sources. const TargetSources& GetSources() const; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h index dfd102b55f..9731b68d61 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h @@ -42,6 +42,9 @@ namespace TestImpact //! Returns the target with the specified name or throws if target not found. const Target* GetTargetOrThrow(const AZStd::string& name) const; + //! Returns true if the specified target is in the list, otherwise false. + bool HasTarget(const AZStd::string& name) const; + // Returns the number of targets in the list. size_t GetNumTargets() const; @@ -71,7 +74,7 @@ namespace TestImpact m_targets.reserve(descriptors.size()); for (auto&& descriptor : descriptors) { - m_targets.emplace_back(Target(AZStd::move(descriptor))); + m_targets.emplace_back(AZStd::move(Target(AZStd::move(descriptor)))); } } @@ -122,4 +125,10 @@ namespace TestImpact AZ_TestImpact_Eval(target, TargetException, AZStd::string::format("Couldn't find target %s", name.c_str()).c_str()); return target; } + + template + bool BuildTargetList::HasTarget(const AZStd::string& name) const + { + return GetTarget(name) != nullptr; + } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.cpp index 0189bc9e78..1890863981 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.cpp @@ -25,6 +25,16 @@ namespace TestImpact return m_testMetaData.m_suite; } + const AZStd::string& TestTarget::GetCustomArgs() const + { + return m_testMetaData.m_customArgs; + } + + AZStd::chrono::milliseconds TestTarget::GetTimeout() const + { + return m_testMetaData.m_timeout; + } + LaunchMethod TestTarget::GetLaunchMethod() const { return m_testMetaData.m_launchMethod; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.h index b6f44e7cc1..e8449dde3d 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.h @@ -29,6 +29,12 @@ namespace TestImpact //! Returns the test target suite. const AZStd::string& GetSuite() const; + //! Returns the launcher custom arguments. + const AZStd::string& GetCustomArgs() const; + + //! Returns the test run timeout. + AZStd::chrono::milliseconds GetTimeout() const; + //! Returns the test target launch method. LaunchMethod GetLaunchMethod() const; From c477c699ef601e1a279ae8a8e21c50485775c83b Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 21 May 2021 09:11:53 +0100 Subject: [PATCH 041/233] Additional refactoring --- .../TestImpactClientFailureReport.h | 2 +- .../Dependency/TestImpactDynamicDependencyMap.cpp | 10 +++------- .../Process/JobRunner/TestImpactProcessJobMeta.h | 3 +-- .../Code/Source/Target/TestImpactBuildTargetList.h | 2 +- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h index d2ab863d86..02b1169e93 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h @@ -130,7 +130,7 @@ namespace TestImpact private: AZStd::vector m_executionFailures; AZStd::vector m_launcherFailures; - AZStd::vector m_unexecutedTestsTests; + AZStd::vector m_unexecutedTests; }; //! Represents the report for a failed regular test sequence run without test impact analysis. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp index 93fb5ee366..31b3485c70 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp @@ -138,7 +138,7 @@ namespace TestImpact { // Autogen input files are not compiled sources and thus supplying coverage data for them makes no sense AZ_TestImpact_Eval( - m_autogenInputToOutputMap.find(sourceCoverage.GetPath().c_str()) == m_autogenInputToOutputMap.end(), + m_autogenInputToOutputMap.find(sourceCoverage.GetPath().String()) == m_autogenInputToOutputMap.end(), DependencyException, AZStd::string::format("Couldn't replace source coverage for %s, source file is an autogen input file", sourceCoverage.GetPath().c_str()).c_str()); @@ -188,16 +188,12 @@ namespace TestImpact // Clearing the coverage data of an autogen input source instead clears the coverage data of its output sources for (const auto& outputSource : outputSources->second) { - AZStd::vector coverage; - coverage.emplace_back(RepoPath(outputSource)); - ReplaceSourceCoverage(SourceCoveringTestsList(AZStd::move(coverage))); + ReplaceSourceCoverage(SourceCoveringTestsList(AZStd::vector{ SourceCoveringTests(RepoPath(outputSource)) })); } } else { - AZStd::vector coverage; - coverage.emplace_back(RepoPath(path)); - ReplaceSourceCoverage(SourceCoveringTestsList(AZStd::move(coverage))); + ReplaceSourceCoverage(SourceCoveringTestsList(AZStd::vector{ SourceCoveringTests(RepoPath(path)) })); } } } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h index 6e7ce97468..874934b54d 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h @@ -34,8 +34,7 @@ namespace TestImpact struct JobMeta { JobResult m_result = JobResult::NotExecuted; - AZStd::optional - m_startTime; //!< The time, relative to the job runner start, that this job started. + AZStd::optional m_startTime; //!< The time, relative to the job runner start, that this job started. AZStd::optional m_duration; //!< The duration that this job took to complete. AZStd::optional m_returnCode; //!< The return code of the underlying processes of this job. }; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h index 9731b68d61..79f93f7779 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h @@ -74,7 +74,7 @@ namespace TestImpact m_targets.reserve(descriptors.size()); for (auto&& descriptor : descriptors) { - m_targets.emplace_back(AZStd::move(Target(AZStd::move(descriptor)))); + m_targets.emplace_back(Target(AZStd::move(descriptor))); } } From 5113d3f756396a02ce6bb1e560300298731fe222 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 21 May 2021 09:36:01 +0100 Subject: [PATCH 042/233] REname JobMetaContainer to JobMetaWrapper --- .../Process/JobRunner/TestImpactProcessJob.h | 4 ++-- .../Process/JobRunner/TestImpactProcessJobMeta.cpp | 14 +++++++------- .../Process/JobRunner/TestImpactProcessJobMeta.h | 7 ++++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h index 4710cf2141..f1629adda4 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h @@ -22,7 +22,7 @@ namespace TestImpact //! @tparam JobPayloadT The resulting output of the processed artifact produced by this job. template class Job - : public JobMetaContainer + : public JobMetaWrapper { public: using Info = JobInfoT; @@ -47,7 +47,7 @@ namespace TestImpact template Job::Job(const Info& jobInfo, JobMeta&& jobMeta, AZStd::optional&& payload) - : JobMetaContainer(AZStd::move(jobMeta)) + : JobMetaWrapper(AZStd::move(jobMeta)) , m_jobInfo(jobInfo) , m_payload(AZStd::move(payload)) { diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.cpp index ba6ad4c585..022115c042 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.cpp @@ -16,32 +16,32 @@ namespace TestImpact { - JobMetaContainer::JobMetaContainer(const JobMeta& jobMeta) + JobMetaWrapper::JobMetaWrapper(const JobMeta& jobMeta) : m_meta(jobMeta) { } - JobMetaContainer::JobMetaContainer(JobMeta&& jobMeta) + JobMetaWrapper::JobMetaWrapper(JobMeta&& jobMeta) : m_meta(AZStd::move(jobMeta)) { } - JobResult JobMetaContainer::GetJobResult() const + JobResult JobMetaWrapper::GetJobResult() const { return m_meta.m_result; } - AZStd::optional JobMetaContainer::GetReturnCode() const + AZStd::optional JobMetaWrapper::GetReturnCode() const { return m_meta.m_returnCode; } - AZStd::chrono::high_resolution_clock::time_point JobMetaContainer::GetStartTime() const + AZStd::chrono::high_resolution_clock::time_point JobMetaWrapper::GetStartTime() const { return m_meta.m_startTime.value_or(AZStd::chrono::high_resolution_clock::time_point()); } - AZStd::chrono::high_resolution_clock::time_point JobMetaContainer::GetEndTime() const + AZStd::chrono::high_resolution_clock::time_point JobMetaWrapper::GetEndTime() const { if (m_meta.m_startTime.has_value() && m_meta.m_duration.has_value()) { @@ -53,7 +53,7 @@ namespace TestImpact } } - AZStd::chrono::milliseconds JobMetaContainer::GetDuration() const + AZStd::chrono::milliseconds JobMetaWrapper::GetDuration() const { return m_meta.m_duration.value_or(AZStd::chrono::milliseconds{ 0 }); } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h index 874934b54d..c8f459ba6f 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h @@ -39,11 +39,12 @@ namespace TestImpact AZStd::optional m_returnCode; //!< The return code of the underlying processes of this job. }; - class JobMetaContainer + //! Wrapper for job meta structure to inheritance/aggregation without being coupled to the JobInfo or Job classes. + class JobMetaWrapper { public: - JobMetaContainer(const JobMeta& jobMeta); - JobMetaContainer(JobMeta&& jobMeta); + JobMetaWrapper(const JobMeta& jobMeta); + JobMetaWrapper(JobMeta&& jobMeta); //! Returns the result of this job. JobResult GetJobResult() const; From 638f40b7dd23bbb07c268291a3b0c6233fe55ec2 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 21 May 2021 12:19:19 +0100 Subject: [PATCH 043/233] Add proper exhange to payload release --- .../Code/Source/Process/JobRunner/TestImpactProcessJob.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h index f1629adda4..47afe1ca3e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h @@ -38,7 +38,8 @@ namespace TestImpact const AZStd::optional& GetPayload() const; //! Facilitates the client consuming the payload. - AZStd::optional&& ReleasePayload(); + //! @note It is valid for a job life cycle to continue after having released its payload. + AZStd::optional ReleasePayload(); private: Info m_jobInfo; @@ -66,8 +67,8 @@ namespace TestImpact } template - AZStd::optional&& Job::ReleasePayload() + AZStd::optional Job::ReleasePayload() { - return AZStd::move(m_payload); + return AZStd::exchange(m_payload, AZStd::nullopt); } } // namespace TestImpact From dfb0d7f9f567ec0d1d8cfa449b5619190296c231 Mon Sep 17 00:00:00 2001 From: igarri Date: Fri, 21 May 2021 12:33:55 +0100 Subject: [PATCH 044/233] Fixed numeric_cast, style, const variables --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 17 +++++++++-------- .../AssetBrowser/AssetBrowserFilterModel.h | 2 +- .../AssetBrowser/AssetBrowserModel.cpp | 2 +- .../AssetBrowser/AssetBrowserTableModel.cpp | 5 ----- .../AssetBrowser/AssetBrowserTableModel.h | 1 - .../AssetBrowser/Views/AssetBrowserTableView.h | 3 ++- .../AssetBrowser/Views/AssetBrowserTreeView.cpp | 2 +- .../AssetBrowser/Views/EntryDelegate.cpp | 2 +- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 4 ---- 9 files changed, 15 insertions(+), 23 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index c5723832c7..7c7f2aa601 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -135,28 +135,29 @@ namespace AzToolsFramework auto compFilter = qobject_cast >(m_filter); if (compFilter) { - auto& subFilters = compFilter->GetSubFilters(); + const auto& subFilters = compFilter->GetSubFilters(); - auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool + auto compositeFilterIterator = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool { - auto assetTypeFilter = qobject_cast >(filter); + const auto assetTypeFilter = qobject_cast >(filter); return !assetTypeFilter.isNull(); }); - if (it != subFilters.end()) + if (compositeFilterIterator != subFilters.end()) { - m_assetTypeFilter = qobject_cast >(*it); + m_assetTypeFilter = qobject_cast >(*compositeFilterIterator); } auto compStringFilterIter = AZStd::find_if(subFilters.begin(), subFilters.end(), [](FilterConstType filter) -> bool { //The real StringFilter is really a CompositeFilter with just one StringFilter in its subfilter list //To know if it is actually a StringFilter we have to get that subfilter and check if it is a Stringfilter. - auto stringCompositeFilter = qobject_cast >(filter); + const auto stringCompositeFilter = qobject_cast >(filter); bool isStringFilter = false; if (stringCompositeFilter) { const auto& stringSubfilters = stringCompositeFilter->GetSubFilters(); - auto canBeCasted = [](FilterConstType filt) -> bool { + auto canBeCasted = [](FilterConstType filt) -> bool + { auto strFilter = qobject_cast>(filt); return !strFilter.isNull(); }; @@ -173,7 +174,7 @@ namespace AzToolsFramework }); if (compStringFilterIter != subFilters.end()) { - auto compStringFilter = qobject_cast>(*compStringFilterIter); + const auto compStringFilter = qobject_cast>(*compStringFilterIter); if (!compStringFilter->GetSubFilters().isEmpty() && compStringFilter->GetSubFilters()[0]) { m_stringFilter = qobject_cast>(compStringFilter->GetSubFilters()[0]); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h index 7a6faf9f18..5d22791699 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h @@ -68,7 +68,7 @@ namespace AzToolsFramework protected: //set for filtering columns //if the column is in the set the column is not filtered and is shown - AZStd::fixed_unordered_set(AssetBrowserEntry::Column::Count)> m_showColumn; + AZStd::fixed_unordered_set(AssetBrowserEntry::Column::Count)> m_showColumn; bool m_alreadyRecomputingFilters = false; //asset source name match filter FilterConstType m_filter; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp index a5cfad09ba..c0f78baccb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp @@ -163,7 +163,7 @@ namespace AzToolsFramework int AssetBrowserModel::columnCount(const QModelIndex& /*parent*/) const { - return static_cast(AssetBrowserEntry::Column::Count); + return aznumeric_cast(AssetBrowserEntry::Column::Count); } QVariant AssetBrowserModel::data(const QModelIndex& index, int role) const diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index 6d9803dfd4..40f8318a2d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -81,11 +81,6 @@ namespace AzToolsFramework return parent.isValid() ? QModelIndex() : createIndex(row, column, m_indexMap[row].internalPointer()); } - QModelIndex AssetBrowserTableModel::parent([[maybe_unused]] const QModelIndex& child) const - { - return QModelIndex(); - } - int AssetBrowserTableModel::rowCount(const QModelIndex& parent) const { return !parent.isValid() ? m_indexMap.size() : 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index b0497213a0..6b64ceaf92 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -36,7 +36,6 @@ namespace AzToolsFramework // QSortFilterProxyModel void setSourceModel(QAbstractItemModel* sourceModel) override; QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; - QModelIndex parent(const QModelIndex& child) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; public Q_SLOTS: diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h index 482a072373..65f97d1a6d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -68,7 +68,8 @@ namespace AzToolsFramework protected Q_SLOTS: void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override; void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override; - void layoutChangedSlot(const QList &parents = QList(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint); + void layoutChangedSlot(const QList &parents = QList(), + QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint); private: QString m_name; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp index 660c04ccd4..b93caf85f7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp @@ -176,7 +176,7 @@ namespace AzToolsFramework void AssetBrowserTreeView::OnAssetBrowserComponentReady() { - hideColumn(static_cast(AssetBrowserEntry::Column::Path)); + hideColumn(aznumeric_cast(AssetBrowserEntry::Column::Path)); if (!m_name.isEmpty()) { auto crc = AZ::Crc32(m_name.toUtf8().data()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index dff8a170c7..c73c00981b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -75,7 +75,7 @@ namespace AzToolsFramework auto sourceEntry = azrtti_cast(entry); QPalette actualPalette(option.palette); - if (index.column() == static_cast(AssetBrowserEntry::Column::Name)) + if (index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name)) { int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey()); if (sourceEntry) diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 35734042a0..1b123dd000 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -110,7 +110,6 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) connect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); connect(m_ui->m_viewSwitcherCheckBox, &QCheckBox::stateChanged, this, &AzAssetBrowserWindow::LockToDefaultView); - } m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); @@ -124,17 +123,14 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); }); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, this, &AzAssetBrowserWindow::SelectionChangedSlot); connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, &SearchWidget::ClearStringFilter); connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter); - m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); } From e68ac30bcb24dbf83d1ed25ec8b01fb1ba3af0f1 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 21 May 2021 13:11:24 +0100 Subject: [PATCH 045/233] Move test runners --- .../Source/Test/Job/TestImpactTestJobCommon.h | 38 ---- .../Test/Job/TestImpactTestJobException.h | 26 --- .../Enumeration/TestImpactTestEnumeration.h | 2 +- .../TestImpactTestEnumerationException.h | 0 .../TestImpactTestEnumerationSerializer.cpp | 4 +- .../TestImpactTestEnumerationSerializer.h | 2 +- .../Enumeration/TestImpactTestEnumerator.cpp | 22 +-- .../Enumeration/TestImpactTestEnumerator.h | 16 +- .../TestImpactTestJobInfoGenerator.cpp | 167 ++++++++++++++++++ .../TestImpactTestJobInfoGenerator.h | 67 +++++++ .../JobRunner}/TestImpactTestJobRunner.h | 16 +- .../Run/TestImpactInstrumentedTestRunner.cpp | 42 +++-- .../Run/TestImpactInstrumentedTestRunner.h | 20 ++- .../Run/TestImpactTestCoverage.cpp | 63 ++++++- .../Run/TestImpactTestCoverage.h | 11 +- .../Run/TestImpactTestRun.cpp | 68 ++++++- .../Run/TestImpactTestRun.h | 15 +- .../Run/TestImpactTestRunException.h | 0 .../Run/TestImpactTestRunJobData.cpp | 6 +- .../Run/TestImpactTestRunJobData.h | 8 +- .../Run/TestImpactTestRunSerializer.cpp | 4 +- .../Run/TestImpactTestRunSerializer.h | 2 +- .../Run/TestImpactTestRunner.cpp | 21 ++- .../Run/TestImpactTestRunner.h | 6 +- .../TestImpactTestSuiteContainer.h | 80 ++++++++- 25 files changed, 562 insertions(+), 144 deletions(-) delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobException.h rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Enumeration/TestImpactTestEnumeration.h (93%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Enumeration/TestImpactTestEnumerationException.h (100%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Enumeration/TestImpactTestEnumerationSerializer.cpp (96%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Enumeration/TestImpactTestEnumerationSerializer.h (94%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Enumeration/TestImpactTestEnumerator.cpp (87%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Enumeration/TestImpactTestEnumerator.h (90%) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test/Job => TestEngine/JobRunner}/TestImpactTestJobRunner.h (92%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactInstrumentedTestRunner.cpp (69%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactInstrumentedTestRunner.h (82%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactTestCoverage.cpp (57%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactTestCoverage.h (84%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactTestRun.cpp (53%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactTestRun.h (78%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactTestRunException.h (100%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactTestRunJobData.cpp (78%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactTestRunJobData.h (77%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactTestRunSerializer.cpp (98%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactTestRunSerializer.h (95%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactTestRunner.cpp (73%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/Run/TestImpactTestRunner.h (93%) rename Code/Tools/TestImpactFramework/Runtime/Code/Source/{Test => TestEngine}/TestImpactTestSuiteContainer.h (57%) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h deleted file mode 100644 index 03bbd91454..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobCommon.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include -#include -#include - -#pragma once - -namespace TestImpact -{ - template - AZStd::string ReadFileContents(const AZ::IO::Path& file) - { - static_assert(AZStd::is_base_of::value, "Exception must be a TestImpact exception or derived type"); - - const auto fileSize = AZ::IO::SystemFile::Length(file.c_str()); - AZ_TestImpact_Eval(fileSize > 0, ExceptionType, AZStd::string::format("File %s does not exist", file.c_str())); - - AZStd::vector buffer(fileSize + 1); - buffer[fileSize] = '\0'; - AZ_TestImpact_Eval(AZ::IO::SystemFile::Read(file.c_str(), buffer.data()), ExceptionType, "Could not read file contents"); - - return AZStd::string(buffer.begin(), buffer.end()); - } -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobException.h deleted file mode 100644 index 263d70322e..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobException.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#pragma once - -#include - -namespace TestImpact -{ - //! Exception for test job related operations. - class TestJobException - : public Exception - { - public: - using Exception::Exception; - }; -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumeration.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumeration.h similarity index 93% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumeration.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumeration.h index c12abec006..709c5d27d0 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumeration.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumeration.h @@ -13,7 +13,7 @@ #pragma once #include -#include +#include namespace TestImpact { diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationException.h similarity index 100% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationException.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationException.h diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationSerializer.cpp similarity index 96% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationSerializer.cpp index efed04970d..05a13a0703 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationSerializer.cpp @@ -10,8 +10,8 @@ * */ -#include -#include +#include +#include #include #include diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationSerializer.h similarity index 94% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationSerializer.h index 422308d862..06bd224b50 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerationSerializer.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationSerializer.h @@ -12,7 +12,7 @@ #pragma once -#include +#include #include diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp similarity index 87% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.cpp rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp index 1964f8876e..882c65c516 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp @@ -10,11 +10,13 @@ * */ +#include + #include -#include -#include -#include -#include +#include +#include +#include +#include #include @@ -23,7 +25,7 @@ namespace TestImpact namespace { void WriteCacheFile( - const TestEnumeration& enumeration, const AZ::IO::Path& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy) + const TestEnumeration& enumeration, const RepoPath& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy) { const AZStd::string cacheJSON = SerializeTestEnumeration(enumeration); const AZStd::vector cacheBytes(cacheJSON.begin(), cacheJSON.end()); @@ -48,7 +50,7 @@ namespace TestImpact } } - AZStd::optional ReadCacheFile(const AZ::IO::Path& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy) + AZStd::optional ReadCacheFile(const RepoPath& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy) { AZ::IO::SystemFile cacheFile; AZStd::string cacheJSON; @@ -83,18 +85,18 @@ namespace TestImpact } } // namespace - TestEnumeration ParseTestEnumerationFile(const AZ::IO::Path& enumerationFile) + TestEnumeration ParseTestEnumerationFile(const RepoPath& enumerationFile) { return TestEnumeration(GTest::TestEnumerationSuitesFactory(ReadFileContents(enumerationFile))); } - TestEnumerationJobData::TestEnumerationJobData(const AZ::IO::Path& enumerationArtifact, AZStd::optional&& cache) + TestEnumerationJobData::TestEnumerationJobData(const RepoPath& enumerationArtifact, AZStd::optional&& cache) : m_enumerationArtifact(enumerationArtifact) , m_cache(AZStd::move(cache)) { } - const AZ::IO::Path& TestEnumerationJobData::GetEnumerationArtifactPath() const + const RepoPath& TestEnumerationJobData::GetEnumerationArtifactPath() const { return m_enumerationArtifact; } @@ -163,7 +165,7 @@ namespace TestImpact const auto& [meta, jobInfo] = jobData; if (meta.m_result == JobResult::ExecutedWithSuccess) { - const auto& enumeration = enumerations[jobId] = ParseTestEnumerationFile(jobInfo->GetEnumerationArtifactPath()); + const auto& enumeration = (enumerations[jobId] = ParseTestEnumerationFile(jobInfo->GetEnumerationArtifactPath())); // Write out the enumeration to a cache file if we have a cache write policy for this job if (jobInfo->GetCache().has_value() && jobInfo->GetCache()->m_policy == JobData::CachePolicy::Write) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h similarity index 90% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h index a005a32b0d..63d0b82d3e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Enumeration/TestImpactTestEnumerator.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h @@ -12,11 +12,11 @@ #pragma once -#include -#include -#include +#include -#include +#include +#include +#include namespace TestImpact { @@ -35,19 +35,19 @@ namespace TestImpact struct Cache { CachePolicy m_policy; - AZ::IO::Path m_file; + RepoPath m_file; }; - TestEnumerationJobData(const AZ::IO::Path& enumerationArtifact, AZStd::optional&& cache); + TestEnumerationJobData(const RepoPath& enumerationArtifact, AZStd::optional&& cache); //! Returns the path to the enumeration artifact produced by the test target. - const AZ::IO::Path& GetEnumerationArtifactPath() const; + const RepoPath& GetEnumerationArtifactPath() const; //! Returns the cache details for this job. const AZStd::optional& GetCache() const; private: - AZ::IO::Path m_enumerationArtifact; //!< Path to enumeration artifact to be processed. + RepoPath m_enumerationArtifact; //!< Path to enumeration artifact to be processed. AZStd::optional m_cache = AZStd::nullopt; //!< No caching takes place if cache is empty. }; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp new file mode 100644 index 0000000000..aeb3fcfeb3 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp @@ -0,0 +1,167 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +namespace TestImpact +{ + static constexpr char* const standAloneExtension = ".exe"; // these are os specific so move these to the platform dir + static constexpr char* const testRunnerExtension = ".dll"; // these are os specific so move these to the platform dir + + // SPLIT INTO COMMAND GENERATOR (INSTRUMENT, RUNNER, ENUM, RESULTS) + + TestJobInfoGenerator::TestJobInfoGenerator( + const RepoPath& sourceDir, + const RepoPath& targetBinaryDir, + const RepoPath& cacheDir, + const RepoPath& artifactDir, + const RepoPath& testRunnerBinary, + const RepoPath& instrumentBinary) + : m_sourceDir(sourceDir) + , m_targetBinaryDir(targetBinaryDir) + , m_cacheDir(cacheDir) + , m_artifactDir(artifactDir) + , m_testRunnerBinary(testRunnerBinary) + , m_instrumentBinary(instrumentBinary) + { + } + + AZStd::string TestJobInfoGenerator::GenerateLaunchArgument(const TestTarget* testTarget) const + { + if (testTarget->GetLaunchMethod() == LaunchMethod::StandAlone) + { + return AZStd::string::format( + "%s%s %s", + (m_targetBinaryDir / testTarget->GetOutputName()).c_str(), + standAloneExtension, + testTarget->GetCustomArgs().c_str()).c_str(); + } + else + { + return AZStd::string::format( + "\"%s\" \"%s%s\" %s", + m_testRunnerBinary.c_str(), + (m_targetBinaryDir / testTarget->GetOutputName()).c_str(), + testRunnerExtension, + testTarget->GetCustomArgs().c_str()).c_str(); + } + } + + RepoPath TestJobInfoGenerator::GenerateTargetEnumerationCacheFilePath(const TestTarget* testTarget) const + { + return AZStd::string::format("%s.cache", (m_artifactDir / testTarget->GetName()).c_str()); + } + + RepoPath TestJobInfoGenerator::GenerateTargetEnumerationArtifactFilePath(const TestTarget* testTarget) const + { + return AZStd::string::format("%s.Enumeration.xml", (m_artifactDir / testTarget->GetName()).c_str()); + } + + RepoPath TestJobInfoGenerator::GenerateTargetRunArtifactFilePath(const TestTarget* testTarget) const + { + return AZStd::string::format("%s.Run.xml", (m_artifactDir / testTarget->GetName()).c_str()); + } + + RepoPath TestJobInfoGenerator::GenerateTargetCoverageArtifactFilePath(const TestTarget* testTarget) const + { + return AZStd::string::format("%s.Coverage.xml", (m_artifactDir / testTarget->GetName()).c_str()); + } + + const RepoPath& TestJobInfoGenerator::GetCacheDir() const + { + return m_cacheDir; + } + + const RepoPath& TestJobInfoGenerator::GetArtifactDir() const + { + return m_artifactDir; + } + + TestEnumerator::JobInfo TestJobInfoGenerator::GenerateTestEnumerationJobInfo( + const TestTarget* testTarget, + TestEnumerator::JobInfo::Id jobId, + TestEnumerator::JobInfo::CachePolicy cachePolicy) const + { + using Command = TestEnumerator::Command; + using JobInfo = TestEnumerator::JobInfo; + using JobData = TestEnumerator::JobData; + using Cache = TestEnumerator::JobData::Cache; + + const auto enumerationArtifact = GenerateTargetEnumerationArtifactFilePath(testTarget); + const Command args = + { + AZStd::string::format( + "%s --gtest_list_tests --gtest_output=xml:\"%s\"", + GenerateLaunchArgument(testTarget).c_str(), + enumerationArtifact.c_str()) + }; + + return JobInfo(jobId, args, JobData(enumerationArtifact, Cache{ cachePolicy, GenerateTargetEnumerationCacheFilePath(testTarget) })); + } + + TestRunner::JobInfo TestJobInfoGenerator::GenerateRegularTestRunJobInfo( + const TestTarget* testTarget, + TestRunner::JobInfo::Id jobId) const + { + using Command = TestRunner::Command; + using JobInfo = TestRunner::JobInfo; + using JobData = TestRunner::JobData; + + const auto runArtifact = GenerateTargetRunArtifactFilePath(testTarget); + const Command args = + { + AZStd::string::format( + "%s --gtest_output=xml:\"%s\"", + GenerateLaunchArgument(testTarget).c_str(), + runArtifact.c_str()) + }; + + return JobInfo(jobId, args, JobData(runArtifact)); + } + + InstrumentedTestRunner::JobInfo TestJobInfoGenerator::GenerateInstrumentedTestRunJobInfo( + const TestTarget* testTarget, + InstrumentedTestRunner::JobInfo::Id jobId, + CoverageLevel coverageLevel) const + { + using Command = InstrumentedTestRunner::Command; + using JobInfo = InstrumentedTestRunner::JobInfo; + using JobData = InstrumentedTestRunner::JobData; + + const auto coverageArtifact = GenerateTargetCoverageArtifactFilePath(testTarget); + const auto runArtifact = GenerateTargetRunArtifactFilePath(testTarget); + const Command args = + { + AZStd::string::format( + "\"%s\" " // 1. Instrumented test runner + "--coverage_level %s " // 2. Coverage level + "--export_type cobertura:\"%s\" " // 3. Test coverage artifact path + "--modules \"%s\" " // 4. Modules path + "--excluded_modules \"%s\" " // 5. Exclude modules + "--sources \"%s\" -- " // 6. Sources path + "%s " // 7. Launch command + "--gtest_output=xml:\"%s\"", // 8. Result artifact + + m_instrumentBinary.c_str(), // 1. Instrumented test runner + (coverageLevel == CoverageLevel::Line ? "line" : "source"), // 2. Coverage level + coverageArtifact.c_str(), // 3. Test coverage artifact path + m_targetBinaryDir.c_str(), // 4. Modules path + m_testRunnerBinary.c_str(), // 5. Exclude modules + m_sourceDir.c_str(), // 6. Sources path + GenerateLaunchArgument(testTarget).c_str(), // 7. Launch command + runArtifact.c_str()) // 8. Result artifact + }; + + return JobInfo(jobId, args, JobData(runArtifact, coverageArtifact)); + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h new file mode 100644 index 0000000000..5d55904edd --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h @@ -0,0 +1,67 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace TestImpact +{ + class TestTarget; + + class TestJobInfoGenerator + { + public: + TestJobInfoGenerator( + const RepoPath& sourceDir, + const RepoPath& targetBinaryDir, + const RepoPath& cacheDir, + const RepoPath& artifactDir, + const RepoPath& testRunnerBinary, + const RepoPath& instrumentBinary); + + const RepoPath& GetCacheDir() const; + const RepoPath& GetArtifactDir() const; + + TestEnumerator::JobInfo GenerateTestEnumerationJobInfo( + const TestTarget* testTarget, + TestEnumerator::JobInfo::Id jobId, + TestEnumerator::JobInfo::CachePolicy cachePolicy) const; + + TestRunner::JobInfo GenerateRegularTestRunJobInfo( + const TestTarget* testTarget, + TestRunner::JobInfo::Id jobId) const; + + InstrumentedTestRunner::JobInfo GenerateInstrumentedTestRunJobInfo( + const TestTarget* testTarget, + InstrumentedTestRunner::JobInfo::Id jobId, + CoverageLevel coverageLevel) const; + private: + AZStd::string GenerateLaunchArgument(const TestTarget* testTarget) const; + RepoPath GenerateTargetEnumerationCacheFilePath(const TestTarget* testTarget) const; + RepoPath GenerateTargetEnumerationArtifactFilePath(const TestTarget* testTarget) const; + RepoPath GenerateTargetRunArtifactFilePath(const TestTarget* testTarget) const; + RepoPath GenerateTargetCoverageArtifactFilePath(const TestTarget* testTarget) const; + + RepoPath m_sourceDir; + RepoPath m_targetBinaryDir; + RepoPath m_cacheDir; + RepoPath m_artifactDir; + RepoPath m_testRunnerBinary; + RepoPath m_instrumentBinary; + }; +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h similarity index 92% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobRunner.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h index a4a0b19845..c60a93e21d 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Job/TestImpactTestJobRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h @@ -12,11 +12,10 @@ #pragma once -#include +#include #include #include -#include #include #include @@ -45,6 +44,7 @@ namespace TestImpact public: using JobData = AdditionalInfo; using JobInfo = JobInfo; + using Command = typename JobInfo::Command; using JobPayload = Payload; using Job = Job; using ClientJobCallback = AZStd::function; @@ -111,20 +111,22 @@ namespace TestImpact // Callback to handle job exception policies and client/derived callbacks const auto jobCallback = [this, &jobExceptionPolicy](const JobInfo& jobInfo, const JobMeta& meta, StdContent&& std) { + auto callbackResult = ProcessCallbackResult::Continue; if (meta.m_result == JobResult::FailedToExecute && IsFlagSet(jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)) { - throw TestJobException("Job failed to execute"); + callbackResult = ProcessCallbackResult::Abort; } else if (meta.m_result == JobResult::ExecutedWithFailure && IsFlagSet(jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)) { - throw TestJobException("Job executed with failure"); + callbackResult = ProcessCallbackResult::Abort; } if (m_derivedJobCallback.has_value()) { - if (const auto result = (*m_derivedJobCallback)(jobInfo, meta, AZStd::move(std)); result != CallbackResult::Continue) + if (const auto result = (*m_derivedJobCallback)(jobInfo, meta, AZStd::move(std)); + result == ProcessCallbackResult::Abort) { - return result; + callbackResult = ProcessCallbackResult::Abort; } } @@ -133,7 +135,7 @@ namespace TestImpact (*m_clientJobCallback)(jobInfo, meta); } - return CallbackResult::Continue; + return callbackResult; }; return m_jobRunner.Execute(jobInfos, jobCallback, payloadMapProducer); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp similarity index 69% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.cpp rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp index 3b7a088fb4..0d27d56af1 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp @@ -10,31 +10,32 @@ * */ +#include + #include #include -#include -#include -#include -#include +#include +#include +#include #include namespace TestImpact { - InstrumentedTestRunJobData::InstrumentedTestRunJobData(const AZ::IO::Path& resultsArtifact, const AZ::IO::Path& coverageArtifact) + InstrumentedTestRunJobData::InstrumentedTestRunJobData(const RepoPath& resultsArtifact, const RepoPath& coverageArtifact) : TestRunJobData(resultsArtifact) , m_coverageArtifact(coverageArtifact) { } - const AZ::IO::Path& InstrumentedTestRunJobData::GetCoverageArtifactPath() const + const RepoPath& InstrumentedTestRunJobData::GetCoverageArtifactPath() const { return m_coverageArtifact; } InstrumentedTestRunner::JobPayload ParseTestRunAndCoverageFiles( - const AZ::IO::Path& runFile, - const AZ::IO::Path& coverageFile, + const RepoPath& runFile, + const RepoPath& coverageFile, AZStd::chrono::milliseconds duration, InstrumentedTestRunner::CoverageExceptionPolicy coverageExceptionPolicy) { @@ -44,7 +45,7 @@ namespace TestImpact { AZ_TestImpact_Eval( !IsFlagSet(coverageExceptionPolicy, Bitwise::CoverageExceptionPolicy::OnEmptyCoverage), TestRunException, - "No coverage data generated"); + AZStd::string::format("No coverage data generated for '%s'", coverageFile.c_str())); } TestCoverage coverage(AZStd::move(moduleCoverages)); @@ -73,11 +74,24 @@ namespace TestImpact const auto& [meta, jobInfo] = jobData; if (meta.m_result == JobResult::ExecutedWithSuccess || meta.m_result == JobResult::ExecutedWithFailure) { - runs[jobId] = ParseTestRunAndCoverageFiles( - jobInfo->GetRunArtifactPath(), - jobInfo->GetCoverageArtifactPath(), - meta.m_duration.value(), - coverageExceptionPolicy); + try + { + runs[jobId] = ParseTestRunAndCoverageFiles( + jobInfo->GetRunArtifactPath(), + jobInfo->GetCoverageArtifactPath(), + meta.m_duration.value(), + coverageExceptionPolicy); + } + catch (const Exception& e) + { + AZ_Warning("RunInstrumentedTests", false, e.what()); + runs[jobId] = AZStd::nullopt; + + if (coverageExceptionPolicy == CoverageExceptionPolicy::OnEmptyCoverage) + { + break; + } + } } } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h similarity index 82% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h index e6f059b5d3..3b47f53128 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactInstrumentedTestRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h @@ -12,24 +12,25 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include +#include namespace TestImpact { //! Per-job data for instrumented test runs. - class InstrumentedTestRunJobData : public TestRunJobData + class InstrumentedTestRunJobData + : public TestRunJobData { public: - InstrumentedTestRunJobData(const AZ::IO::Path& resultsArtifact, const AZ::IO::Path& coverageArtifact); + InstrumentedTestRunJobData(const RepoPath& resultsArtifact, const RepoPath& coverageArtifact); //! Returns the path to the coverage artifact produced by the test target. - const AZ::IO::Path& GetCoverageArtifactPath() const; + const RepoPath& GetCoverageArtifactPath() const; private: - AZ::IO::Path m_coverageArtifact; //!< Path to coverage data. + RepoPath m_coverageArtifact; //!< Path to coverage data. }; namespace Bitwise @@ -43,7 +44,8 @@ namespace TestImpact } // namespace Bitwise //! Runs a batch of test targets to determine the test coverage and passes/failures. - class InstrumentedTestRunner : public TestJobRunner> + class InstrumentedTestRunner + : public TestJobRunner> { using JobRunner = TestJobRunner>; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.cpp similarity index 57% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.cpp rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.cpp index 0d50dbbf9a..6135383863 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.cpp @@ -10,16 +10,75 @@ * */ -#include +#include #include #include namespace TestImpact { - TestCoverage::TestCoverage(AZStd::vector&& moduleCoverages) + TestCoverage::TestCoverage(const TestCoverage& other) + : m_modules(other.m_modules) + , m_sourcesCovered(other.m_sourcesCovered) + , m_coverageLevel(other.m_coverageLevel) + { + } + + TestCoverage::TestCoverage(TestCoverage&& other) noexcept + : m_modules(AZStd::move(other.m_modules)) + , m_sourcesCovered(AZStd::move(other.m_sourcesCovered)) + { + AZStd::swap(m_coverageLevel, other.m_coverageLevel); + other.~TestCoverage(); + } + + TestCoverage::TestCoverage(const AZStd::vector& moduleCoverages) + : m_modules(moduleCoverages) + { + CalculateTestMetrics(); + } + + TestCoverage::TestCoverage(AZStd::vector&& moduleCoverages) noexcept : m_modules(AZStd::move(moduleCoverages)) { + CalculateTestMetrics(); + } + + TestCoverage::~TestCoverage() + { + m_modules.clear(); + m_coverageLevel.reset(); + m_sourcesCovered.clear(); + } + + TestCoverage& TestCoverage::operator=(const TestCoverage& other) + { + if (this != &other) + { + this->~TestCoverage(); + new(this)TestCoverage(other); + } + + return *this; + } + + TestCoverage& TestCoverage::operator=(TestCoverage&& other) noexcept + { + if (this != &other) + { + this->~TestCoverage(); + new(this)TestCoverage(AZStd::move(other)); + other.~TestCoverage(); + } + + return *this; + } + + void TestCoverage::CalculateTestMetrics() + { + m_coverageLevel.reset(); + m_sourcesCovered.clear(); + for (const auto& moduleCovered : m_modules) { for (const auto& sourceCovered : moduleCovered.m_sources) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.h similarity index 84% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.h index 7253a29df0..c672814f7c 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestCoverage.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.h @@ -29,7 +29,14 @@ namespace TestImpact class TestCoverage { public: - TestCoverage(AZStd::vector&& moduleCoverages); + TestCoverage(const TestCoverage&); + TestCoverage(TestCoverage&&) noexcept; + TestCoverage(AZStd::vector&& moduleCoverages) noexcept; + TestCoverage(const AZStd::vector& moduleCoverages); + ~TestCoverage(); + + TestCoverage& operator=(const TestCoverage&); + TestCoverage& operator=(TestCoverage&&) noexcept; //! Returns the number of unique sources covered. size_t GetNumSourcesCovered() const; @@ -47,6 +54,8 @@ namespace TestImpact AZStd::optional GetCoverageLevel() const; private: + void CalculateTestMetrics(); + AZStd::vector m_modules; AZStd::vector m_sourcesCovered; AZStd::optional m_coverageLevel; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.cpp similarity index 53% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.cpp rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.cpp index 1ba2a3e849..9ee1569f0e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.cpp @@ -14,10 +14,76 @@ namespace TestImpact { - TestRun::TestRun(AZStd::vector&& testSuites, AZStd::chrono::milliseconds duration) + TestRun::TestRun(const TestRun& other) + : TestSuiteContainer(other) + { + CalculateTestMetrics(); + } + + TestRun::TestRun(TestRun&& other) noexcept + : TestSuiteContainer(AZStd::move(other)) + , m_numRuns(other.m_numRuns) + , m_numNotRuns(other.m_numNotRuns) + , m_numPasses(other.m_numPasses) + , m_numFailures(other.m_numFailures) + , m_duration(other.m_duration) + { + other.~TestRun(); + } + + TestRun::TestRun(AZStd::vector&& testSuites, AZStd::chrono::milliseconds duration) noexcept : TestSuiteContainer(AZStd::move(testSuites)) , m_duration(duration) { + CalculateTestMetrics(); + } + + TestRun::TestRun(const AZStd::vector& testSuites, AZStd::chrono::milliseconds duration) + : TestSuiteContainer(testSuites) + , m_duration(duration) + { + CalculateTestMetrics(); + } + + TestRun::~TestRun() + { + m_numRuns = 0; + m_numNotRuns = 0; + m_numPasses = 0; + m_numFailures = 0; + m_duration = AZStd::chrono::milliseconds{ 0 }; + } + + TestRun& TestRun::operator=(TestRun&& other) noexcept + { + if (this != &other) + { + this->~TestRun(); + new(this)TestRun(AZStd::move(other)); + other.~TestRun(); + } + + return *this; + } + + TestRun& TestRun::operator=(const TestRun& other) + { + if (this != &other) + { + this->~TestRun(); + new(this)TestRun(other); + } + + return *this; + } + + void TestRun::CalculateTestMetrics() + { + m_numRuns = 0; + m_numNotRuns = 0; + m_numPasses = 0; + m_numFailures = 0; + for (const auto& suite : m_testSuites) { for (const auto& test : suite.m_tests) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.h similarity index 78% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.h index 75d2140a68..f2eeac14fa 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRun.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.h @@ -13,7 +13,7 @@ #pragma once #include -#include +#include namespace TestImpact { @@ -24,7 +24,14 @@ namespace TestImpact using TestSuiteContainer = TestSuiteContainer; public: - TestRun(AZStd::vector&& testSuites, AZStd::chrono::milliseconds duration); + TestRun(const TestRun&); + TestRun(TestRun&&) noexcept; + TestRun(const AZStd::vector& testSuites, AZStd::chrono::milliseconds duration); + TestRun(AZStd::vector&& testSuites, AZStd::chrono::milliseconds duration) noexcept; + ~TestRun(); + + TestRun& operator=(const TestRun&); + TestRun& operator=(TestRun&&) noexcept; //! Returns the total number of tests that were run. size_t GetNumRuns() const; @@ -42,10 +49,12 @@ namespace TestImpact AZStd::chrono::milliseconds GetDuration() const; private: + void CalculateTestMetrics(); + size_t m_numRuns = 0; size_t m_numNotRuns = 0; size_t m_numPasses = 0; size_t m_numFailures = 0; - AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{0}; + AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{0}; // this might be removed... }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunException.h similarity index 100% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunException.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunException.h diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunJobData.cpp similarity index 78% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.cpp rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunJobData.cpp index fa40b30263..a7ed446a25 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunJobData.cpp @@ -10,16 +10,16 @@ * */ -#include +#include namespace TestImpact { - TestRunJobData::TestRunJobData(const AZ::IO::Path& resultsArtifact) + TestRunJobData::TestRunJobData(const RepoPath& resultsArtifact) : m_runArtifact(resultsArtifact) { } - const AZ::IO::Path& TestRunJobData::GetRunArtifactPath() const + const RepoPath& TestRunJobData::GetRunArtifactPath() const { return m_runArtifact; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunJobData.h similarity index 77% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunJobData.h index 802aa0d30f..a2033b5945 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunJobData.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunJobData.h @@ -12,7 +12,7 @@ #pragma once -#include +#include namespace TestImpact { @@ -20,12 +20,12 @@ namespace TestImpact class TestRunJobData { public: - TestRunJobData(const AZ::IO::Path& resultsArtifact); + TestRunJobData(const RepoPath& resultsArtifact); //! Returns the path to the test run artifact produced by the test target. - const AZ::IO::Path& GetRunArtifactPath() const; + const RepoPath& GetRunArtifactPath() const; private: - AZ::IO::Path m_runArtifact; //!< Path to results data. + RepoPath m_runArtifact; //!< Path to results data. }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunSerializer.cpp similarity index 98% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.cpp rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunSerializer.cpp index 7f29dd77a9..7fe8789d22 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunSerializer.cpp @@ -10,8 +10,8 @@ * */ -#include -#include +#include +#include #include #include diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunSerializer.h similarity index 95% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunSerializer.h index 5978b27105..a73e675559 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunSerializer.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunSerializer.h @@ -12,7 +12,7 @@ #pragma once -#include +#include #include diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp similarity index 73% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.cpp rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp index dde7dd7d47..ebb846f406 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp @@ -10,17 +10,18 @@ * */ +#include + #include -#include -#include -#include -#include +#include +#include +#include #include namespace TestImpact { - TestRun ParseTestRunFile(const AZ::IO::Path& runFile, AZStd::chrono::milliseconds duration) + TestRun ParseTestRunFile(const RepoPath& runFile, AZStd::chrono::milliseconds duration) { return TestRun(GTest::TestRunSuitesFactory(ReadFileContents(runFile)), duration); } @@ -46,7 +47,15 @@ namespace TestImpact const auto& [meta, jobInfo] = jobData; if (meta.m_result == JobResult::ExecutedWithSuccess || meta.m_result == JobResult::ExecutedWithFailure) { - runs[jobId] = ParseTestRunFile(jobInfo->GetRunArtifactPath(), meta.m_duration.value()); + try + { + runs[jobId] = ParseTestRunFile(jobInfo->GetRunArtifactPath(), meta.m_duration.value()); + } + catch (const Exception& e) + { + AZ_Warning("RunTests", false, e.what()); + runs[jobId] = AZStd::nullopt; + } } } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h similarity index 93% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h index 0bf66601fe..379c6b424e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/Run/TestImpactTestRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h @@ -12,9 +12,9 @@ #pragma once -#include -#include -#include +#include +#include +#include namespace TestImpact { diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/TestImpactTestSuiteContainer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestSuiteContainer.h similarity index 57% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/TestImpactTestSuiteContainer.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestSuiteContainer.h index c41d76bd04..4f3fdebb08 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Test/TestImpactTestSuiteContainer.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestSuiteContainer.h @@ -22,7 +22,14 @@ namespace TestImpact class TestSuiteContainer { public: - TestSuiteContainer(AZStd::vector&& testSuites); + TestSuiteContainer(const TestSuiteContainer&); + TestSuiteContainer(TestSuiteContainer&&) noexcept; + TestSuiteContainer(const AZStd::vector& testSuites); + TestSuiteContainer(AZStd::vector&& testSuites) noexcept; + virtual ~TestSuiteContainer(); + + TestSuiteContainer& operator=(const TestSuiteContainer&); + TestSuiteContainer& operator=(TestSuiteContainer&&) noexcept; //! Returns the test suites in this container. const AZStd::vector& GetTestSuites() const; @@ -39,6 +46,9 @@ namespace TestImpact //! Returns the total number of disabled tests across all test suites. size_t GetNumDisabledTests() const; + private: + void CalculateTestMetrics(); + protected: AZStd::vector m_testSuites; size_t m_numDisabledTests = 0; @@ -46,9 +56,75 @@ namespace TestImpact }; template - TestSuiteContainer::TestSuiteContainer(AZStd::vector&& testSuites) + TestSuiteContainer::TestSuiteContainer(TestSuiteContainer&& other) noexcept + : m_testSuites(AZStd::move(other.m_testSuites)) + , m_numDisabledTests(other.m_numDisabledTests) + , m_numEnabledTests(other.m_numEnabledTests) + { + other.~TestSuiteContainer(); + } + + template + TestSuiteContainer::TestSuiteContainer(const TestSuiteContainer& other) + : m_testSuites(other.m_testSuites.begin(), other.m_testSuites.end()) + , m_numDisabledTests(other.m_numDisabledTests) + , m_numEnabledTests(other.m_numEnabledTests) + { + } + + template + TestSuiteContainer::~TestSuiteContainer() + { + m_testSuites.clear(); + m_numDisabledTests = 0; + m_numEnabledTests = 0; + } + + template + TestSuiteContainer::TestSuiteContainer(AZStd::vector&& testSuites) noexcept : m_testSuites(std::move(testSuites)) { + CalculateTestMetrics(); + } + + template + TestSuiteContainer::TestSuiteContainer(const AZStd::vector& testSuites) + : m_testSuites(testSuites) + { + CalculateTestMetrics(); + } + + template + TestSuiteContainer& TestSuiteContainer::operator=(TestSuiteContainer&& other) noexcept + { + if (this != &other) + { + this->~TestSuiteContainer(); + new(this)TestSuiteContainer(AZStd::move(other)); + other.~TestSuiteContainer(); + } + + return *this; + } + + template + TestSuiteContainer& TestSuiteContainer::operator=(const TestSuiteContainer& other) + { + if (this != &other) + { + this->~TestSuiteContainer(); + new(this)TestSuiteContainer(other); + } + + return *this; + } + + template + void TestSuiteContainer::CalculateTestMetrics() + { + m_numDisabledTests = 0; + m_numEnabledTests = 0; + for (const auto& suite : m_testSuites) { if (suite.m_enabled) From f08de28cd819e38b995c4337984ab7a1cc804993 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 21 May 2021 13:13:54 +0100 Subject: [PATCH 046/233] Remove JobInfoGenerator from this PR --- .../TestImpactTestJobInfoGenerator.cpp | 167 ------------------ .../TestImpactTestJobInfoGenerator.h | 67 ------- 2 files changed, 234 deletions(-) delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp deleted file mode 100644 index aeb3fcfeb3..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include - -namespace TestImpact -{ - static constexpr char* const standAloneExtension = ".exe"; // these are os specific so move these to the platform dir - static constexpr char* const testRunnerExtension = ".dll"; // these are os specific so move these to the platform dir - - // SPLIT INTO COMMAND GENERATOR (INSTRUMENT, RUNNER, ENUM, RESULTS) - - TestJobInfoGenerator::TestJobInfoGenerator( - const RepoPath& sourceDir, - const RepoPath& targetBinaryDir, - const RepoPath& cacheDir, - const RepoPath& artifactDir, - const RepoPath& testRunnerBinary, - const RepoPath& instrumentBinary) - : m_sourceDir(sourceDir) - , m_targetBinaryDir(targetBinaryDir) - , m_cacheDir(cacheDir) - , m_artifactDir(artifactDir) - , m_testRunnerBinary(testRunnerBinary) - , m_instrumentBinary(instrumentBinary) - { - } - - AZStd::string TestJobInfoGenerator::GenerateLaunchArgument(const TestTarget* testTarget) const - { - if (testTarget->GetLaunchMethod() == LaunchMethod::StandAlone) - { - return AZStd::string::format( - "%s%s %s", - (m_targetBinaryDir / testTarget->GetOutputName()).c_str(), - standAloneExtension, - testTarget->GetCustomArgs().c_str()).c_str(); - } - else - { - return AZStd::string::format( - "\"%s\" \"%s%s\" %s", - m_testRunnerBinary.c_str(), - (m_targetBinaryDir / testTarget->GetOutputName()).c_str(), - testRunnerExtension, - testTarget->GetCustomArgs().c_str()).c_str(); - } - } - - RepoPath TestJobInfoGenerator::GenerateTargetEnumerationCacheFilePath(const TestTarget* testTarget) const - { - return AZStd::string::format("%s.cache", (m_artifactDir / testTarget->GetName()).c_str()); - } - - RepoPath TestJobInfoGenerator::GenerateTargetEnumerationArtifactFilePath(const TestTarget* testTarget) const - { - return AZStd::string::format("%s.Enumeration.xml", (m_artifactDir / testTarget->GetName()).c_str()); - } - - RepoPath TestJobInfoGenerator::GenerateTargetRunArtifactFilePath(const TestTarget* testTarget) const - { - return AZStd::string::format("%s.Run.xml", (m_artifactDir / testTarget->GetName()).c_str()); - } - - RepoPath TestJobInfoGenerator::GenerateTargetCoverageArtifactFilePath(const TestTarget* testTarget) const - { - return AZStd::string::format("%s.Coverage.xml", (m_artifactDir / testTarget->GetName()).c_str()); - } - - const RepoPath& TestJobInfoGenerator::GetCacheDir() const - { - return m_cacheDir; - } - - const RepoPath& TestJobInfoGenerator::GetArtifactDir() const - { - return m_artifactDir; - } - - TestEnumerator::JobInfo TestJobInfoGenerator::GenerateTestEnumerationJobInfo( - const TestTarget* testTarget, - TestEnumerator::JobInfo::Id jobId, - TestEnumerator::JobInfo::CachePolicy cachePolicy) const - { - using Command = TestEnumerator::Command; - using JobInfo = TestEnumerator::JobInfo; - using JobData = TestEnumerator::JobData; - using Cache = TestEnumerator::JobData::Cache; - - const auto enumerationArtifact = GenerateTargetEnumerationArtifactFilePath(testTarget); - const Command args = - { - AZStd::string::format( - "%s --gtest_list_tests --gtest_output=xml:\"%s\"", - GenerateLaunchArgument(testTarget).c_str(), - enumerationArtifact.c_str()) - }; - - return JobInfo(jobId, args, JobData(enumerationArtifact, Cache{ cachePolicy, GenerateTargetEnumerationCacheFilePath(testTarget) })); - } - - TestRunner::JobInfo TestJobInfoGenerator::GenerateRegularTestRunJobInfo( - const TestTarget* testTarget, - TestRunner::JobInfo::Id jobId) const - { - using Command = TestRunner::Command; - using JobInfo = TestRunner::JobInfo; - using JobData = TestRunner::JobData; - - const auto runArtifact = GenerateTargetRunArtifactFilePath(testTarget); - const Command args = - { - AZStd::string::format( - "%s --gtest_output=xml:\"%s\"", - GenerateLaunchArgument(testTarget).c_str(), - runArtifact.c_str()) - }; - - return JobInfo(jobId, args, JobData(runArtifact)); - } - - InstrumentedTestRunner::JobInfo TestJobInfoGenerator::GenerateInstrumentedTestRunJobInfo( - const TestTarget* testTarget, - InstrumentedTestRunner::JobInfo::Id jobId, - CoverageLevel coverageLevel) const - { - using Command = InstrumentedTestRunner::Command; - using JobInfo = InstrumentedTestRunner::JobInfo; - using JobData = InstrumentedTestRunner::JobData; - - const auto coverageArtifact = GenerateTargetCoverageArtifactFilePath(testTarget); - const auto runArtifact = GenerateTargetRunArtifactFilePath(testTarget); - const Command args = - { - AZStd::string::format( - "\"%s\" " // 1. Instrumented test runner - "--coverage_level %s " // 2. Coverage level - "--export_type cobertura:\"%s\" " // 3. Test coverage artifact path - "--modules \"%s\" " // 4. Modules path - "--excluded_modules \"%s\" " // 5. Exclude modules - "--sources \"%s\" -- " // 6. Sources path - "%s " // 7. Launch command - "--gtest_output=xml:\"%s\"", // 8. Result artifact - - m_instrumentBinary.c_str(), // 1. Instrumented test runner - (coverageLevel == CoverageLevel::Line ? "line" : "source"), // 2. Coverage level - coverageArtifact.c_str(), // 3. Test coverage artifact path - m_targetBinaryDir.c_str(), // 4. Modules path - m_testRunnerBinary.c_str(), // 5. Exclude modules - m_sourceDir.c_str(), // 6. Sources path - GenerateLaunchArgument(testTarget).c_str(), // 7. Launch command - runArtifact.c_str()) // 8. Result artifact - }; - - return JobInfo(jobId, args, JobData(runArtifact, coverageArtifact)); - } -} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h deleted file mode 100644 index 5d55904edd..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#pragma once - -#include - -#include -#include -#include -#include - -namespace TestImpact -{ - class TestTarget; - - class TestJobInfoGenerator - { - public: - TestJobInfoGenerator( - const RepoPath& sourceDir, - const RepoPath& targetBinaryDir, - const RepoPath& cacheDir, - const RepoPath& artifactDir, - const RepoPath& testRunnerBinary, - const RepoPath& instrumentBinary); - - const RepoPath& GetCacheDir() const; - const RepoPath& GetArtifactDir() const; - - TestEnumerator::JobInfo GenerateTestEnumerationJobInfo( - const TestTarget* testTarget, - TestEnumerator::JobInfo::Id jobId, - TestEnumerator::JobInfo::CachePolicy cachePolicy) const; - - TestRunner::JobInfo GenerateRegularTestRunJobInfo( - const TestTarget* testTarget, - TestRunner::JobInfo::Id jobId) const; - - InstrumentedTestRunner::JobInfo GenerateInstrumentedTestRunJobInfo( - const TestTarget* testTarget, - InstrumentedTestRunner::JobInfo::Id jobId, - CoverageLevel coverageLevel) const; - private: - AZStd::string GenerateLaunchArgument(const TestTarget* testTarget) const; - RepoPath GenerateTargetEnumerationCacheFilePath(const TestTarget* testTarget) const; - RepoPath GenerateTargetEnumerationArtifactFilePath(const TestTarget* testTarget) const; - RepoPath GenerateTargetRunArtifactFilePath(const TestTarget* testTarget) const; - RepoPath GenerateTargetCoverageArtifactFilePath(const TestTarget* testTarget) const; - - RepoPath m_sourceDir; - RepoPath m_targetBinaryDir; - RepoPath m_cacheDir; - RepoPath m_artifactDir; - RepoPath m_testRunnerBinary; - RepoPath m_instrumentBinary; - }; -} From d917388bf9fc31df0c175f120daca53e826944e3 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 21 May 2021 13:16:38 +0100 Subject: [PATCH 047/233] Remove dead comment --- .../Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.h index f2eeac14fa..a0acb37061 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.h @@ -55,6 +55,6 @@ namespace TestImpact size_t m_numNotRuns = 0; size_t m_numPasses = 0; size_t m_numFailures = 0; - AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{0}; // this might be removed... + AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{0}; }; } // namespace TestImpact From 7c28480307746b6571acae5f059589f2fcbbe605 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 21 May 2021 14:09:12 +0100 Subject: [PATCH 048/233] Address PR comments --- .../TestEngine/Run/TestImpactTestCoverage.cpp | 19 ++++++----------- .../TestEngine/Run/TestImpactTestCoverage.h | 1 - .../TestEngine/Run/TestImpactTestRun.cpp | 10 --------- .../Source/TestEngine/Run/TestImpactTestRun.h | 1 - .../TestEngine/TestImpactTestSuiteContainer.h | 21 ++++++------------- 5 files changed, 12 insertions(+), 40 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.cpp index 6135383863..453b1fa962 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.cpp @@ -29,7 +29,6 @@ namespace TestImpact , m_sourcesCovered(AZStd::move(other.m_sourcesCovered)) { AZStd::swap(m_coverageLevel, other.m_coverageLevel); - other.~TestCoverage(); } TestCoverage::TestCoverage(const AZStd::vector& moduleCoverages) @@ -44,19 +43,13 @@ namespace TestImpact CalculateTestMetrics(); } - TestCoverage::~TestCoverage() - { - m_modules.clear(); - m_coverageLevel.reset(); - m_sourcesCovered.clear(); - } - TestCoverage& TestCoverage::operator=(const TestCoverage& other) { if (this != &other) { - this->~TestCoverage(); - new(this)TestCoverage(other); + m_modules = other.m_modules; + m_sourcesCovered = other.m_sourcesCovered; + m_coverageLevel = other.m_coverageLevel; } return *this; @@ -66,9 +59,9 @@ namespace TestImpact { if (this != &other) { - this->~TestCoverage(); - new(this)TestCoverage(AZStd::move(other)); - other.~TestCoverage(); + m_modules = AZStd::move(other.m_modules); + m_sourcesCovered = other.m_sourcesCovered; + m_coverageLevel = other.m_coverageLevel; } return *this; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.h index c672814f7c..e7599fb145 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.h @@ -33,7 +33,6 @@ namespace TestImpact TestCoverage(TestCoverage&&) noexcept; TestCoverage(AZStd::vector&& moduleCoverages) noexcept; TestCoverage(const AZStd::vector& moduleCoverages); - ~TestCoverage(); TestCoverage& operator=(const TestCoverage&); TestCoverage& operator=(TestCoverage&&) noexcept; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.cpp index 9ee1569f0e..3796bbfe7e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.cpp @@ -28,7 +28,6 @@ namespace TestImpact , m_numFailures(other.m_numFailures) , m_duration(other.m_duration) { - other.~TestRun(); } TestRun::TestRun(AZStd::vector&& testSuites, AZStd::chrono::milliseconds duration) noexcept @@ -45,15 +44,6 @@ namespace TestImpact CalculateTestMetrics(); } - TestRun::~TestRun() - { - m_numRuns = 0; - m_numNotRuns = 0; - m_numPasses = 0; - m_numFailures = 0; - m_duration = AZStd::chrono::milliseconds{ 0 }; - } - TestRun& TestRun::operator=(TestRun&& other) noexcept { if (this != &other) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.h index a0acb37061..f3a1ba93de 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.h @@ -28,7 +28,6 @@ namespace TestImpact TestRun(TestRun&&) noexcept; TestRun(const AZStd::vector& testSuites, AZStd::chrono::milliseconds duration); TestRun(AZStd::vector&& testSuites, AZStd::chrono::milliseconds duration) noexcept; - ~TestRun(); TestRun& operator=(const TestRun&); TestRun& operator=(TestRun&&) noexcept; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestSuiteContainer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestSuiteContainer.h index 4f3fdebb08..5991640c46 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestSuiteContainer.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestSuiteContainer.h @@ -26,7 +26,6 @@ namespace TestImpact TestSuiteContainer(TestSuiteContainer&&) noexcept; TestSuiteContainer(const AZStd::vector& testSuites); TestSuiteContainer(AZStd::vector&& testSuites) noexcept; - virtual ~TestSuiteContainer(); TestSuiteContainer& operator=(const TestSuiteContainer&); TestSuiteContainer& operator=(TestSuiteContainer&&) noexcept; @@ -61,7 +60,6 @@ namespace TestImpact , m_numDisabledTests(other.m_numDisabledTests) , m_numEnabledTests(other.m_numEnabledTests) { - other.~TestSuiteContainer(); } template @@ -72,14 +70,6 @@ namespace TestImpact { } - template - TestSuiteContainer::~TestSuiteContainer() - { - m_testSuites.clear(); - m_numDisabledTests = 0; - m_numEnabledTests = 0; - } - template TestSuiteContainer::TestSuiteContainer(AZStd::vector&& testSuites) noexcept : m_testSuites(std::move(testSuites)) @@ -99,9 +89,9 @@ namespace TestImpact { if (this != &other) { - this->~TestSuiteContainer(); - new(this)TestSuiteContainer(AZStd::move(other)); - other.~TestSuiteContainer(); + m_testSuites = AZStd::move(other.m_testSuites); + m_numDisabledTests = other.m_numDisabledTests; + m_numEnabledTests = other.m_numEnabledTests; } return *this; @@ -112,8 +102,9 @@ namespace TestImpact { if (this != &other) { - this->~TestSuiteContainer(); - new(this)TestSuiteContainer(other); + m_testSuites = other.m_testSuites; + m_numDisabledTests = other.m_numDisabledTests; + m_numEnabledTests = other.m_numEnabledTests; } return *this; From 084e459a632b4a1417e719c4e4ff8b3c9cdb61f6 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 21 May 2021 17:41:19 +0100 Subject: [PATCH 049/233] Add missing change from PR feedback --- .../TestEngine/Run/TestImpactTestRun.cpp | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.cpp index 3796bbfe7e..6a361ca9d0 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRun.cpp @@ -16,6 +16,11 @@ namespace TestImpact { TestRun::TestRun(const TestRun& other) : TestSuiteContainer(other) + , m_numRuns(other.m_numRuns) + , m_numNotRuns(other.m_numNotRuns) + , m_numPasses(other.m_numPasses) + , m_numFailures(other.m_numFailures) + , m_duration(other.m_duration) { CalculateTestMetrics(); } @@ -48,9 +53,12 @@ namespace TestImpact { if (this != &other) { - this->~TestRun(); - new(this)TestRun(AZStd::move(other)); - other.~TestRun(); + TestSuiteContainer::operator=(AZStd::move(other)); + m_numRuns = other.m_numRuns; + m_numNotRuns = other.m_numNotRuns; + m_numPasses = other.m_numPasses; + m_numFailures = other.m_numFailures; + m_duration = other.m_duration; } return *this; @@ -60,8 +68,12 @@ namespace TestImpact { if (this != &other) { - this->~TestRun(); - new(this)TestRun(other); + TestSuiteContainer::operator=(other); + m_numRuns = other.m_numRuns; + m_numNotRuns = other.m_numNotRuns; + m_numPasses = other.m_numPasses; + m_numFailures = other.m_numFailures; + m_duration = other.m_duration; } return *this; From 95a30b0feea76583bd32dece9eeebd41b9ddaef9 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 21 May 2021 19:39:50 +0100 Subject: [PATCH 050/233] Add missing file from refactor --- .../Code/Source/Artifact/Dynamic/TestImpactCoverage.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactCoverage.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactCoverage.h index 54b78d682e..4e1c435841 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactCoverage.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Dynamic/TestImpactCoverage.h @@ -12,6 +12,8 @@ #pragma once +#include + #include #include @@ -27,14 +29,14 @@ namespace TestImpact //! Coverage information about a particular source file. struct SourceCoverage { - AZStd::string m_path; //!< Source file path. + RepoPath m_path; //!< Source file path. AZStd::vector m_coverage; //!< Source file line coverage (empty if source level coverage only). }; //! Coverage information about a particular module (executable, shared library). struct ModuleCoverage { - AZStd::string m_path; //!< Module path. + RepoPath m_path; //!< Module path. AZStd::vector m_sources; //!< Sources of this module that are covered. }; } // namespace TestImpact From 597f0905c5250f8417dfe19aa5c56c9b526ba06d Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 21 May 2021 20:13:10 +0100 Subject: [PATCH 051/233] Refactor ProcessScheduler with Execute method --- .../JobRunner/TestImpactProcessJobRunner.h | 78 +++++------- .../Scheduler/TestImpactProcessScheduler.cpp | 118 +++++++++++++----- .../Scheduler/TestImpactProcessScheduler.h | 50 ++++---- .../Enumeration/TestImpactTestEnumerator.cpp | 32 +++-- .../Enumeration/TestImpactTestEnumerator.h | 23 ++-- .../JobRunner/TestImpactTestJobRunner.h | 70 +++++------ .../Run/TestImpactInstrumentedTestRunner.cpp | 26 ++-- .../Run/TestImpactInstrumentedTestRunner.h | 25 ++-- .../TestEngine/Run/TestImpactTestCoverage.cpp | 2 +- .../TestEngine/Run/TestImpactTestCoverage.h | 4 +- .../TestEngine/Run/TestImpactTestRunner.cpp | 26 ++-- .../TestEngine/Run/TestImpactTestRunner.h | 21 ++-- 12 files changed, 269 insertions(+), 206 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h index 0c30accc84..384111b823 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h @@ -50,32 +50,29 @@ namespace TestImpact { public: //! Constructs the job runner with the specified parameters to constrain job runs. - //! @param stdOutRouting The standard output routing to be specified for all jobs. - //! @param stdErrRouting The standard error routing to be specified for all jobs. //! @param maxConcurrentProcesses he maximum number of concurrent jobs in-flight. - //! @param processTimeout The maximum duration a job may be in-flight before being forcefully terminated (nullopt if no timeout). - //! @param scheduleTimeout The maximum duration the scheduler may run before forcefully terminating all in-flight jobs (nullopt if - //! no timeout). - JobRunner( - StdOutputRouting stdOutRouting, - StdErrorRouting stdErrRouting, - size_t maxConcurrentProcesses, - AZStd::optional processTimeout, - AZStd::optional scheduleTimeout); - + JobRunner(size_t maxConcurrentProcesses); + //! Executes the specified jobs and returns the products of their labor. - //! @note: the job and payload callbacks are specified here rather than in the constructor to allow clients to use capturing lambdas - //! should they desire to. //! @param jobs The arguments (and other pertinent information) required for each job to be run. + //! @param stdOutRouting The standard output routing to be specified for all jobs. + //! @param stdErrRouting The standard error routing to be specified for all jobs. + //! @param jobTimeout The maximum duration a job may be in-flight before being forcefully terminated (nullopt if no timeout). + //! @param runnerTimeout The maximum duration the scheduler may run before forcefully terminating all in-flight jobs (nullopt if no timeout). + //! @param payloadMapProducer The client callback to be called when all jobs have finished to transform the work produced by each job into the desired output. //! @param jobCallback The client callback to be called when each job changes state. - //! @param payloadMapProducer The client callback to be called when all jobs have finished to transform the work produced by each - //! job into the desired output. - AZStd::vector Execute( - const AZStd::vector& jobs, JobCallback jobCallback, - PayloadMapProducer payloadMapProducer); + //! @return The result of the run sequence and the jobs with their associated payloads. + AZStd::pair> Execute( + const AZStd::vector& jobs, + PayloadMapProducer payloadMapProducer, + StdOutputRouting stdOutRouting, + StdErrorRouting stdErrRouting, + AZStd::optional jobTimeout, + AZStd::optional runnerTimeout, + JobCallback jobCallback); private: - size_t m_maxConcurrentProcesses = 0; //!< Maximum number of concurrent jobs being executed at a given time. + ProcessScheduler m_processScheduler; StdOutputRouting m_stdOutRouting; //!< Standard output routing from each job process to job runner. StdErrorRouting m_stdErrRouting; //!< Standard error routing from each job process to job runner AZStd::optional m_jobTimeout; //!< Maximum time a job can run for before being forcefully terminated. @@ -83,25 +80,20 @@ namespace TestImpact }; template - JobRunner::JobRunner( - StdOutputRouting stdOutRouting, - StdErrorRouting stdErrRouting, - size_t maxConcurrentProcesses, - AZStd::optional jobTimeout, - AZStd::optional runnerTimeout) - : m_maxConcurrentProcesses(maxConcurrentProcesses) - , m_stdOutRouting(stdOutRouting) - , m_stdErrRouting(stdErrRouting) - , m_jobTimeout(jobTimeout) - , m_runnerTimeout(runnerTimeout) + JobRunner::JobRunner(size_t maxConcurrentProcesses) + : m_processScheduler(maxConcurrentProcesses) { } template - AZStd::vector JobRunner::Execute( + AZStd::pair> JobRunner::Execute( const AZStd::vector& jobInfos, - JobCallback jobCallback, - PayloadMapProducer payloadMapProducer) + PayloadMapProducer payloadMapProducer, + StdOutputRouting stdOutRouting, + StdErrorRouting stdErrRouting, + AZStd::optional jobTimeout, + AZStd::optional runnerTimeout, + JobCallback jobCallback) { AZStd::vector processes; AZStd::unordered_map> metas; @@ -115,11 +107,11 @@ namespace TestImpact const auto* jobInfo = &jobInfos[jobIndex]; const auto jobId = jobInfo->GetId().m_value; metas.emplace(jobId, AZStd::pair{JobMeta{}, jobInfo}); - processes.emplace_back(jobId, m_stdOutRouting, m_stdErrRouting, jobInfo->GetCommand().m_args); + processes.emplace_back(jobId, stdOutRouting, stdErrRouting, jobInfo->GetCommand().m_args); } // Wrapper around low-level process launch callback to gather job meta-data and present a simplified callback interface to the client - const auto processLaunchCallback = [&jobCallback, &jobInfos, &metas]( + const ProcessLaunchCallback processLaunchCallback = [&jobCallback, &jobInfos, &metas]( TestImpact::ProcessId pid, TestImpact::LaunchResult launchResult, AZStd::chrono::high_resolution_clock::time_point createTime) @@ -138,7 +130,7 @@ namespace TestImpact }; // Wrapper around low-level process exit callback to gather job meta-data and present a simplified callback interface to the client - const auto processExitCallback = [&jobCallback, &jobInfos, &metas]( + const ProcessExitCallback processExitCallback = [&jobCallback, &jobInfos, &metas]( TestImpact::ProcessId pid, TestImpact::ExitCondition exitCondition, TestImpact::ReturnCode returnCode, @@ -169,14 +161,12 @@ namespace TestImpact }; // Schedule all jobs for execution - ProcessScheduler scheduler( + const auto result = m_processScheduler.Execute( processes, + jobTimeout, + runnerTimeout, processLaunchCallback, - processExitCallback, - m_maxConcurrentProcesses, - m_jobTimeout, - m_runnerTimeout - ); + processExitCallback); // Hand off the jobs to the client for payload generation auto payloadMap = payloadMapProducer(metas); @@ -188,6 +178,6 @@ namespace TestImpact jobs.emplace_back(JobT(jobInfo, AZStd::move(metas.at(jobId).first), AZStd::move(payloadMap[jobId]))); } - return jobs; + return { result, jobs }; } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp index 04a68bf612..8d6c5c6739 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp @@ -18,7 +18,7 @@ namespace TestImpact { - struct ProcessScheduler::ProcessInFlight + struct ProcessInFlight { AZStd::unique_ptr m_process; AZStd::optional m_startTime; @@ -26,29 +26,64 @@ namespace TestImpact AZStd::string m_stdError; }; - ProcessScheduler::ProcessScheduler( - const AZStd::vector& processes, - const ProcessLaunchCallback& processLaunchCallback, - const ProcessExitCallback& processExitCallback, + class ProcessScheduler::ExecutionState + { + public: + ExecutionState( + size_t maxConcurrentProcesses, + AZStd::optional processTimeout, + AZStd::optional scheduleTimeout, + ProcessLaunchCallback& processLaunchCallback, + ProcessExitCallback& processExitCallback); + ~ExecutionState(); + + ProcessSchedulerResult MonitorProcesses(const AZStd::vector& processes); + void TerminateAllProcesses(ExitCondition exitStatus); + private: + ProcessCallbackResult PopAndLaunch(ProcessInFlight& processInFlight); + StdContent ConsumeProcessStdContent(ProcessInFlight& processInFlight); + void AccumulateProcessStdContent(ProcessInFlight& processInFlight); + + size_t m_maxConcurrentProcesses = 0; + ProcessLaunchCallback m_processLaunchCallback; + ProcessExitCallback m_processExitCallback; + AZStd::optional m_processTimeout; + AZStd::optional m_scheduleTimeout; + AZStd::chrono::high_resolution_clock::time_point m_startTime; + AZStd::vector m_processPool; + AZStd::queue m_processQueue; + }; + + ProcessScheduler::ExecutionState::ExecutionState( size_t maxConcurrentProcesses, AZStd::optional processTimeout, - AZStd::optional scheduleTimeout) - : m_processCreateCallback(processLaunchCallback) + AZStd::optional scheduleTimeout, + ProcessLaunchCallback& processLaunchCallback, + ProcessExitCallback& processExitCallback) + : m_maxConcurrentProcesses(maxConcurrentProcesses) + , m_processLaunchCallback(processLaunchCallback) , m_processExitCallback(processExitCallback) , m_processTimeout(processTimeout) , m_scheduleTimeout(scheduleTimeout) - , m_startTime(AZStd::chrono::high_resolution_clock::now()) { - AZ_TestImpact_Eval(maxConcurrentProcesses != 0, ProcessException, "Max Number of concurrent processes in flight cannot be 0"); - AZ_TestImpact_Eval(!processes.empty(), ProcessException, "Number of processes to launch cannot be 0"); AZ_TestImpact_Eval( !m_processTimeout.has_value() || m_processTimeout->count() > 0, ProcessException, "Process timeout must be empty or non-zero value"); AZ_TestImpact_Eval( !m_scheduleTimeout.has_value() || m_scheduleTimeout->count() > 0, ProcessException, "Scheduler timeout must be empty or non-zero value"); + } - const size_t numConcurrentProcesses = AZStd::min(processes.size(), maxConcurrentProcesses); + ProcessScheduler::ExecutionState::~ExecutionState() + { + TerminateAllProcesses(ExitCondition::Terminated); + } + + ProcessSchedulerResult ProcessScheduler::ExecutionState::MonitorProcesses(const AZStd::vector& processes) + { + AZ_TestImpact_Eval(!processes.empty(), ProcessException, "Number of processes to launch cannot be 0"); + m_startTime = AZStd::chrono::high_resolution_clock::now(); + const size_t numConcurrentProcesses = AZStd::min(processes.size(), m_maxConcurrentProcesses); m_processPool.resize(numConcurrentProcesses); for (const auto& process : processes) @@ -61,20 +96,10 @@ namespace TestImpact if (PopAndLaunch(process) == ProcessCallbackResult::Abort) { TerminateAllProcesses(ExitCondition::Terminated); - return; + return ProcessSchedulerResult::Graceful; } } - MonitorProcesses(); - } - - ProcessScheduler::~ProcessScheduler() - { - TerminateAllProcesses(ExitCondition::Terminated); - } - - void ProcessScheduler::MonitorProcesses() - { while (true) { // Check to see whether or not the scheduling has exceeded its specified runtime @@ -86,7 +111,7 @@ namespace TestImpact { // Runtime exceeded, terminate all proccesses and schedule no further TerminateAllProcesses(ExitCondition::Timeout); - return; + return ProcessSchedulerResult::Timeout; } } @@ -98,7 +123,7 @@ namespace TestImpact { if (processInFlight.m_process) { - // Process is alive (note: not necessarilly currently running) + // Process is alive (note: not necessarily currently running) AccumulateProcessStdContent(processInFlight); const ProcessId processId = processInFlight.m_process->GetProcessInfo().GetId(); @@ -119,7 +144,7 @@ namespace TestImpact { // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); - return; + return ProcessSchedulerResult::Graceful; } else if (!m_processQueue.empty()) { @@ -128,7 +153,7 @@ namespace TestImpact { // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); - return; + return ProcessSchedulerResult::Graceful; } else { @@ -157,9 +182,9 @@ namespace TestImpact ConsumeProcessStdContent(processInFlight), exitTime)) { - // Flight time exceeded, terminate this process + // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); - return; + return ProcessSchedulerResult::Graceful; } } @@ -176,7 +201,7 @@ namespace TestImpact { // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); - return; + return ProcessSchedulerResult::Graceful; } else { @@ -192,9 +217,11 @@ namespace TestImpact break; } } + + return ProcessSchedulerResult::Graceful; } - ProcessCallbackResult ProcessScheduler::PopAndLaunch(ProcessInFlight& processInFlight) + ProcessCallbackResult ProcessScheduler::ExecutionState::PopAndLaunch(ProcessInFlight& processInFlight) { auto processInfo = m_processQueue.front(); m_processQueue.pop(); @@ -212,17 +239,17 @@ namespace TestImpact createResult = LaunchResult::Failure; } - return m_processCreateCallback(processInfo.GetId(), createResult, createTime); + return m_processLaunchCallback(processInfo.GetId(), createResult, createTime); } - void ProcessScheduler::AccumulateProcessStdContent(ProcessInFlight& processInFlight) + void ProcessScheduler::ExecutionState::AccumulateProcessStdContent(ProcessInFlight& processInFlight) { // Accumulate the stdout/stderr so we don't deadlock with the process waiting for the pipe to empty before finishing processInFlight.m_stdOutput += processInFlight.m_process->ConsumeStdOut().value_or(""); processInFlight.m_stdError += processInFlight.m_process->ConsumeStdErr().value_or(""); } - StdContent ProcessScheduler::ConsumeProcessStdContent(ProcessInFlight& processInFlight) + StdContent ProcessScheduler::ExecutionState::ConsumeProcessStdContent(ProcessInFlight& processInFlight) { return { @@ -235,7 +262,7 @@ namespace TestImpact }; } - void ProcessScheduler::TerminateAllProcesses(ExitCondition exitStatus) + void ProcessScheduler::ExecutionState::TerminateAllProcesses(ExitCondition exitStatus) { bool isCallingBackToClient = true; const ReturnCode returnCode = static_cast(exitStatus); @@ -267,4 +294,27 @@ namespace TestImpact } } } + + ProcessScheduler::ProcessScheduler(size_t maxConcurrentProcesses) + : m_maxConcurrentProcesses(maxConcurrentProcesses) + { + AZ_TestImpact_Eval(maxConcurrentProcesses != 0, ProcessException, "Max Number of concurrent processes in flight cannot be 0"); + } + + ProcessScheduler::~ProcessScheduler() = default; + + ProcessSchedulerResult ProcessScheduler::Execute( + const AZStd::vector& processes, + AZStd::optional processTimeout, + AZStd::optional scheduleTimeout, + ProcessLaunchCallback processLaunchCallback, + ProcessExitCallback processExitCallback) + { + AZ_TestImpact_Eval(!m_executionState, ProcessException, "Couldn't execute schedule, schedule already in progress"); + m_executionState = AZStd::make_unique( + m_maxConcurrentProcesses, processTimeout, scheduleTimeout, processLaunchCallback, processExitCallback); + const auto result = m_executionState->MonitorProcesses(processes); + m_executionState.reset(); + return result; + } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h index 27171d25ee..7f562fb99e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h @@ -22,6 +22,7 @@ #include #include #include +#include namespace TestImpact { @@ -49,6 +50,13 @@ namespace TestImpact Abort //!< Abort scheduling immediately. }; + //! Result of the process scheduling sequence. + enum class ProcessSchedulerResult : bool + { + Graceful, //!< The scheduler completed its run without incident or was terminated gracefully in response to a client callback result. + Timeout //!< The scheduler aborted its run prematurely due to its runtime exceeding the scheduler timeout value. + }; + //! Callback for process launch attempt. //! @param processId The id of the process that attempted to launch. //! @param launchResult The result of the process launch attempt. @@ -79,38 +87,28 @@ namespace TestImpact { public: //! Constructs the scheduler with the specified batch of processes. - //! @param processes The batch of processes to schedule. - //! @param processLaunchCallback The process launch callback function. - //! @param processExitCallback The process exit callback function. //! @param maxConcurrentProcesses The maximum number of concurrent processes in-flight. + ProcessScheduler(size_t maxConcurrentProcesses); + ~ProcessScheduler(); + + //! Executes the specified processes and calls the client callbacks (if any) as each process progresses in its life cycle. + //! @note Multiple subsequent calls to Execute are permitted. + //! @param processes The batch of processes to schedule. //! @param processTimeout The maximum duration a process may be in-flight for before being forcefully terminated. //! @param scheduleTimeout The maximum duration the scheduler may run before forcefully terminating all in-flight processes. - //! processes and abandoning any queued processes. - ProcessScheduler( + //! @param processLaunchCallback The process launch callback function. + //! @param processExitCallback The process exit callback function. + //! @returns The state that triggered the end of the schedule sequence. + ProcessSchedulerResult Execute( const AZStd::vector& processes, - const ProcessLaunchCallback& processLaunchCallback, - const ProcessExitCallback& processExitCallback, - size_t maxConcurrentProcesses, AZStd::optional processTimeout, - AZStd::optional scheduleTimeout); - - ~ProcessScheduler(); + AZStd::optional scheduleTimeout, + ProcessLaunchCallback processLaunchCallback, + ProcessExitCallback processExitCallback); private: - struct ProcessInFlight; - - void MonitorProcesses(); - ProcessCallbackResult PopAndLaunch(ProcessInFlight& processInFlight); - void TerminateAllProcesses(ExitCondition exitStatus); - StdContent ConsumeProcessStdContent(ProcessInFlight& processInFlight); - void AccumulateProcessStdContent(ProcessInFlight& processInFlight); - - const ProcessLaunchCallback m_processCreateCallback; - const ProcessExitCallback m_processExitCallback; - const AZStd::optional m_processTimeout; - const AZStd::optional m_scheduleTimeout; - const AZStd::chrono::high_resolution_clock::time_point m_startTime; - AZStd::vector m_processPool; - AZStd::queue m_processQueue; + class ExecutionState; + AZStd::unique_ptr m_executionState; + size_t m_maxConcurrentProcesses = 0; }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp index 882c65c516..09cea87d41 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp @@ -106,19 +106,18 @@ namespace TestImpact return m_cache; } - TestEnumerator::TestEnumerator( - AZStd::optional clientCallback, - size_t maxConcurrentEnumerations, - AZStd::optional enumerationTimeout, - AZStd::optional enumeratorTimeout) - : JobRunner(clientCallback, AZStd::nullopt, StdOutputRouting::None, StdErrorRouting::None, maxConcurrentEnumerations, enumerationTimeout, enumeratorTimeout) + TestEnumerator::TestEnumerator(size_t maxConcurrentEnumerations) + : JobRunner(maxConcurrentEnumerations) { } - - AZStd::vector TestEnumerator::Enumerate( + + AZStd::pair> TestEnumerator::Enumerate( const AZStd::vector& jobInfos, CacheExceptionPolicy cacheExceptionPolicy, - JobExceptionPolicy jobExceptionPolicy) + JobExceptionPolicy jobExceptionPolicy, + AZStd::optional enumerationTimeout, + AZStd::optional enumeratorTimeout, + AZStd::optional clientCallback) { AZStd::vector cachedJobs; AZStd::vector jobQueue; @@ -179,14 +178,23 @@ namespace TestImpact }; // Generate the enumeration results for the jobs that weren't cached - auto jobs = ExecuteJobs(jobQueue, payloadGenerator, jobExceptionPolicy); - + auto [result, jobs] = ExecuteJobs( + jobQueue, + jobExceptionPolicy, + payloadGenerator, + StdOutputRouting::None, + StdErrorRouting::None, + enumerationTimeout, + enumeratorTimeout, + clientCallback, + AZStd::nullopt); + // We need to add the cached jobs to the completed job list even though they technically weren't executed for (auto&& job : cachedJobs) { jobs.emplace_back(AZStd::move(job)); } - return jobs; + return { result, jobs }; } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h index 63d0b82d3e..287dd9e2d9 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h @@ -73,22 +73,23 @@ namespace TestImpact using CacheExceptionPolicy = Bitwise::CacheExceptionPolicy; //! Constructs a test enumerator with the specified parameters common to all enumeration job runs of this enumerator. - //! @param clientCallback The optional client callback to be called whenever an enumeration job changes state. //! @param maxConcurrentEnumerations The maximum number of enumerations to be in flight at any given time. - //! @param enumerationTimeout The maximum duration an enumeration may be in-flight for before being forcefully terminated. - //! @param enumeratorTimeout The maximum duration the enumerator may run before forcefully terminating all in-flight enumerations. - TestEnumerator( - AZStd::optional clientCallback, - size_t maxConcurrentEnumerations, - AZStd::optional enumerationTimeout, - AZStd::optional enumeratorTimeout); + TestEnumerator(size_t maxConcurrentEnumerations); //! Executes the specified test enumeration jobs according to the specified cache and job exception policies. //! @param jobInfos The enumeration jobs to execute. //! @param cacheExceptionPolicy The cache exception policy to be used for this run. //! @param jobExceptionPolicy The enumeration job exception policy to be used for this run. - //! @return the test enumeration jobs with their associated test enumeration payloads. - AZStd::vector Enumerate( - const AZStd::vector& jobInfos, CacheExceptionPolicy cacheExceptionPolicy, JobExceptionPolicy jobExceptionPolicy); + //! @param enumerationTimeout The maximum duration an enumeration may be in-flight for before being forcefully terminated. + //! @param enumeratorTimeout The maximum duration the enumerator may run before forcefully terminating all in-flight enumerations. + //! @param clientCallback The optional client callback to be called whenever an enumeration job changes state. + //! @return The result of the run sequence and the enumeration jobs with their associated test enumeration payloads. + AZStd::pair> Enumerate( + const AZStd::vector& jobInfos, + CacheExceptionPolicy cacheExceptionPolicy, + JobExceptionPolicy jobExceptionPolicy, + AZStd::optional enumerationTimeout, + AZStd::optional enumeratorTimeout, + AZStd::optional clientCallback); }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h index c60a93e21d..91f4d4e39c 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h @@ -53,32 +53,31 @@ namespace TestImpact using JobDataMap = JobDataMap; //! Constructs the job runner with the specified parameters common to all job runs of this runner. - //! @param clientCallback The optional callback function provided by the client to be called upon job state change. - //! @param clientCallback The optional callback function provided by the derived job runner to be called upon job state change. - //! @param stdOutRouting The standard output routing from the underlying job processes to the derived runner. - //! @param stdErrorRouting The standard error routing from the underlying job processes to the derived runner. //! @param maxConcurrentJobs The maximum number of jobs to be in flight at any given time. - //! @param jobTimeout The maximum duration a job may be in-flight for before being forcefully terminated (nullopt if no timeout). - //! @param runnerTimeout The maximum duration the runner may run before forcefully terminating all in-flight jobs (nullopt if no - //! timeout). - TestJobRunner( - AZStd::optional clientCallback, - AZStd::optional derivedJobCallback, - StdOutputRouting stdOutRouting, - StdErrorRouting stdErrRouting, - size_t maxConcurrentJobs, - AZStd::optional jobTimeout, - AZStd::optional runnerTimeout); + TestJobRunner(size_t maxConcurrentJobs); protected: //! Runs the specified jobs and returns the completed payloads produced by each job. //! @param jobInfos The batch of jobs to execute. - //! @param payloadMapProducer The client callback for producing the payload map based on the completed job data. //! @param jobExceptionPolicy The job execution policy for this job run. - AZStd::vector ExecuteJobs( + //! @param payloadMapProducer The client callback for producing the payload map based on the completed job data. + //! @param stdOutRouting The standard output routing from the underlying job processes to the derived runner. + //! @param stdErrorRouting The standard error routing from the underlying job processes to the derived runner. + //! @param jobTimeout The maximum duration a job may be in-flight for before being forcefully terminated (nullopt if no timeout). + //! @param runnerTimeout The maximum duration the runner may run before forcefully terminating all in-flight jobs (nullopt if no timeout). + //! @param clientCallback The optional callback function provided by the client to be called upon job state change. + //! @param clientCallback The optional callback function provided by the derived job runner to be called upon job state change. + //! @returns The result of the run sequence and the jobs that the sequence produced. + AZStd::pair> ExecuteJobs( const AZStd::vector& jobInfos, + JobExceptionPolicy jobExceptionPolicy, PayloadMapProducer payloadMapProducer, - JobExceptionPolicy jobExceptionPolicy); + StdOutputRouting stdOutRouting, + StdErrorRouting stdErrRouting, + AZStd::optional jobTimeout, + AZStd::optional runnerTimeout, + AZStd::optional clientCallback, + AZStd::optional derivedJobCallback); const AZStd::optional m_clientJobCallback; @@ -88,28 +87,25 @@ namespace TestImpact }; template - TestJobRunner::TestJobRunner( - AZStd::optional clientCallback, - AZStd::optional derivedJobCallback, - StdOutputRouting stdOutRouting, - StdErrorRouting stdErrRouting, - size_t maxConcurrentJobs, - AZStd::optional jobTimeout, - AZStd::optional runnerTimeout) - : m_jobRunner(stdOutRouting, stdErrRouting, maxConcurrentJobs, jobTimeout, runnerTimeout) - , m_clientJobCallback(clientCallback) - , m_derivedJobCallback(derivedJobCallback) + TestJobRunner::TestJobRunner(size_t maxConcurrentJobs) + : m_jobRunner(maxConcurrentJobs) { } template - AZStd::vector::Job> TestJobRunner::ExecuteJobs( + AZStd::pair::Job>> TestJobRunner::ExecuteJobs( const AZStd::vector& jobInfos, + JobExceptionPolicy jobExceptionPolicy, PayloadMapProducer payloadMapProducer, - JobExceptionPolicy jobExceptionPolicy) + StdOutputRouting stdOutRouting, + StdErrorRouting stdErrRouting, + AZStd::optional jobTimeout, + AZStd::optional runnerTimeout, + AZStd::optional clientCallback, + AZStd::optional derivedJobCallback) { // Callback to handle job exception policies and client/derived callbacks - const auto jobCallback = [this, &jobExceptionPolicy](const JobInfo& jobInfo, const JobMeta& meta, StdContent&& std) + const auto jobCallback = [&clientCallback, &derivedJobCallback, &jobExceptionPolicy](const JobInfo& jobInfo, const JobMeta& meta, StdContent&& std) { auto callbackResult = ProcessCallbackResult::Continue; if (meta.m_result == JobResult::FailedToExecute && IsFlagSet(jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)) @@ -121,23 +117,23 @@ namespace TestImpact callbackResult = ProcessCallbackResult::Abort; } - if (m_derivedJobCallback.has_value()) + if (derivedJobCallback.has_value()) { - if (const auto result = (*m_derivedJobCallback)(jobInfo, meta, AZStd::move(std)); + if (const auto result = (*derivedJobCallback)(jobInfo, meta, AZStd::move(std)); result == ProcessCallbackResult::Abort) { callbackResult = ProcessCallbackResult::Abort; } } - if (m_clientJobCallback.has_value()) + if (clientCallback.has_value()) { - (*m_clientJobCallback)(jobInfo, meta); + (*clientCallback)(jobInfo, meta); } return callbackResult; }; - return m_jobRunner.Execute(jobInfos, jobCallback, payloadMapProducer); + return m_jobRunner.Execute(jobInfos, payloadMapProducer, stdOutRouting, stdErrRouting, jobTimeout, runnerTimeout, jobCallback); } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp index 0d27d56af1..c0373e89f5 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp @@ -52,19 +52,18 @@ namespace TestImpact return {AZStd::move(run), AZStd::move(coverage)}; } - InstrumentedTestRunner::InstrumentedTestRunner( - AZStd::optional clientCallback, - size_t maxConcurrentRuns, - AZStd::optional runTimeout, - AZStd::optional runnerTimeout) - : JobRunner(clientCallback, AZStd::nullopt, StdOutputRouting::None, StdErrorRouting::None, maxConcurrentRuns, runTimeout, runnerTimeout) + InstrumentedTestRunner::InstrumentedTestRunner(size_t maxConcurrentRuns) + : JobRunner(maxConcurrentRuns) { } - AZStd::vector InstrumentedTestRunner::RunInstrumentedTests( + AZStd::pair> InstrumentedTestRunner::RunInstrumentedTests( const AZStd::vector& jobInfos, CoverageExceptionPolicy coverageExceptionPolicy, - JobExceptionPolicy jobExceptionPolicy) + JobExceptionPolicy jobExceptionPolicy, + AZStd::optional runTimeout, + AZStd::optional runnerTimeout, + AZStd::optional clientCallback) { const auto payloadGenerator = [this, coverageExceptionPolicy](const JobDataMap& jobDataMap) { @@ -98,6 +97,15 @@ namespace TestImpact return runs; }; - return ExecuteJobs(jobInfos, payloadGenerator, jobExceptionPolicy); + return ExecuteJobs( + jobInfos, + jobExceptionPolicy, + payloadGenerator, + StdOutputRouting::None, + StdErrorRouting::None, + runTimeout, + runnerTimeout, + clientCallback, + AZStd::nullopt); } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h index 3b47f53128..5545843e35 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h @@ -53,21 +53,24 @@ namespace TestImpact using CoverageExceptionPolicy = Bitwise::CoverageExceptionPolicy; //! Constructs an instrumented test runner with the specified parameters common to all job runs of this runner. - //! @param clientCallback The optional client callback to be called whenever a run job changes state. - //! @param maxConcurrentRuns The maximum number of runs to be in flight at any given time. - //! @param runTimeout The maximum duration a run may be in-flight for before being forcefully terminated. - //! @param runnerTimeout The maximum duration the runner may run before forcefully terminating all in-flight runs. - InstrumentedTestRunner( - AZStd::optional clientCallback, size_t maxConcurrentRuns, - AZStd::optional runTimeout, AZStd::optional runnerTimeout); - + //! @param maxConcurrentRuns The maximum number of runs to be in flight at any given time. + InstrumentedTestRunner(size_t maxConcurrentRuns); + //! Executes the specified instrumented test run jobs according to the specified job exception policies. //! @param jobInfos The test run jobs to execute. //! @param CoverageExceptionPolicy The coverage exception policy to be used for this run. //! @param jobExceptionPolicy The test run job exception policy to be used for this run (use //! TestJobExceptionPolicy::OnFailedToExecute to throw on test failures). - //! @return the instrumented test run jobs with their associated test run and test coverage payloads. - AZStd::vector RunInstrumentedTests( - const AZStd::vector& jobInfos, CoverageExceptionPolicy coverageExceptionPolicy, JobExceptionPolicy jobExceptionPolicy); + //! @param runTimeout The maximum duration a run may be in-flight for before being forcefully terminated. + //! @param runnerTimeout The maximum duration the runner may run before forcefully terminating all in-flight runs. + //! @param clientCallback The optional client callback to be called whenever a run job changes state. + //! @return The result of the run sequence and the instrumented run jobs with their associated test run and coverage payloads. + AZStd::pair> RunInstrumentedTests( + const AZStd::vector& jobInfos, + CoverageExceptionPolicy coverageExceptionPolicy, + JobExceptionPolicy jobExceptionPolicy, + AZStd::optional runTimeout, + AZStd::optional runnerTimeout, + AZStd::optional clientCallback); }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.cpp index 453b1fa962..4e82857cb2 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.cpp @@ -103,7 +103,7 @@ namespace TestImpact return m_modules.size(); } - const AZStd::vector& TestCoverage::GetSourcesCovered() const + const AZStd::vector& TestCoverage::GetSourcesCovered() const { return m_sourcesCovered; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.h index e7599fb145..8de5fbb6c7 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestCoverage.h @@ -44,7 +44,7 @@ namespace TestImpact size_t GetNumModulesCovered() const; //! Returns the sorted set of unique sources covered (empty if no coverage). - const AZStd::vector& GetSourcesCovered() const; + const AZStd::vector& GetSourcesCovered() const; //! Returns the modules covered (empty if no coverage). const AZStd::vector& GetModuleCoverages() const; @@ -56,7 +56,7 @@ namespace TestImpact void CalculateTestMetrics(); AZStd::vector m_modules; - AZStd::vector m_sourcesCovered; + AZStd::vector m_sourcesCovered; AZStd::optional m_coverageLevel; }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp index ebb846f406..27780a5f95 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp @@ -26,18 +26,17 @@ namespace TestImpact return TestRun(GTest::TestRunSuitesFactory(ReadFileContents(runFile)), duration); } - TestRunner::TestRunner( - AZStd::optional clientCallback, - size_t maxConcurrentRuns, - AZStd::optional runTimeout, - AZStd::optional runnerTimeout) - : JobRunner(clientCallback, AZStd::nullopt, StdOutputRouting::None, StdErrorRouting::None, maxConcurrentRuns, runTimeout, runnerTimeout) + TestRunner::TestRunner(size_t maxConcurrentRuns) + : JobRunner(maxConcurrentRuns) { } - AZStd::vector TestRunner::RunTests( + AZStd::pair> TestRunner::RunTests( const AZStd::vector& jobInfos, - JobExceptionPolicy jobExceptionPolicy) + JobExceptionPolicy jobExceptionPolicy, + AZStd::optional runTimeout, + AZStd::optional runnerTimeout, + AZStd::optional clientCallback) { const auto payloadGenerator = [this](const JobDataMap& jobDataMap) { @@ -62,6 +61,15 @@ namespace TestImpact return runs; }; - return ExecuteJobs(jobInfos, payloadGenerator, jobExceptionPolicy); + return ExecuteJobs( + jobInfos, + jobExceptionPolicy, + payloadGenerator, + StdOutputRouting::None, + StdErrorRouting::None, + runTimeout, + runnerTimeout, + clientCallback, + AZStd::nullopt); } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h index 379c6b424e..597ce2d2f8 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h @@ -26,21 +26,22 @@ namespace TestImpact public: //! Constructs a test runner with the specified parameters common to all job runs of this runner. - //! @param clientCallback The optional client callback to be called whenever a run job changes state. //! @param maxConcurrentRuns The maximum number of runs to be in flight at any given time. - //! @param runTimeout The maximum duration a run may be in-flight for before being forcefully terminated. - //! @param runnerTimeout The maximum duration the runner may run before forcefully terminating all in-flight runs. - TestRunner( - AZStd::optional clientCallback, - size_t maxConcurrentRuns, - AZStd::optional runTimeout, - AZStd::optional runnerTimeout); + TestRunner(size_t maxConcurrentRuns); //! Executes the specified test run jobs according to the specified job exception policies. //! @param jobInfos The test run jobs to execute. //! @param jobExceptionPolicy The test run job exception policy to be used for this run (use //! TestJobExceptionPolicy::OnFailedToExecute to throw on test failures). - //! @return the test run jobs with their associated test run payloads. - AZStd::vector RunTests(const AZStd::vector& jobInfos, JobExceptionPolicy jobExceptionPolicy); + //! @param runTimeout The maximum duration a run may be in-flight for before being forcefully terminated. + //! @param runnerTimeout The maximum duration the runner may run before forcefully terminating all in-flight runs. + //! @param clientCallback The optional client callback to be called whenever a run job changes state. + //! @return The result of the run sequence and the run jobs with their associated test run payloads. + AZStd::pair> RunTests( + const AZStd::vector& jobInfos, + JobExceptionPolicy jobExceptionPolicy, + AZStd::optional runTimeout, + AZStd::optional runnerTimeout, + AZStd::optional clientCallback); }; } // namespace TestImpact From 6a0cf974560d5d617d601cc9c765d7de022be1ea Mon Sep 17 00:00:00 2001 From: igarri Date: Mon, 24 May 2021 13:19:35 +0100 Subject: [PATCH 052/233] Fixed Selection in AssetBrowserTreeView --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 5 +++-- .../AssetBrowser/Views/AssetBrowserTreeView.cpp | 4 +++- Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp | 5 ++--- .../Code/Editor/Model/UnitTestBrowserFilterModel.cpp | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index 7c7f2aa601..fc2fc41656 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -23,8 +23,9 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") #include AZ_POP_DISABLE_WARNING -AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserTableView); - +AZ_CVAR( + bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null, + "Use the new AssetBrowser TableView for searching assets."); namespace AzToolsFramework { namespace AssetBrowser diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp index b93caf85f7..52c785d678 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp @@ -99,8 +99,10 @@ namespace AzToolsFramework AZStd::vector AssetBrowserTreeView::GetSelectedAssets() const { + + const QModelIndexList& selectedIndexes = selectionModel()->selectedRows(); QModelIndexList sourceIndexes; - for (const auto& index : selectedIndexes()) + for (const auto& index : selectedIndexes) { //If we check for more than one column then the model will try to select the same entry several times. if (index.column() == 0) diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 1b123dd000..e722d6d03e 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -33,9 +33,8 @@ AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING #include AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING -AZ_CVAR( - bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null, - "Use the new AssetBrowser TableView for searching assets."); +AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserTableView); + class ListenerForShowAssetEditorEvent : public QObject diff --git a/Gems/ScriptCanvas/Code/Editor/Model/UnitTestBrowserFilterModel.cpp b/Gems/ScriptCanvas/Code/Editor/Model/UnitTestBrowserFilterModel.cpp index 665c81fa57..1a4d95c585 100644 --- a/Gems/ScriptCanvas/Code/Editor/Model/UnitTestBrowserFilterModel.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Model/UnitTestBrowserFilterModel.cpp @@ -34,7 +34,7 @@ namespace ScriptCanvasEditor { setDynamicSortFilter(true); - m_showColumn.insert(AssetBrowserModel::m_column); + m_showColumn.insert(aznumeric_cast(AssetBrowserEntry::Column::DisplayName)); UnitTestWidgetNotificationBus::Handler::BusConnect(); From 787b44db49396487ef295b83d4664d50bcd11f96 Mon Sep 17 00:00:00 2001 From: jonawals Date: Mon, 24 May 2021 14:15:32 +0100 Subject: [PATCH 053/233] Address PR comments --- .../Process/JobRunner/TestImpactProcessJobRunner.h | 2 +- .../Process/Scheduler/TestImpactProcessScheduler.cpp | 11 ++++++----- .../Process/Scheduler/TestImpactProcessScheduler.h | 5 +++-- .../TestEngine/Enumeration/TestImpactTestEnumerator.h | 2 +- .../TestEngine/JobRunner/TestImpactTestJobRunner.h | 2 +- .../TestEngine/Run/TestImpactInstrumentedTestRunner.h | 2 +- .../Code/Source/TestEngine/Run/TestImpactTestRunner.h | 2 +- 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h index 384111b823..7d726cecc1 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h @@ -51,7 +51,7 @@ namespace TestImpact public: //! Constructs the job runner with the specified parameters to constrain job runs. //! @param maxConcurrentProcesses he maximum number of concurrent jobs in-flight. - JobRunner(size_t maxConcurrentProcesses); + explicit JobRunner(size_t maxConcurrentProcesses); //! Executes the specified jobs and returns the products of their labor. //! @param jobs The arguments (and other pertinent information) required for each job to be run. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp index 8d6c5c6739..065caf54bd 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp @@ -95,8 +95,9 @@ namespace TestImpact { if (PopAndLaunch(process) == ProcessCallbackResult::Abort) { + // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); - return ProcessSchedulerResult::Graceful; + return ProcessSchedulerResult::UserAborted; } } @@ -144,7 +145,7 @@ namespace TestImpact { // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); - return ProcessSchedulerResult::Graceful; + return ProcessSchedulerResult::UserAborted; } else if (!m_processQueue.empty()) { @@ -153,7 +154,7 @@ namespace TestImpact { // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); - return ProcessSchedulerResult::Graceful; + return ProcessSchedulerResult::UserAborted; } else { @@ -184,7 +185,7 @@ namespace TestImpact { // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); - return ProcessSchedulerResult::Graceful; + return ProcessSchedulerResult::UserAborted; } } @@ -201,7 +202,7 @@ namespace TestImpact { // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); - return ProcessSchedulerResult::Graceful; + return ProcessSchedulerResult::UserAborted; } else { diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h index 7f562fb99e..769bf59ff8 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h @@ -51,9 +51,10 @@ namespace TestImpact }; //! Result of the process scheduling sequence. - enum class ProcessSchedulerResult : bool + enum class ProcessSchedulerResult : AZ::u8 { Graceful, //!< The scheduler completed its run without incident or was terminated gracefully in response to a client callback result. + UserAborted, //!< The scheduler aborted prematurely due to the user returning an abort value from thier callback handler. Timeout //!< The scheduler aborted its run prematurely due to its runtime exceeding the scheduler timeout value. }; @@ -88,7 +89,7 @@ namespace TestImpact public: //! Constructs the scheduler with the specified batch of processes. //! @param maxConcurrentProcesses The maximum number of concurrent processes in-flight. - ProcessScheduler(size_t maxConcurrentProcesses); + explicit ProcessScheduler(size_t maxConcurrentProcesses); ~ProcessScheduler(); //! Executes the specified processes and calls the client callbacks (if any) as each process progresses in its life cycle. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h index 287dd9e2d9..fedaf300f7 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h @@ -74,7 +74,7 @@ namespace TestImpact //! Constructs a test enumerator with the specified parameters common to all enumeration job runs of this enumerator. //! @param maxConcurrentEnumerations The maximum number of enumerations to be in flight at any given time. - TestEnumerator(size_t maxConcurrentEnumerations); + explicit TestEnumerator(size_t maxConcurrentEnumerations); //! Executes the specified test enumeration jobs according to the specified cache and job exception policies. //! @param jobInfos The enumeration jobs to execute. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h index 91f4d4e39c..3078114ef4 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h @@ -54,7 +54,7 @@ namespace TestImpact //! Constructs the job runner with the specified parameters common to all job runs of this runner. //! @param maxConcurrentJobs The maximum number of jobs to be in flight at any given time. - TestJobRunner(size_t maxConcurrentJobs); + explicit TestJobRunner(size_t maxConcurrentJobs); protected: //! Runs the specified jobs and returns the completed payloads produced by each job. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h index 5545843e35..42b718b241 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h @@ -54,7 +54,7 @@ namespace TestImpact //! Constructs an instrumented test runner with the specified parameters common to all job runs of this runner. //! @param maxConcurrentRuns The maximum number of runs to be in flight at any given time. - InstrumentedTestRunner(size_t maxConcurrentRuns); + explicit InstrumentedTestRunner(size_t maxConcurrentRuns); //! Executes the specified instrumented test run jobs according to the specified job exception policies. //! @param jobInfos The test run jobs to execute. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h index 597ce2d2f8..1fdbc16d58 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h @@ -27,7 +27,7 @@ namespace TestImpact public: //! Constructs a test runner with the specified parameters common to all job runs of this runner. //! @param maxConcurrentRuns The maximum number of runs to be in flight at any given time. - TestRunner(size_t maxConcurrentRuns); + explicit TestRunner(size_t maxConcurrentRuns); //! Executes the specified test run jobs according to the specified job exception policies. //! @param jobInfos The test run jobs to execute. From bdbbedda71c9e60c5c3cc200b9c130fd249811fc Mon Sep 17 00:00:00 2001 From: jonawals Date: Mon, 24 May 2021 15:00:21 +0100 Subject: [PATCH 054/233] Add test engine --- .../TestImpactTestEnumerationSerializer.cpp | 4 +- .../Enumeration/TestImpactTestEnumerator.cpp | 69 ++-- .../Enumeration/TestImpactTestEnumerator.h | 4 +- .../Run/TestImpactInstrumentedTestRunner.cpp | 29 +- .../Run/TestImpactInstrumentedTestRunner.h | 14 - .../Run/TestImpactTestRunSerializer.cpp | 4 +- .../TestEngine/Run/TestImpactTestRunner.cpp | 11 +- .../TestEngine/TestImpactTestEngine.cpp | 382 ++++++++++++++++++ .../Source/TestEngine/TestImpactTestEngine.h | 126 ++++++ .../TestImpactTestEngineEnumeration.cpp | 27 ++ .../TestImpactTestEngineEnumeration.h | 32 ++ ...tion.h => TestImpactTestEngineException.h} | 4 +- .../TestImpactTestEngineInstrumentedRun.cpp | 52 +++ .../TestImpactTestEngineInstrumentedRun.h | 32 ++ .../TestEngine/TestImpactTestEngineJob.cpp | 40 ++ .../TestEngine/TestImpactTestEngineJob.h | 41 ++ ...n.h => TestImpactTestEngineRegularRun.cpp} | 19 +- .../TestImpactTestEngineRegularRun.h | 34 ++ 18 files changed, 819 insertions(+), 105 deletions(-) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineEnumeration.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineEnumeration.h rename Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/{Run/TestImpactTestRunException.h => TestImpactTestEngineException.h} (86%) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJob.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJob.h rename Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/{Enumeration/TestImpactTestEnumerationException.h => TestImpactTestEngineRegularRun.cpp} (61%) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineRegularRun.h diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationSerializer.cpp index 05a13a0703..d1e7925cc0 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationSerializer.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationSerializer.cpp @@ -10,7 +10,7 @@ * */ -#include +#include #include #include @@ -82,7 +82,7 @@ namespace TestImpact if (doc.Parse<0>(testEnumString.c_str()).HasParseError()) { - throw TestEnumerationException("Could not parse enumeration data"); + throw TestEngineException("Could not parse enumeration data"); } for (const auto& suite : doc[TestEnumFields::Keys[TestEnumFields::SuitesKey]].GetArray()) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp index 09cea87d41..f7b6d4a264 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include #include @@ -36,7 +36,7 @@ namespace TestImpact AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY)) { AZ_TestImpact_Eval( - !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheWriteFailure), TestEnumerationException, + !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheWriteFailure), TestEngineException, "Couldn't open cache file for writing"); return; } @@ -44,50 +44,16 @@ namespace TestImpact if (cacheFile.Write(cacheBytes.data(), cacheBytes.size()) == 0) { AZ_TestImpact_Eval( - !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheWriteFailure), TestEnumerationException, + !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheWriteFailure), TestEngineException, "Couldn't write cache file data"); return; } } - - AZStd::optional ReadCacheFile(const RepoPath& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy) - { - AZ::IO::SystemFile cacheFile; - AZStd::string cacheJSON; - if (!cacheFile.Open(path.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY)) - { - AZ_TestImpact_Eval( - !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheNotExist), TestEnumerationException, - "Couldn't locate cache file"); - return AZStd::nullopt; - } - - const AZ::IO::SystemFile::SizeType length = cacheFile.Length(); - if (length == 0) - { - AZ_TestImpact_Eval( - !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheReadFailure), TestEnumerationException, - "Cache file is empty"); - return AZStd::nullopt; - } - - cacheFile.Seek(0, AZ::IO::SystemFile::SF_SEEK_BEGIN); - cacheJSON.resize(length); - if (cacheFile.Read(length, cacheJSON.data()) != length) - { - AZ_TestImpact_Eval( - !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheReadFailure), TestEnumerationException, - "Couldn't read cache file"); - return AZStd::nullopt; - } - - return DeserializeTestEnumeration(cacheJSON); - } } // namespace TestEnumeration ParseTestEnumerationFile(const RepoPath& enumerationFile) { - return TestEnumeration(GTest::TestEnumerationSuitesFactory(ReadFileContents(enumerationFile))); + return TestEnumeration(GTest::TestEnumerationSuitesFactory(ReadFileContents(enumerationFile))); } TestEnumerationJobData::TestEnumerationJobData(const RepoPath& enumerationArtifact, AZStd::optional&& cache) @@ -128,7 +94,16 @@ namespace TestImpact if (jobInfo.GetCache().has_value() && jobInfo.GetCache()->m_policy == JobData::CachePolicy::Read) { JobMeta meta; - auto enumeration = ReadCacheFile(jobInfo.GetCache()->m_file, cacheExceptionPolicy); + AZStd::optional enumeration; + + try + { + enumeration = TestEnumeration(DeserializeTestEnumeration(ReadFileContents(jobInfo.GetCache()->m_file))); + } + catch (const TestEngineException& e) + { + AZ_Printf("Enumerate", "Enumeration cache error: %s", e.what()); + } // Even though cached jobs don't get executed we still give the client the opportunity to handle the job state // change in order to make the caching process transparent to the client @@ -164,12 +139,20 @@ namespace TestImpact const auto& [meta, jobInfo] = jobData; if (meta.m_result == JobResult::ExecutedWithSuccess) { - const auto& enumeration = (enumerations[jobId] = ParseTestEnumerationFile(jobInfo->GetEnumerationArtifactPath())); + try + { + const auto& enumeration = (enumerations[jobId] = ParseTestEnumerationFile(jobInfo->GetEnumerationArtifactPath())); - // Write out the enumeration to a cache file if we have a cache write policy for this job - if (jobInfo->GetCache().has_value() && jobInfo->GetCache()->m_policy == JobData::CachePolicy::Write) + // Write out the enumeration to a cache file if we have a cache write policy for this job + if (jobInfo->GetCache().has_value() && jobInfo->GetCache()->m_policy == JobData::CachePolicy::Write) + { + WriteCacheFile(enumeration.value(), jobInfo->GetCache()->m_file, cacheExceptionPolicy); + } + } + catch (const Exception& e) { - WriteCacheFile(enumeration.value(), jobInfo->GetCache()->m_file, cacheExceptionPolicy); + AZ_Warning("Enumerate", false, e.what()); + enumerations[jobId] = AZStd::nullopt; } } } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h index fedaf300f7..c802b92529 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h @@ -57,9 +57,7 @@ namespace TestImpact enum class CacheExceptionPolicy { Never = 0, //! Never throw. - OnCacheNotExist = 1, //! Throw when a cache read policy was in place but a cache file for this job doesn't exist. - OnCacheReadFailure = 1 << 1, //! Throw when a cache read policy is in place but the cache file could not be read. - OnCacheWriteFailure = 1 << 2 //! Throw when a cache write policy is in place but the cache file could not be written. + OnCacheWriteFailure = 1 //! Throw when a cache write policy is in place but the cache file could not be written. }; } // namespace Bitwise diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp index c0373e89f5..3b109c3a77 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp @@ -14,8 +14,8 @@ #include #include +#include #include -#include #include #include @@ -36,18 +36,10 @@ namespace TestImpact InstrumentedTestRunner::JobPayload ParseTestRunAndCoverageFiles( const RepoPath& runFile, const RepoPath& coverageFile, - AZStd::chrono::milliseconds duration, - InstrumentedTestRunner::CoverageExceptionPolicy coverageExceptionPolicy) + AZStd::chrono::milliseconds duration) { - TestRun run(GTest::TestRunSuitesFactory(ReadFileContents(runFile)), duration); - AZStd::vector moduleCoverages = Cobertura::ModuleCoveragesFactory(ReadFileContents(coverageFile)); - if (moduleCoverages.empty()) - { - AZ_TestImpact_Eval( - !IsFlagSet(coverageExceptionPolicy, Bitwise::CoverageExceptionPolicy::OnEmptyCoverage), TestRunException, - AZStd::string::format("No coverage data generated for '%s'", coverageFile.c_str())); - } - + TestRun run(GTest::TestRunSuitesFactory(ReadFileContents(runFile)), duration); + AZStd::vector moduleCoverages = Cobertura::ModuleCoveragesFactory(ReadFileContents(coverageFile)); TestCoverage coverage(AZStd::move(moduleCoverages)); return {AZStd::move(run), AZStd::move(coverage)}; } @@ -59,13 +51,12 @@ namespace TestImpact AZStd::pair> InstrumentedTestRunner::RunInstrumentedTests( const AZStd::vector& jobInfos, - CoverageExceptionPolicy coverageExceptionPolicy, JobExceptionPolicy jobExceptionPolicy, AZStd::optional runTimeout, AZStd::optional runnerTimeout, AZStd::optional clientCallback) { - const auto payloadGenerator = [this, coverageExceptionPolicy](const JobDataMap& jobDataMap) + const auto payloadGenerator = [this](const JobDataMap& jobDataMap) { PayloadMap runs; for (const auto& [jobId, jobData] : jobDataMap) @@ -78,18 +69,12 @@ namespace TestImpact runs[jobId] = ParseTestRunAndCoverageFiles( jobInfo->GetRunArtifactPath(), jobInfo->GetCoverageArtifactPath(), - meta.m_duration.value(), - coverageExceptionPolicy); + meta.m_duration.value()); } catch (const Exception& e) { - AZ_Warning("RunInstrumentedTests", false, e.what()); + AZ_Printf("RunInstrumentedTests", e.what()); runs[jobId] = AZStd::nullopt; - - if (coverageExceptionPolicy == CoverageExceptionPolicy::OnEmptyCoverage) - { - break; - } } } } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h index 42b718b241..b2d7f84b6d 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h @@ -33,16 +33,6 @@ namespace TestImpact RepoPath m_coverageArtifact; //!< Path to coverage data. }; - namespace Bitwise - { - //! Exception policy for test coverage artifacts. - enum class CoverageExceptionPolicy - { - Never = 0, //! Never throw. - OnEmptyCoverage = 1 //! Throw when no coverage data was produced. - }; - } // namespace Bitwise - //! Runs a batch of test targets to determine the test coverage and passes/failures. class InstrumentedTestRunner : public TestJobRunner> @@ -50,15 +40,12 @@ namespace TestImpact using JobRunner = TestJobRunner>; public: - using CoverageExceptionPolicy = Bitwise::CoverageExceptionPolicy; - //! Constructs an instrumented test runner with the specified parameters common to all job runs of this runner. //! @param maxConcurrentRuns The maximum number of runs to be in flight at any given time. explicit InstrumentedTestRunner(size_t maxConcurrentRuns); //! Executes the specified instrumented test run jobs according to the specified job exception policies. //! @param jobInfos The test run jobs to execute. - //! @param CoverageExceptionPolicy The coverage exception policy to be used for this run. //! @param jobExceptionPolicy The test run job exception policy to be used for this run (use //! TestJobExceptionPolicy::OnFailedToExecute to throw on test failures). //! @param runTimeout The maximum duration a run may be in-flight for before being forcefully terminated. @@ -67,7 +54,6 @@ namespace TestImpact //! @return The result of the run sequence and the instrumented run jobs with their associated test run and coverage payloads. AZStd::pair> RunInstrumentedTests( const AZStd::vector& jobInfos, - CoverageExceptionPolicy coverageExceptionPolicy, JobExceptionPolicy jobExceptionPolicy, AZStd::optional runTimeout, AZStd::optional runnerTimeout, diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunSerializer.cpp index 7fe8789d22..15d3cc04b1 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunSerializer.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunSerializer.cpp @@ -10,7 +10,7 @@ * */ -#include +#include #include #include @@ -142,7 +142,7 @@ namespace TestImpact if (doc.Parse<0>(testEnumString.c_str()).HasParseError()) { - throw TestRunException("Could not parse enumeration data"); + throw TestEngineException("Could not parse enumeration data"); } // Run duration diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp index 27780a5f95..401084c934 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include @@ -21,11 +21,6 @@ namespace TestImpact { - TestRun ParseTestRunFile(const RepoPath& runFile, AZStd::chrono::milliseconds duration) - { - return TestRun(GTest::TestRunSuitesFactory(ReadFileContents(runFile)), duration); - } - TestRunner::TestRunner(size_t maxConcurrentRuns) : JobRunner(maxConcurrentRuns) { @@ -48,11 +43,11 @@ namespace TestImpact { try { - runs[jobId] = ParseTestRunFile(jobInfo->GetRunArtifactPath(), meta.m_duration.value()); + runs[jobId] = TestRun(GTest::TestRunSuitesFactory(ReadFileContents(jobInfo->GetRunArtifactPath())), meta.m_duration.value()); } catch (const Exception& e) { - AZ_Warning("RunTests", false, e.what()); + AZ_Printf("RunTests", e.what()); runs[jobId] = AZStd::nullopt; } } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp new file mode 100644 index 0000000000..5046b3f8ee --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp @@ -0,0 +1,382 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace TestImpact +{ + // Known error codes for test instrumentation, test runner and unit test library + // This could be refactored into a generic solution agnostic of the tool and library specific details + namespace ErrorCodes + { + namespace OpenCppCoverage + { + static constexpr ReturnCode InvalidArgs = -1618178468; + } + + namespace GTest + { + static constexpr ReturnCode Unsuccessful = 1; + } + + namespace AZTestRunner + { + static constexpr ReturnCode InvalidArgs = 101; + static constexpr ReturnCode FailedToFindTargetBinary = 102; + static constexpr ReturnCode SymbolNotFound = 103; + static constexpr ReturnCode ModuleSkipped = 104; + } + } + + namespace + { + // Calculate the sequence result by analysing the state of the test targets that were run. + template + TestSequenceResult CalculateSequenceResult( + ProcessSchedulerResult result, + const AZStd::vector& engineJobs, + Policy::ExecutionFailure executionFailurePolicy) + { + if (result == ProcessSchedulerResult::Timeout) + { + // Test job runner timing out overrules all other possible sequence results + return TestSequenceResult::Timeout; + } + + bool hasExecutionFailures = false; + bool hasTestFailures = false; + for (const auto& engineJob : engineJobs) + { + switch (engineJob.GetTestResult()) + { + case Client::TestRunResult::FailedToExecute: + { + hasExecutionFailures = true; + break; + } + case Client::TestRunResult::Timeout: + case Client::TestRunResult::TestFailures: + { + hasTestFailures = true; + break; + } + default: + { + continue; + } + } + } + + // Execution failure can be considered test passes if a permissive execution failure policy is used, otherwise they are failures + if ((hasExecutionFailures && executionFailurePolicy != Policy::ExecutionFailure::Ignore) || hasTestFailures) + { + return TestSequenceResult::Failure; + } + else + { + return TestSequenceResult::Success; + } + } + + // Deduces the run result for a given test target based on how the process exited and known return values + Client::TestRunResult GetClientTestRunResultForMeta(const JobMeta& meta) + { + // Attempt to determine why a given test target executed successfully but return with an error code + if (meta.m_returnCode.has_value()) + { + switch (meta.m_returnCode.value()) + { + // We will consider test targets that technically execute but their launcher or unit test library return a know error + // code that pertains to incorrect argument usage as test targets that failed to execute + case ErrorCodes::OpenCppCoverage::InvalidArgs: + case ErrorCodes::AZTestRunner::InvalidArgs: + case ErrorCodes::AZTestRunner::FailedToFindTargetBinary: + case ErrorCodes::AZTestRunner::ModuleSkipped: + case ErrorCodes::AZTestRunner::SymbolNotFound: + return Client::TestRunResult::FailedToExecute; + // The trivial case: the test target has failing tests + case ErrorCodes::GTest::Unsuccessful: + return Client::TestRunResult::TestFailures; + default: + break; + } + } + + switch (meta.m_result) + { + // If the test target executed successfully but returned in an unknown abnormal state it's probably because a test caused + // an unhandled exception, segfault or any other of the weird and wonderful ways a badly behaving test can terminate + case JobResult::ExecutedWithFailure: + return Client::TestRunResult::TestFailures; + // The trivial case: all of the tests in the test target passed + case JobResult::ExecutedWithSuccess: + return Client::TestRunResult::AllTestsPass; + // NotExecuted happens when a test is queued for launch but the test runner terminates the sequence (either due to client abort + // or due to the sequence timer expiring) whereas Terminated happens when the aforementioned scenarios happen when the test target + // is in flight + case JobResult::NotExecuted: + case JobResult::Terminated: + return Client::TestRunResult::NotRun; + // The individual timer for the test target expired + case JobResult::Timeout: + return Client::TestRunResult::Timeout; + default: + throw(TestEngineException(AZStd::string::format("Unexpected job result: %u", static_cast(meta.m_result)))); + } + } + + // Map for storing the test engine job data of completed test target runs + template + using TestEngineJobMap = AZStd::unordered_map; + + // Helper trait for identifying the test engine job specialization for a given test job runner + template + struct TestJobRunnerTrait + {}; + + // Helper function for getting the type directly of the test job runner trait + template + using TestEngineJobType = typename TestJobRunnerTrait::TestEngineJobType; + + // Type trait for the test enumerator + template<> + struct TestJobRunnerTrait + { + using TestEngineJobType = TestEngineEnumeration; + }; + + // Type trait for the test runner + template<> + struct TestJobRunnerTrait + { + using TestEngineJobType = TestEngineRegularRun; + }; + + // Type trait for the instrumented test runner + template<> + struct TestJobRunnerTrait + { + using TestEngineJobType = TestEngineInstrumentedRun; + }; + + // Functor for handling test job runner callbacks + template + class TestJobRunnerCallbackHandler + { + using IdType = typename TestJobRunner::JobInfo::IdType; + using JobInfo = typename TestJobRunner::JobInfo; + public: + TestJobRunnerCallbackHandler( + const AZStd::vector& testTargets, + TestEngineJobMap* engineJobs, + AZStd::optional* callback) + : m_testTargets(testTargets) + , m_engineJobs(engineJobs) + , m_callback(callback) + { + } + + void operator()(const typename JobInfo& jobInfo, const TestImpact::JobMeta& meta) + { + const auto id = jobInfo.GetId().m_value; + const auto& args = jobInfo.GetCommand().m_args; + const auto* target = m_testTargets[id]; + const auto result = GetClientTestRunResultForMeta(meta); + + // Place the test engine job associated with this test run into the map along with its client test run result so + // that it can be retrieved when the sequence has ended (and any associated artifacts processed) + const auto& [it, success] = m_engineJobs->emplace(id, TestEngineJob(target, args, meta, result)); + + if (m_callback->has_value()) + { + (*m_callback).value()(it->second); + } + } + + private: + const AZStd::vector& m_testTargets; + TestEngineJobMap* m_engineJobs; + AZStd::optional* m_callback; + }; + + // Helper function to compile the run type specific test engine jobs from their associated jobs and payloads + template + AZStd::vector> CompileTestEngineRuns( + const AZStd::vector& testTargets, + AZStd::vector& runnerjobs, + TestEngineJobMap&& engineJobs) + { + AZStd::vector> engineRuns; + engineRuns.reserve(testTargets.size()); + + for (auto& job : runnerjobs) + { + const auto id = job.GetJobInfo().GetId().m_value; + if (auto it = engineJobs.find(id); + it != engineJobs.end()) + { + // An entry in the test engine job map means that this job was acted upon (an attempt to execute, successful or otherwise) + auto& engineJob = it->second; + TestEngineJobType run(AZStd::move(engineJob), job.ReleasePayload()); + engineRuns.push_back(AZStd::move(run)); + } + else + { + // No entry in the test engine job map means that this job never had the opportunity to be acted upon (the sequence + // was terminated whilst this job was still queued up for execution) + const auto& args = job.GetJobInfo().GetCommand().m_args; + const auto* target = testTargets[id]; + TestEngineJobType run(TestEngineJob(target, args, {}, Client::TestRunResult::NotRun), {}); + engineRuns.push_back(AZStd::move(run)); + } + } + + return engineRuns; + } + + Bitwise::TestJobExceptionPolicy GetTestJobExceptionPolicy( + Policy::ExecutionFailure executionFailurePolicy, Policy::TestFailure testFailurePolicy) + { + auto jobExecutionPolicy = Bitwise::TestJobExceptionPolicy::Never; + if (executionFailurePolicy == Policy::ExecutionFailure::Abort) + { + jobExecutionPolicy |= Bitwise::TestJobExceptionPolicy::OnFailedToExecute; + } + + if (testFailurePolicy == Policy::TestFailure::Abort) + { + jobExecutionPolicy |= Bitwise::TestJobExceptionPolicy::OnExecutedWithFailure; + } + + return jobExecutionPolicy; + } + } + + TestEngine::TestEngine( + const RepoPath& sourceDir, + const RepoPath& targetBinaryDir, + const RepoPath& cacheDir, + const RepoPath& artifactDir, + const RepoPath& testRunnerBinary, + const RepoPath& instrumentBinary, + size_t maxConcurrentRuns) + : m_maxConcurrentRuns(maxConcurrentRuns) + , m_testJobInfoGenerator(AZStd::make_unique( + sourceDir, targetBinaryDir, cacheDir, artifactDir, testRunnerBinary, instrumentBinary)) + , m_testEnumerator(AZStd::make_unique(maxConcurrentRuns)) + , m_instrumentedTestRunner(AZStd::make_unique(maxConcurrentRuns)) + , m_testRunner(AZStd::make_unique(maxConcurrentRuns)) + { + } + + TestEngine::~TestEngine() = default; + + AZStd::pair> TestEngine::UpdateEnumerationCache( + const AZStd::vector& testTargets, + Policy::ExecutionFailure executionFailurePolicy, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional callback) + { + TestEngineJobMap engineJobs; + const auto jobInfos = m_testJobInfoGenerator->GenerateTestEnumerationJobInfos(testTargets, TestEnumerator::JobInfo::CachePolicy::Write); + + const auto jobExecutionPolicy = executionFailurePolicy == Policy::ExecutionFailure::Abort + ? (TestEnumerator::JobExceptionPolicy::OnExecutedWithFailure | TestEnumerator::JobExceptionPolicy::OnFailedToExecute) + : TestEnumerator::JobExceptionPolicy::Never; + + auto [result, runnerJobs] = m_testEnumerator->Enumerate( + jobInfos, + TestEnumerator::CacheExceptionPolicy::OnCacheWriteFailure, + jobExecutionPolicy, + testTargetTimeout, + globalTimeout, + TestJobRunnerCallbackHandler(testTargets, &engineJobs, &callback)); + + auto engineRuns = CompileTestEngineRuns(testTargets, runnerJobs, AZStd::move(engineJobs)); + return { CalculateSequenceResult(result, engineRuns, executionFailurePolicy), AZStd::move(engineRuns) }; + } + + AZStd::pair> TestEngine::RegularRun( + const AZStd::vector& testTargets, + [[maybe_unused]]Policy::TestSharding testShardingPolicy, + Policy::ExecutionFailure executionFailurePolicy, + Policy::TestFailure testFailurePolicy, + [[maybe_unused]]Policy::TargetOutputCapture targetOutputCapture, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional callback) + { + TestEngineJobMap engineJobs; + const auto jobInfos = m_testJobInfoGenerator->GenerateRegularTestRunJobInfos(testTargets); + + TestJobRunnerCallbackHandler jobCallback(testTargets, &engineJobs, &callback); + auto [result, runnerJobs] = m_testRunner->RunTests( + jobInfos, + GetTestJobExceptionPolicy(executionFailurePolicy, testFailurePolicy), + testTargetTimeout, + globalTimeout, + jobCallback); + + auto engineRuns = CompileTestEngineRuns(testTargets, runnerJobs, AZStd::move(engineJobs)); + return { CalculateSequenceResult(result, engineRuns, executionFailurePolicy), AZStd::move(engineRuns) }; + } + + AZStd::pair> TestEngine::InstrumentedRun( + const AZStd::vector& testTargets, + [[maybe_unused]] Policy::TestSharding testShardingPolicy, + Policy::ExecutionFailure executionFailurePolicy, + Policy::IntegrityFailure integrityFailurePolicy, + Policy::TestFailure testFailurePolicy, + [[maybe_unused]]Policy::TargetOutputCapture targetOutputCapture, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional callback) + { + TestEngineJobMap engineJobs; + const auto jobInfos = m_testJobInfoGenerator->GenerateInstrumentedTestRunJobInfos(testTargets, CoverageLevel::Source); + + auto [result, runnerJobs] = m_instrumentedTestRunner->RunInstrumentedTests( + jobInfos, + GetTestJobExceptionPolicy(executionFailurePolicy, testFailurePolicy), + testTargetTimeout, + globalTimeout, + TestJobRunnerCallbackHandler(testTargets, &engineJobs, &callback)); + + auto engineRuns = CompileTestEngineRuns(testTargets, runnerJobs, AZStd::move(engineJobs)); + + // Now that we know the true result of successful jobs that return non-zero we can deduce if we have any integrity failures + // where a test target ran and completed its tests without incident yet failed to produce coverage data + if (integrityFailurePolicy == Policy::IntegrityFailure::Abort) + { + for (const auto& engineRun : engineRuns) + { + if (const auto testResult = engineRun.GetTestResult(); + testResult == Client::TestRunResult::AllTestsPass || testResult == Client::TestRunResult::TestFailures) + { + AZ_TestImpact_Eval(engineRun.GetTestCoverge().has_value(), TestEngineException, AZStd::string::format( + "Test target %s completed its test run but failed to produce coverage data", engineRun.GetTestTarget()->GetName().c_str())); + } + } + } + + return { CalculateSequenceResult(result, engineRuns, executionFailurePolicy), AZStd::move(engineRuns) }; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h new file mode 100644 index 0000000000..f6fb6cec11 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h @@ -0,0 +1,126 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace TestImpact +{ + class TestTarget; + class TestJobInfoGenerator; + class TestEnumerator; + class InstrumentedTestRunner; + class TestRunner; + + //! Callback for when a given test engine job completes. + using TestEngineJobCompleteCallback = AZStd::function; + + //! Provides the front end for performing test enumerations and test runs. + class TestEngine + { + public: + //! Configures the test engine with the necessary path information for launching test targets and managing the artifacts they produce. + //! @param sourceDir Root path where source files are found (including subfolders). + //! @param targetBinaryDir Path to where the test target binaries are found. + //! @param cacheDir Path to the persistent folder where test target enumerations are cached. + //! @param artifactDir Path to the transient directory where test artifacts are produced. + //! @param testRunnerBinary Path to the binary responsible for launching test targets that have the TestRunner launch method. + //! @param instrumentBinary Path to the binary responsible for launching test targets with test coverage instrumentation. + //! @param maxConcurrentRuns The maximum number of concurrent test targets that can be in flight at any given moment. + TestEngine( + const RepoPath& sourceDir, + const RepoPath& targetBinaryDir, + const RepoPath& cacheDir, + const RepoPath& artifactDir, + const RepoPath& testRunnerBinary, + const RepoPath& instrumentBinary, + size_t maxConcurrentRuns); + + ~TestEngine(); + + //! Updates the cached enumerations for the specified test targets. + //! @note Whilst test runs will make use of this cache for test target sharding it is the responsibility of the client to + //! ensure any stale caches are up to date by calling this function. No attempt to maintain internal consistency will be made + //! by the test engine itself. + //! @param testTargets The test targets to enumerate. + //! @param executionFailurePolicy The policy for how enumeration execution failures should be handled. + //! @param testTargetTimeout The maximum duration a test target may be in-flight for before being forcefully terminated (infinite if empty). + //! @param globalTimeout The maximum duration the enumeration sequence may run before being forcefully terminated (infinite if empty). + //! @param callback The client callback function to handle completed test target enumerations. + //! @ returns The sequence result and the enumerations for the target that were enumerated. + AZStd::pair < TestSequenceResult, AZStd::vector> UpdateEnumerationCache( + const AZStd::vector& testTargets, + Policy::ExecutionFailure executionFailurePolicy, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional callback); + + //! Performs a test run without any instrumentation and, for each test target, returns the test run results and metrics about the run. + //! @param testTargets The test targets to run. + //! @param testShardingPolicy Test sharding policy to use for test targets in this run. + //! @param executionFailurePolicy Policy for how test execution failures should be handled. + //! @param testFailurePolicy Policy for how test targets with failing tests should be handled. + //! @param targetOutputCapture Policy for how test target standard output should be captured and handled. + //! @param testTargetTimeout The maximum duration a test target may be in-flight for before being forcefully terminated (infinite if empty). + //! @param globalTimeout The maximum duration the enumeration sequence may run before being forcefully terminated (infinite if empty). + //! @param callback The client callback function to handle completed test target runs. + //! @ returns The sequence result and the test run results for the test targets that were run. + [[nodiscard]]AZStd::pair> RegularRun( + const AZStd::vector& testTargets, + Policy::TestSharding testShardingPolicy, + Policy::ExecutionFailure executionFailurePolicy, + Policy::TestFailure testFailurePolicy, + Policy::TargetOutputCapture targetOutputCapture, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional callback); + + //! Performs a test run with instrumentation and, for each test target, returns the test run results, coverage data and metrics about the run. + //! @param testTargets The test targets to run. + //! @param testShardingPolicy Test sharding policy to use for test targets in this run. + //! @param executionFailurePolicy Policy for how test execution failures should be handled. + //! @param integrityFailurePolicy Policy for how integrty failures of the test impact data and source tree model should be handled. + //! @param testFailurePolicy Policy for how test targets with failing tests should be handled. + //! @param targetOutputCapture Policy for how test target standard output should be captured and handled. + //! @param testTargetTimeout The maximum duration a test target may be in-flight for before being forcefully terminated (infinite if empty). + //! @param globalTimeout The maximum duration the enumeration sequence may run before being forcefully terminated (infinite if empty). + //! @param callback The client callback function to handle completed test target runs. + //! @ returns The sequence result and the test run results and test coverages for the test targets that were run. + [[nodiscard]]AZStd::pair> InstrumentedRun( + const AZStd::vector& testTargets, + Policy::TestSharding testShardingPolicy, + Policy::ExecutionFailure executionFailurePolicy, + Policy::IntegrityFailure integrityFailurePolicy, + Policy::TestFailure testFailurePolicy, + Policy::TargetOutputCapture targetOutputCapture, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional callback); + + private: + size_t m_maxConcurrentRuns = 0; + AZStd::unique_ptr m_testJobInfoGenerator; + AZStd::unique_ptr m_testEnumerator; + AZStd::unique_ptr m_instrumentedTestRunner; + AZStd::unique_ptr m_testRunner; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineEnumeration.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineEnumeration.cpp new file mode 100644 index 0000000000..3c114d1b33 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineEnumeration.cpp @@ -0,0 +1,27 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + TestEngineEnumeration::TestEngineEnumeration(TestEngineJob&& job, AZStd::optional&& enumeration) + : TestEngineJob(AZStd::move(job)) + , m_enumeration(AZStd::move(enumeration)) + { + } + + const AZStd::optional& TestEngineEnumeration::GetTestEnumeration() const + { + return m_enumeration; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineEnumeration.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineEnumeration.h new file mode 100644 index 0000000000..28473a08bc --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineEnumeration.h @@ -0,0 +1,32 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Represents the generated test enumeration data for a test engine enumeration. + class TestEngineEnumeration + : public TestEngineJob + { + public: + TestEngineEnumeration(TestEngineJob&& job, AZStd::optional&& enumeration); + + //! Returns the test enumeration payload for this job (if any). + const AZStd::optional& GetTestEnumeration() const; + private: + AZStd::optional m_enumeration; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineException.h similarity index 86% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunException.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineException.h index ec13a17f57..cf10b68b24 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunException.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineException.h @@ -16,8 +16,8 @@ namespace TestImpact { - //! Exception for test runs and test run related operations. - class TestRunException : public Exception + //! Exception for test engine runs and related operations. + class TestEngineException : public Exception { public: using Exception::Exception; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.cpp new file mode 100644 index 0000000000..b19ec723d1 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.cpp @@ -0,0 +1,52 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + namespace + { + AZStd::optional ReleaseTestRun(AZStd::optional>&& testRunAndCoverage) + { + if (testRunAndCoverage.has_value()) + { + return AZStd::move(testRunAndCoverage.value().first); + } + + return AZStd::nullopt; + } + + AZStd::optional ReleaseTestCoverage(AZStd::optional>&& testRunAndCoverage) + { + if (testRunAndCoverage.has_value()) + { + return AZStd::move(testRunAndCoverage.value().second); + } + + return AZStd::nullopt; + } + } + + TestEngineInstrumentedRun::TestEngineInstrumentedRun(TestEngineJob&& testJob, AZStd::optional>&& testRunAndCoverage) + : TestEngineRegularRun(AZStd::move(testJob), ReleaseTestRun(AZStd::move(testRunAndCoverage))) + , m_testCoverage(ReleaseTestCoverage(AZStd::move(testRunAndCoverage))) + { + } + + const AZStd::optional& TestEngineInstrumentedRun::GetTestCoverge() const + { + return m_testCoverage; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.h new file mode 100644 index 0000000000..b1edd943a4 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.h @@ -0,0 +1,32 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace TestImpact +{ + //! Represents the generated test run and coverage data for an instrumented regular test engine run. + class TestEngineInstrumentedRun + : public TestEngineRegularRun + { + public: + TestEngineInstrumentedRun(TestEngineJob&& testJob, AZStd::optional>&& testRunAndCoverage); + + //! Returns the test coverage payload for this job (if any). + const AZStd::optional& GetTestCoverge() const; + private: + AZStd::optional m_testCoverage; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJob.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJob.cpp new file mode 100644 index 0000000000..26aa71adcd --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJob.cpp @@ -0,0 +1,40 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +namespace TestImpact +{ + TestEngineJob::TestEngineJob(const TestTarget* testTarget, const AZStd::string& commandString, const JobMeta& jobMeta, Client::TestRunResult testResult) + : JobMetaWrapper(jobMeta) + , m_testTarget(testTarget) + , m_commandString(commandString) + , m_testResult(testResult) + { + } + + const TestTarget* TestEngineJob::GetTestTarget() const + { + return m_testTarget; + } + + const AZStd::string& TestEngineJob::GetCommandString() const + { + return m_commandString; + } + + Client::TestRunResult TestEngineJob::GetTestResult() const + { + return m_testResult; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJob.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJob.h new file mode 100644 index 0000000000..00d0c7d099 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJob.h @@ -0,0 +1,41 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + class TestTarget; + + //! Represents the meta-data describing a test engine run. + class TestEngineJob + : public JobMetaWrapper + { + public: + TestEngineJob(const TestTarget* testTarget, const AZStd::string& commandString, const JobMeta& jobMeta, Client::TestRunResult testResult); + + //! Returns the test target that was run for this job. + const TestTarget* GetTestTarget() const; + + //! Returns the result of the job that was run. + Client::TestRunResult GetTestResult() const; + + //! Returns the command string that was used to execute this job. + const AZStd::string& GetCommandString() const; + private: + const TestTarget* m_testTarget; + AZStd::string m_commandString; + Client::TestRunResult m_testResult; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationException.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineRegularRun.cpp similarity index 61% rename from Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationException.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineRegularRun.cpp index 0c9d27df7a..0090689e8b 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerationException.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineRegularRun.cpp @@ -10,17 +10,18 @@ * */ -#pragma once - -#include +#include namespace TestImpact { - //! Exception for test enumerations and test enumeration related operations. - class TestEnumerationException - : public Exception + TestEngineRegularRun::TestEngineRegularRun(TestEngineJob&& testJob, AZStd::optional&& testRun) + : TestEngineJob(AZStd::move(testJob)) + , m_testRun(AZStd::move(testRun)) + { + } + + const AZStd::optional& TestEngineRegularRun::GetTestRun() const { - public: - using Exception::Exception; - }; + return m_testRun; + } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineRegularRun.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineRegularRun.h new file mode 100644 index 0000000000..3c65830094 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineRegularRun.h @@ -0,0 +1,34 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include +#include + +namespace TestImpact +{ + //! Represents the generated test run data for a regular test engine run. + class TestEngineRegularRun + : public TestEngineJob + { + public: + TestEngineRegularRun(TestEngineJob&& testJob, AZStd::optional&& testRun); + + //! Returns the test run payload for this job (if any). + const AZStd::optional& GetTestRun() const; + private: + AZStd::optional m_testRun; + }; +} // namespace TestImpact From 0ef519d11d5b7d7fd6d3924a88f469afd0088dea Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 25 May 2021 12:55:11 +0100 Subject: [PATCH 055/233] Address PR comments --- .../Runtime/Code/Source/TestEngine/TestImpactTestEngine.h | 6 +++--- .../TestEngine/TestImpactTestEngineInstrumentedRun.cpp | 8 ++++---- .../TestEngine/TestImpactTestEngineInstrumentedRun.h | 1 + .../Code/Source/TestEngine/TestImpactTestEngineJob.h | 1 + 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h index f6fb6cec11..c67a10aa4c 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h @@ -67,7 +67,7 @@ namespace TestImpact //! @param globalTimeout The maximum duration the enumeration sequence may run before being forcefully terminated (infinite if empty). //! @param callback The client callback function to handle completed test target enumerations. //! @ returns The sequence result and the enumerations for the target that were enumerated. - AZStd::pair < TestSequenceResult, AZStd::vector> UpdateEnumerationCache( + AZStd::pair> UpdateEnumerationCache( const AZStd::vector& testTargets, Policy::ExecutionFailure executionFailurePolicy, AZStd::optional testTargetTimeout, @@ -84,7 +84,7 @@ namespace TestImpact //! @param globalTimeout The maximum duration the enumeration sequence may run before being forcefully terminated (infinite if empty). //! @param callback The client callback function to handle completed test target runs. //! @ returns The sequence result and the test run results for the test targets that were run. - [[nodiscard]]AZStd::pair> RegularRun( + [[nodiscard]] AZStd::pair> RegularRun( const AZStd::vector& testTargets, Policy::TestSharding testShardingPolicy, Policy::ExecutionFailure executionFailurePolicy, @@ -105,7 +105,7 @@ namespace TestImpact //! @param globalTimeout The maximum duration the enumeration sequence may run before being forcefully terminated (infinite if empty). //! @param callback The client callback function to handle completed test target runs. //! @ returns The sequence result and the test run results and test coverages for the test targets that were run. - [[nodiscard]]AZStd::pair> InstrumentedRun( + [[nodiscard]] AZStd::pair> InstrumentedRun( const AZStd::vector& testTargets, Policy::TestSharding testShardingPolicy, Policy::ExecutionFailure executionFailurePolicy, diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.cpp index b19ec723d1..20c80c5f5f 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.cpp @@ -18,7 +18,7 @@ namespace TestImpact { namespace { - AZStd::optional ReleaseTestRun(AZStd::optional>&& testRunAndCoverage) + AZStd::optional ReleaseTestRun(AZStd::optional>& testRunAndCoverage) { if (testRunAndCoverage.has_value()) { @@ -28,7 +28,7 @@ namespace TestImpact return AZStd::nullopt; } - AZStd::optional ReleaseTestCoverage(AZStd::optional>&& testRunAndCoverage) + AZStd::optional ReleaseTestCoverage(AZStd::optional>& testRunAndCoverage) { if (testRunAndCoverage.has_value()) { @@ -40,8 +40,8 @@ namespace TestImpact } TestEngineInstrumentedRun::TestEngineInstrumentedRun(TestEngineJob&& testJob, AZStd::optional>&& testRunAndCoverage) - : TestEngineRegularRun(AZStd::move(testJob), ReleaseTestRun(AZStd::move(testRunAndCoverage))) - , m_testCoverage(ReleaseTestCoverage(AZStd::move(testRunAndCoverage))) + : TestEngineRegularRun(AZStd::move(testJob), ReleaseTestRun(testRunAndCoverage)) + , m_testCoverage(ReleaseTestCoverage(testRunAndCoverage)) { } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.h index b1edd943a4..efecebcbd4 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineInstrumentedRun.h @@ -26,6 +26,7 @@ namespace TestImpact //! Returns the test coverage payload for this job (if any). const AZStd::optional& GetTestCoverge() const; + private: AZStd::optional m_testCoverage; }; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJob.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJob.h index 00d0c7d099..753a4cd494 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJob.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJob.h @@ -33,6 +33,7 @@ namespace TestImpact //! Returns the command string that was used to execute this job. const AZStd::string& GetCommandString() const; + private: const TestTarget* m_testTarget; AZStd::string m_commandString; From 828aeb2147e73b25fbde5ce890fd89ee9c03137d Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 25 May 2021 13:20:43 +0100 Subject: [PATCH 056/233] Add JobInfoGenerator --- .../TestImpactWin32_TestTargetExtension.cpp | 46 +++++ .../Windows/platform_windows_files.cmake | 1 + .../TestImpactTestJobInfoGenerator.cpp | 191 ++++++++++++++++++ .../TestImpactTestJobInfoGenerator.h | 108 ++++++++++ .../JobRunner/TestImpactTestTargetExtension.h | 23 +++ 5 files changed, 369 insertions(+) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestTargetExtension.h diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp new file mode 100644 index 0000000000..bd380ac2a7 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp @@ -0,0 +1,46 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include + +namespace TestImpact +{ + AZStd::string GetTestTargetExtension(const TestTarget* testTarget) + { + static constexpr char* const standAloneExtension = ".exe"; + static constexpr char* const testRunnerExtension = ".dll"; + + switch (const auto launchMethod = testTarget->GetLaunchMethod(); launchMethod) + { + case LaunchMethod::StandAlone: + { + return standAloneExtension; + } + case LaunchMethod::TestRunner: + { + return testRunnerExtension; + } + default: + { + throw TestEngineException( + AZStd::string::format( + "Unexpected launch method for target %s: %u", + testTarget->GetName().c_str(), + aznumeric_cast(launchMethod))); + } + } + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/platform_windows_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/platform_windows_files.cmake index 32f996cdf8..5f5e8fc3d1 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/platform_windows_files.cmake +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -16,4 +16,5 @@ set(FILES Process/TestImpactWin32_Handle.h Process/TestImpactWin32_Pipe.cpp Process/TestImpactWin32_Pipe.h + TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp ) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp new file mode 100644 index 0000000000..0cf717e88e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp @@ -0,0 +1,191 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +namespace TestImpact +{ + TestJobInfoGenerator::TestJobInfoGenerator( + const RepoPath& sourceDir, + const RepoPath& targetBinaryDir, + const RepoPath& cacheDir, + const RepoPath& artifactDir, + const RepoPath& testRunnerBinary, + const RepoPath& instrumentBinary) + : m_sourceDir(sourceDir) + , m_targetBinaryDir(targetBinaryDir) + , m_cacheDir(cacheDir) + , m_artifactDir(artifactDir) + , m_testRunnerBinary(testRunnerBinary) + , m_instrumentBinary(instrumentBinary) + { + } + + AZStd::string TestJobInfoGenerator::GenerateLaunchArgument(const TestTarget* testTarget) const + { + if (testTarget->GetLaunchMethod() == LaunchMethod::StandAlone) + { + return AZStd::string::format( + "%s%s %s", + (m_targetBinaryDir / testTarget->GetOutputName()).c_str(), + GetTestTargetExtension(testTarget).c_str(), + testTarget->GetCustomArgs().c_str()).c_str(); + } + else + { + return AZStd::string::format( + "\"%s\" \"%s%s\" %s", + m_testRunnerBinary.c_str(), + (m_targetBinaryDir / testTarget->GetOutputName()).c_str(), + GetTestTargetExtension(testTarget).c_str(), + testTarget->GetCustomArgs().c_str()).c_str(); + } + } + + RepoPath TestJobInfoGenerator::GenerateTargetEnumerationCacheFilePath(const TestTarget* testTarget) const + { + return AZStd::string::format("%s.cache", (m_artifactDir / testTarget->GetName()).c_str()); + } + + RepoPath TestJobInfoGenerator::GenerateTargetEnumerationArtifactFilePath(const TestTarget* testTarget) const + { + return AZStd::string::format("%s.Enumeration.xml", (m_artifactDir / testTarget->GetName()).c_str()); + } + + RepoPath TestJobInfoGenerator::GenerateTargetRunArtifactFilePath(const TestTarget* testTarget) const + { + return AZStd::string::format("%s.Run.xml", (m_artifactDir / testTarget->GetName()).c_str()); + } + + RepoPath TestJobInfoGenerator::GenerateTargetCoverageArtifactFilePath(const TestTarget* testTarget) const + { + return AZStd::string::format("%s.Coverage.xml", (m_artifactDir / testTarget->GetName()).c_str()); + } + + TestEnumerator::JobInfo TestJobInfoGenerator::GenerateTestEnumerationJobInfo( + const TestTarget* testTarget, + TestEnumerator::JobInfo::Id jobId, + TestEnumerator::JobInfo::CachePolicy cachePolicy) const + { + using Command = TestEnumerator::Command; + using JobInfo = TestEnumerator::JobInfo; + using JobData = TestEnumerator::JobData; + using Cache = TestEnumerator::JobData::Cache; + + const auto enumerationArtifact = GenerateTargetEnumerationArtifactFilePath(testTarget); + const Command args = + { + AZStd::string::format( + "%s --gtest_list_tests --gtest_output=xml:\"%s\"", + GenerateLaunchArgument(testTarget).c_str(), + enumerationArtifact.c_str()) + }; + + return JobInfo(jobId, args, JobData(enumerationArtifact, Cache{ cachePolicy, GenerateTargetEnumerationCacheFilePath(testTarget) })); + } + + TestRunner::JobInfo TestJobInfoGenerator::GenerateRegularTestRunJobInfo( + const TestTarget* testTarget, + TestRunner::JobInfo::Id jobId) const + { + using Command = TestRunner::Command; + using JobInfo = TestRunner::JobInfo; + using JobData = TestRunner::JobData; + + const auto runArtifact = GenerateTargetRunArtifactFilePath(testTarget); + const Command args = + { + AZStd::string::format( + "%s --gtest_output=xml:\"%s\"", + GenerateLaunchArgument(testTarget).c_str(), + runArtifact.c_str()) + }; + + return JobInfo(jobId, args, JobData(runArtifact)); + } + + InstrumentedTestRunner::JobInfo TestJobInfoGenerator::GenerateInstrumentedTestRunJobInfo( + const TestTarget* testTarget, + InstrumentedTestRunner::JobInfo::Id jobId, + CoverageLevel coverageLevel) const + { + using Command = InstrumentedTestRunner::Command; + using JobInfo = InstrumentedTestRunner::JobInfo; + using JobData = InstrumentedTestRunner::JobData; + + const auto coverageArtifact = GenerateTargetCoverageArtifactFilePath(testTarget); + const auto runArtifact = GenerateTargetRunArtifactFilePath(testTarget); + const Command args = + { + AZStd::string::format( + "\"%s\" " // 1. Instrumented test runner + "--coverage_level %s " // 2. Coverage level + "--export_type cobertura:\"%s\" " // 3. Test coverage artifact path + "--modules \"%s\" " // 4. Modules path + "--excluded_modules \"%s\" " // 5. Exclude modules + "--sources \"%s\" -- " // 6. Sources path + "%s " // 7. Launch command + "--gtest_output=xml:\"%s\"", // 8. Result artifact + + m_instrumentBinary.c_str(), // 1. Instrumented test runner + (coverageLevel == CoverageLevel::Line ? "line" : "source"), // 2. Coverage level + coverageArtifact.c_str(), // 3. Test coverage artifact path + m_targetBinaryDir.c_str(), // 4. Modules path + m_testRunnerBinary.c_str(), // 5. Exclude modules + m_sourceDir.c_str(), // 6. Sources path + GenerateLaunchArgument(testTarget).c_str(), // 7. Launch command + runArtifact.c_str()) // 8. Result artifact + }; + + return JobInfo(jobId, args, JobData(runArtifact, coverageArtifact)); + } + + AZStd::vector TestJobInfoGenerator::GenerateTestEnumerationJobInfos( + const AZStd::vector& testTargets, + TestEnumerator::JobInfo::CachePolicy cachePolicy) const + { + AZStd::vector jobInfos; + for (size_t jobId = 0; jobId < testTargets.size(); jobId++) + { + jobInfos.push_back(GenerateTestEnumerationJobInfo(testTargets[jobId], { jobId }, cachePolicy)); + } + + return jobInfos; + } + + AZStd::vector TestJobInfoGenerator::GenerateRegularTestRunJobInfos( + const AZStd::vector& testTargets) const + { + AZStd::vector jobInfos; + for (size_t jobId = 0; jobId < testTargets.size(); jobId++) + { + jobInfos.push_back(GenerateRegularTestRunJobInfo(testTargets[jobId], { jobId })); + } + + return jobInfos; + } + + AZStd::vector TestJobInfoGenerator::GenerateInstrumentedTestRunJobInfos( + const AZStd::vector& testTargets, + CoverageLevel coverageLevel) const + { + AZStd::vector jobInfos; + for (size_t jobId = 0; jobId < testTargets.size(); jobId++) + { + jobInfos.push_back(GenerateInstrumentedTestRunJobInfo(testTargets[jobId], { jobId }, coverageLevel)); + } + + return jobInfos; + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h new file mode 100644 index 0000000000..0cae419e3a --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h @@ -0,0 +1,108 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include +#include +#include +#include + +#include + +namespace TestImpact +{ + class TestTarget; + + // Generates job information for the different test job runner types. + class TestJobInfoGenerator + { + public: + //! Configures the test job info generator with the necessary path information for launching test targets. + //! @param sourceDir Root path where source files are found (including subfolders). + //! @param targetBinaryDir Path to where the test target binaries are found. + //! @param cacheDir Path to the persistent folder where test target enumerations are cached. + //! @param artifactDir Path to the transient directory where test artifacts are produced. + //! @param testRunnerBinary Path to the binary responsible for launching test targets that have the TestRunner launch method. + //! @param instrumentBinary Path to the binary responsible for launching test targets with test coverage instrumentation. + TestJobInfoGenerator( + const RepoPath& sourceDir, + const RepoPath& targetBinaryDir, + const RepoPath& cacheDir, + const RepoPath& artifactDir, + const RepoPath& testRunnerBinary, + const RepoPath& instrumentBinary); + + //! Generates a the information for a test enumeration job. + //! @param testTarget The test target to generate the job information for. + //! @param jobId The id to assign for this job. + //! @param cachePolicy The cache policy to use for this job. + TestEnumerator::JobInfo GenerateTestEnumerationJobInfo( + const TestTarget* testTarget, + TestEnumerator::JobInfo::Id jobId, + TestEnumerator::JobInfo::CachePolicy cachePolicy) const; + + //! Generates a the information for a test run job. + //! @param testTarget The test target to generate the job information for. + //! @param jobId The id to assign for this job. + TestRunner::JobInfo GenerateRegularTestRunJobInfo( + const TestTarget* testTarget, + TestRunner::JobInfo::Id jobId) const; + + //! Generates a the information for an instrumented test run job. + //! @param testTarget The test target to generate the job information for. + //! @param jobId The id to assign for this job. + //! @param coverageLevel The coverage level to use for this job. + InstrumentedTestRunner::JobInfo GenerateInstrumentedTestRunJobInfo( + const TestTarget* testTarget, + InstrumentedTestRunner::JobInfo::Id jobId, + CoverageLevel coverageLevel) const; + + //! Generates the information for the batch of test enumeration jobs. + AZStd::vector GenerateTestEnumerationJobInfos( + const AZStd::vector& testTargets, + TestEnumerator::JobInfo::CachePolicy cachePolicy) const; + + //! Generates the information for the batch of test run jobs. + AZStd::vector GenerateRegularTestRunJobInfos( + const AZStd::vector& testTargets) const; + + //! Generates the information for the batch of instrumented test run jobs. + AZStd::vector GenerateInstrumentedTestRunJobInfos( + const AZStd::vector& testTargets, + CoverageLevel coverageLevel) const; + private: + //! Generates the command string to launch the specified test target. + AZStd::string GenerateLaunchArgument(const TestTarget* testTarget) const; + + //! Generates the path to the enumeration cache file for the specified test target. + RepoPath GenerateTargetEnumerationCacheFilePath(const TestTarget* testTarget) const; + + //! Generates the path to the enumeration artifact file for the specified test target. + RepoPath GenerateTargetEnumerationArtifactFilePath(const TestTarget* testTarget) const; + + //! Generates the path to the test run artifact file for the specified test target. + RepoPath GenerateTargetRunArtifactFilePath(const TestTarget* testTarget) const; + + //! Generates the path to the test coverage artifact file for the specified test target. + RepoPath GenerateTargetCoverageArtifactFilePath(const TestTarget* testTarget) const; + + RepoPath m_sourceDir; + RepoPath m_targetBinaryDir; + RepoPath m_cacheDir; + RepoPath m_artifactDir; + RepoPath m_testRunnerBinary; + RepoPath m_instrumentBinary; + }; +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestTargetExtension.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestTargetExtension.h new file mode 100644 index 0000000000..9251164052 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestTargetExtension.h @@ -0,0 +1,23 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + class TestTarget; + + //! Returns the binary file extension for the specified test target. + AZStd::string GetTestTargetExtension(const TestTarget* testTarget); +} From c93a2f06ff7f5944f390be98206367d23b649055 Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 25 May 2021 15:37:31 +0100 Subject: [PATCH 057/233] Address PR comments --- .../JobRunner/TestImpactWin32_TestTargetExtension.cpp | 2 -- .../JobRunner/TestImpactTestJobInfoGenerator.cpp | 3 +++ .../TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp index bd380ac2a7..12c78f91cf 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp @@ -10,8 +10,6 @@ * */ -#pragma once - #include #include #include diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp index 0cf717e88e..7e8e65e940 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp @@ -156,6 +156,7 @@ namespace TestImpact TestEnumerator::JobInfo::CachePolicy cachePolicy) const { AZStd::vector jobInfos; + jobInfos.reserve(testTargets.size()); for (size_t jobId = 0; jobId < testTargets.size(); jobId++) { jobInfos.push_back(GenerateTestEnumerationJobInfo(testTargets[jobId], { jobId }, cachePolicy)); @@ -168,6 +169,7 @@ namespace TestImpact const AZStd::vector& testTargets) const { AZStd::vector jobInfos; + jobInfos.reserve(testTargets.size()); for (size_t jobId = 0; jobId < testTargets.size(); jobId++) { jobInfos.push_back(GenerateRegularTestRunJobInfo(testTargets[jobId], { jobId })); @@ -181,6 +183,7 @@ namespace TestImpact CoverageLevel coverageLevel) const { AZStd::vector jobInfos; + jobInfos.reserve(testTargets.size()); for (size_t jobId = 0; jobId < testTargets.size(); jobId++) { jobInfos.push_back(GenerateInstrumentedTestRunJobInfo(testTargets[jobId], { jobId }, coverageLevel)); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h index 0cae419e3a..1bd06f6372 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h @@ -25,7 +25,7 @@ namespace TestImpact { class TestTarget; - // Generates job information for the different test job runner types. + //! Generates job information for the different test job runner types. class TestJobInfoGenerator { public: @@ -44,7 +44,7 @@ namespace TestImpact const RepoPath& testRunnerBinary, const RepoPath& instrumentBinary); - //! Generates a the information for a test enumeration job. + //! Generates the information for a test enumeration job. //! @param testTarget The test target to generate the job information for. //! @param jobId The id to assign for this job. //! @param cachePolicy The cache policy to use for this job. @@ -53,14 +53,14 @@ namespace TestImpact TestEnumerator::JobInfo::Id jobId, TestEnumerator::JobInfo::CachePolicy cachePolicy) const; - //! Generates a the information for a test run job. + //! Generates the information for a test run job. //! @param testTarget The test target to generate the job information for. //! @param jobId The id to assign for this job. TestRunner::JobInfo GenerateRegularTestRunJobInfo( const TestTarget* testTarget, TestRunner::JobInfo::Id jobId) const; - //! Generates a the information for an instrumented test run job. + //! Generates the information for an instrumented test run job. //! @param testTarget The test target to generate the job information for. //! @param jobId The id to assign for this job. //! @param coverageLevel The coverage level to use for this job. From f9425992f86fe20fa822c761233cd71e34020c52 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 11:37:34 +0100 Subject: [PATCH 058/233] Runtime refactor --- .../TestImpactClientFailureReport.h | 70 +++----------- .../TestImpactClientTestSelection.h | 6 +- ...estImpactUtils.h => TestImpactFileUtils.h} | 22 +++++ .../TestImpactFramework/TestImpactRepoPath.h | 68 ++++++++++++-- .../TestImpactFramework/TestImpactRuntime.h | 77 +++++++++++---- .../TestImpactTestSequence.h | 18 ++-- .../TestImpactDynamicDependencyMap.cpp | 66 +++++++++++-- .../TestImpactDynamicDependencyMap.h | 14 ++- .../TestImpactSourceCoveringTestsList.cpp | 2 +- ...estImpactSourceCoveringTestsSerializer.cpp | 93 +++++++++++++++++++ .../TestImpactSourceCoveringTestsSerializer.h | 26 ++++++ .../TestImpactTestSelectorAndPrioritizer.cpp | 4 +- .../TestImpactTestSelectorAndPrioritizer.h | 13 +-- .../TestImpactWin32_TestEngineJobFailure.cpp | 35 +++++++ .../TestImpactTestEngineJobFailure.cpp | 82 ++++++++++++++++ .../TestImpactTestEngineJobFailure.h | 29 ++++++ 16 files changed, 512 insertions(+), 113 deletions(-) rename Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/{TestImpactUtils.h => TestImpactFileUtils.h} (77%) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsSerializer.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsSerializer.h create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/TestImpactWin32_TestEngineJobFailure.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h index 02b1169e93..19e98a7d1f 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h @@ -44,19 +44,6 @@ namespace TestImpact AZStd::string m_commandString; }; - //! Represents a test target that terminated abnormally. - class LauncherFailure - : public ExecutionFailure - { - public: - LauncherFailure(const AZStd::string& targetName, const AZStd::string& command, int returnCode); - - //! The return code of the test target that terminated abnormally. - int GetReturnCode() const; - private: - int m_returnCode; - }; - //! Represents an individual test of a test target that failed. class TestFailure { @@ -107,6 +94,7 @@ namespace TestImpact private: AZStd::vector m_testCaseFailures; + size_t m_numTestFailures = 0; }; //! Base class for reporting failing test sequences. @@ -115,63 +103,27 @@ namespace TestImpact public: SequenceFailure( AZStd::vector&& executionFailures, - AZStd::vector&& launcherFailures, + AZStd::vector&& testRunFailures, + AZStd::vector&& timedOutTests, AZStd::vector&& unexecutedTests); //! Returns the test targets in this sequence that failed to execute. const AZStd::vector& GetExecutionFailures() const; - //! Returns the test targets in this sequence that terminated abnormally. - const AZStd::vector& GetLauncherFailures() const; - - //! Returns the test targets in this sequence that were not executed due to the sequence terminating prematurely. - const AZStd::vector& GetUnexecutedTest() const; - - private: - AZStd::vector m_executionFailures; - AZStd::vector m_launcherFailures; - AZStd::vector m_unexecutedTests; - }; - - //! Represents the report for a failed regular test sequence run without test impact analysis. - class RegularSequenceFailure - : public SequenceFailure - { - public: - RegularSequenceFailure( - AZStd::vector&& executionFailures, - AZStd::vector&& launcherFailures, - AZStd::vector&& testRunFailures, - AZStd::vector&& unexecutedTests); - //! Returns the test targets that contain failing tests. const AZStd::vector& GetTestRunFailures() const; - private: - AZStd::vector m_testRunFailures; - }; + //! Returns the test targets in this sequence that were terminated for exceeding their allotted flight time. + const AZStd::vector& GetTimedOutTests() const; - //! Represents the report for a failed test sequence run with test impact analysis. - class ImpactAnalysisSequenceFailure - : public SequenceFailure - { - public: - ImpactAnalysisSequenceFailure( - AZStd::vector&& executionFailures, - AZStd::vector&& launcherFailures, - AZStd::vector&& selectedTestRunFailures, - AZStd::vector&& discardedTestRunFailures, - AZStd::vector&& unexecutedTests); - - //! Returns the test targets that were selected to run but contain failing tests. - const AZStd::vector GetSelectedTestRunFailures() const; - - //! Returns the test targets that were not selected but still run but contain failing tests. - const AZStd::vector GetDiscardedTestRunFailures() const; + //! Returns the test targets in this sequence that were not executed due to the sequence terminating prematurely. + const AZStd::vector& GetUnexecutedTests() const; private: - AZStd::vector m_selectedTestRunFailures; - AZStd::vector m_discardedTestRunFailures; + AZStd::vector m_executionFailures; + AZStd::vector m_testRunFailures; + AZStd::vector m_timedOutTests; + AZStd::vector m_unexecutedTests; }; } // namespace Client } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h index f251e7117d..76e6461206 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h @@ -25,6 +25,7 @@ namespace TestImpact class TestRunSelection { public: + TestRunSelection(const AZStd::vector& includedTests, const AZStd::vector& excludedTests); TestRunSelection(AZStd::vector&& includedTests, AZStd::vector&& excludedTests); //! Returns the test runs that were selected to be run and will actually be run. @@ -37,7 +38,10 @@ namespace TestImpact size_t GetNumIncludedTestRuns() const; //! Returns the number of selected test runs that will not be run. - size_t GetNumNumExcludedTestRuns() const; + size_t GetNumExcludedTestRuns() const; + + //! Returns the total number of tests runs selected regardless of whether or not they will actually be run. + size_t GetTotalNumTests() const; private: AZStd::vector m_includedTestRuns; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFileUtils.h similarity index 77% rename from Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h rename to Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFileUtils.h index d734d7a70e..aa64840e78 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFileUtils.h @@ -59,4 +59,26 @@ namespace TestImpact AZ_TestImpact_Eval( file.Write(bytes.data(), bytes.size()), ExceptionType, AZStd::string::format("Couldn't write contents for file %s", path.c_str())); } + + //! Delete the files that match the pattern from the specified directory. + //! @param path The path to the directory to pattern match the files for deletion. + //! @param pattern The pattern to match files for deletion. + inline void DeleteFiles(const RepoPath& path, const AZStd::string& pattern) + { + AZ::IO::SystemFile::FindFiles(AZStd::string::format("%s/%s", path.c_str(), pattern.c_str()).c_str(), + [&path](const char* file, bool isFile) + { + if (isFile) + { + AZ::IO::SystemFile::Delete(AZStd::string::format("%s/%s", path.c_str(), file).c_str()); + } + + return true; + }); + } + + inline void DeleteFile(const RepoPath& path) + { + DeleteFiles(path.ParentPath(), path.Filename().Native()); + } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h index ccba3f677d..80f82e4785 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h @@ -21,22 +21,78 @@ namespace TestImpact //! to the test impact analysis data as otherwise querying/retrieving test impact analysis data for the same source albeit //! with different path separators will be considered different files entirely. class RepoPath - : public AZ::IO::Path { public: + using string_type = AZ::IO::Path::string_type; + using string_view_type = AZ::IO::Path::string_view_type; + using value_type = AZ::IO::Path::value_type; + constexpr RepoPath() = default; constexpr RepoPath(const RepoPath&) = default; constexpr RepoPath(RepoPath&&) noexcept = default; - constexpr RepoPath(const string_type&) noexcept; - constexpr RepoPath(const value_type*) noexcept; - constexpr RepoPath(const AZ::IO::PathView&); - constexpr RepoPath(const AZ::IO::Path&); + constexpr RepoPath::RepoPath(const string_type & path) noexcept; + constexpr RepoPath::RepoPath(const string_view_type& path) noexcept; + constexpr RepoPath::RepoPath(const value_type* path) noexcept; + constexpr RepoPath::RepoPath(const AZ::IO::PathView& path); + constexpr RepoPath::RepoPath(const AZ::IO::Path& path); RepoPath& operator=(const RepoPath&) noexcept = default; RepoPath& operator=(const string_type&) noexcept; RepoPath& operator=(const value_type*) noexcept; RepoPath& operator=(const AZ::IO::Path& str) noexcept; - using AZ::IO::Path::operator AZ::IO::PathView; + const char* c_str() const { return m_path.c_str(); } + AZStd::string String() const { return m_path.String(); } + constexpr AZ::IO::PathView Stem() const { return m_path.Stem(); } + constexpr AZ::IO::PathView Extension() const { return m_path.Extension(); } + constexpr bool empty() const { return m_path.empty(); } + constexpr AZ::IO::PathView ParentPath() const { return m_path.ParentPath(); } + constexpr AZ::IO::PathView Filename() const { return m_path.Filename(); } + AZ::IO::Path LexicallyRelative(const RepoPath& base) const { return m_path.LexicallyRelative(base.m_path); } + [[nodiscard]] bool IsRelativeTo(const RepoPath& base) const { return m_path.IsRelativeTo(base.m_path); } + constexpr AZ::IO::PathView RootName() const { return m_path.RootName(); } + constexpr AZ::IO::PathView RelativePath() const { return m_path.RelativePath(); } + + friend RepoPath operator/(const RepoPath& lhs, const AZ::IO::PathView& rhs); + friend RepoPath operator/(const RepoPath& lhs, AZStd::string_view rhs); + friend RepoPath operator/(const RepoPath& lhs, const typename value_type* rhs); + friend RepoPath operator/(const RepoPath& lhs, const RepoPath& rhs); + + RepoPath& operator/=(const AZ::IO::PathView& rhs); + RepoPath& operator/=(AZStd::string_view rhs); + RepoPath& operator/=(const typename value_type* rhs); + RepoPath& operator/=(const RepoPath& rhs); + + friend bool operator==(const RepoPath& lhs, const RepoPath& rhs) noexcept; + friend bool operator!=(const RepoPath& lhs, const RepoPath& rhs) noexcept; + friend bool operator<(const RepoPath& lhs, const RepoPath& rhs) noexcept; + + private: + AZ::IO::Path m_path; }; + + constexpr RepoPath::RepoPath(const string_type& path) noexcept + : m_path(AZ::IO::Path(path).MakePreferred()) + { + } + + constexpr RepoPath::RepoPath(const string_view_type& path) noexcept + : m_path(AZ::IO::Path(path).MakePreferred()) + { + } + + constexpr RepoPath::RepoPath(const value_type* path) noexcept + : m_path(AZ::IO::Path(path).MakePreferred()) + { + } + + constexpr RepoPath::RepoPath(const AZ::IO::PathView& path) + : m_path(AZ::IO::Path(path).MakePreferred()) + { + } + + constexpr RepoPath::RepoPath(const AZ::IO::Path& path) + : m_path(AZ::IO::Path(path).MakePreferred()) + { + } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h index 8221eba834..31b2d1edce 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h @@ -29,9 +29,12 @@ namespace TestImpact { + class ChangeDependencyList; class DynamicDependencyMap; + class TestSelectorAndPrioritizer; class TestEngine; class TestTarget; + class SourceCoveringTestsList; //! Callback for a test sequence that isn't using test impact analysis to determine selected tests. //! @param tests The tests that will be run for this sequence. @@ -66,16 +69,22 @@ namespace TestImpact //! Callback for end of a test sequence. //! @param failureReport The test runs that failed for any reason during this sequence. //! @param duration The total duration of this test sequence. - using TestSequenceCompleteCallback = AZStd::function; + using TestSequenceCompleteCallback = AZStd::function; //! Callback for end of a test impact analysis test sequence. - //! @param failureReport The test runs that failed for any reason during this sequence. + //! @param selectedFailureReport The selected test runs that failed for any reason during this sequence. + //! @param discardedFailureReport The discarded test runs that failed for any reason during this sequence. //! @param duration The total duration of this test sequence. - using ImpactAnalysisTestSequenceCompleteCallback = AZStd::function; + using SafeTestSequenceCompleteCallback = AZStd::function; //! Callback for test runs that have completed for any reason. - //! test The test that has completed. - using TestCompleteCallback = AZStd::function; + //! selectedTests The test that has completed. + using TestRunCompleteCallback = AZStd::function; //! The API exposed to the client responsible for all test runs and persistent data management. class Runtime @@ -95,7 +104,7 @@ namespace TestImpact Policy::TestFailure testFailurePolicy, Policy::IntegrityFailure integrationFailurePolicy, Policy::TestSharding testShardingPolicy, - TargetOutputCapture targetOutputCapture, + Policy::TargetOutputCapture targetOutputCapture, AZStd::optional maxConcurrency = AZStd::nullopt); ~Runtime(); @@ -103,78 +112,110 @@ namespace TestImpact //! Runs a test sequence where all tests with a matching suite in the suite filter and also not on the excluded list are selected. //! @param suitesFilter The test suites that will be included in the test selection. //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). - //! @param testTargetTimeout The maximum duration the entire test sequence may run for (infinite if empty). + //! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty). //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. //! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed. //! @param testRunCompleteCallback The client function to be called after an individual test run has completed. + //! @returns TestSequenceResult RegularTestSequence( const AZStd::unordered_set suitesFilter, AZStd::optional testTargetTimeout, AZStd::optional globalTimeout, AZStd::optional testSequenceStartCallback, AZStd::optional testSequenceCompleteCallback, - AZStd::optional testRunCompleteCallback); + AZStd::optional testRunCompleteCallback); //! Runs a test sequence where tests are selected according to test impact analysis so long as they are not on the excluded list. //! @param changeList The change list used to determine the tests to select. //! @param testPrioritizationPolicy Determines how selected tests will be prioritized. //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). - //! @param testTargetTimeout The maximum duration the entire test sequence may run for (infinite if empty). + //! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty). //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. //! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed. //! @param testRunCompleteCallback The client function to be called after an individual test run has completed. + //! @returns TestSequenceResult ImpactAnalysisTestSequence( const ChangeList& changeList, Policy::TestPrioritization testPrioritizationPolicy, AZStd::optional testTargetTimeout, AZStd::optional globalTimeout, AZStd::optional testSequenceStartCallback, - AZStd::optional testSequenceCompleteCallback, - AZStd::optional testRunCompleteCallback); + AZStd::optional testSequenceCompleteCallback, + AZStd::optional testRunCompleteCallback); //! Runs a test sequence as per the ImpactAnalysisTestSequence where the tests not selected are also run (albeit without instrumentation). //! @param changeList The change list used to determine the tests to select. //! @param suitesFilter The test suites that will be included in the test selection. //! @param testPrioritizationPolicy Determines how selected tests will be prioritized. //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). - //! @param testTargetTimeout The maximum duration the entire test sequence may run for (infinite if empty). + //! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty). //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. //! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed. //! @param testRunCompleteCallback The client function to be called after an individual test run has completed. - TestSequenceResult SafeImpactAnalysisTestSequence( + //! @returns + AZStd::pair SafeImpactAnalysisTestSequence( const ChangeList& changeList, const AZStd::unordered_set suitesFilter, Policy::TestPrioritization testPrioritizationPolicy, AZStd::optional testTargetTimeout, AZStd::optional globalTimeout, AZStd::optional testSequenceStartCallback, - AZStd::optional testSequenceCompleteCallback, - AZStd::optional testRunCompleteCallback); + AZStd::optional testSequenceCompleteCallback, + AZStd::optional testRunCompleteCallback); //! Runs all tests not on the excluded list and uses their coverage data to seed the test impact analysis data (ant existing data will be overwritten). - //! @param testTargetTimeout The maximum duration the entire test sequence may run for (infinite if empty). + //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). + //! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty). //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. //! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed. //! @param testRunCompleteCallback The client function to be called after an individual test run has completed. + //! TestSequenceResult SeededTestSequence( + AZStd::optional testTargetTimeout, AZStd::optional globalTimeout, AZStd::optional testSequenceStartCallback, AZStd::optional testSequenceCompleteCallback, - AZStd::optional testRunCompleteCallback); + AZStd::optional testRunCompleteCallback); //! Returns true if the runtime has test impact analysis data (either preexisting or generated). bool HasImpactAnalysisData() const; private: + //! Updates the test enumeration cache for test targets that had sources modified by a given change list. + //! @param changeDependencyList The resolved change dependency list generated for the change list. + void EnumerateMutatedTestTargets(const ChangeDependencyList& changeDependencyList); + + //! Selects the test targets covering a given change list and updates the enumeration cache of the test targets with sources + //! modified in that change list. + //! @param changeList The change list for which the covering tests and enumeration cache updates will be generated for. + //! @param testPrioritizationPolicy The test prioritization strategy to use for the selected test targets. + //! @returns The pair of selected test targets and discarded test targets. + AZStd::pair, AZStd::vector> SelectCoveringTestTargetsAndUpdateEnumerationCache( + const ChangeList& changeList, + Policy::TestPrioritization testPrioritizationPolicy); + + //! Selects the test targets from the specified list of test targets that are not on the test target exclusion list. + //! @param testTargets The list of test targets to select from. + //! @returns The subset of test targets in the specified list that are not on the target exclude list. + AZStd::pair, AZStd::vector> SelectTestTargetsByExcludeList( + AZStd::vector testTargets) const; + + //! Prepares the dynamic dependency map for a seed update by clearing all existing data and deleting the file that will be serialized. + void ClearDynamicDependencyMapAndRemoveExistingFile(); + + //! Updates the dynamic dependency map and serializes the entire map to disk. + void UpdateAndSerializeDynamicDependencyMap(const SourceCoveringTestsList& sourceCoverageTestsList); + RuntimeConfig m_config; Policy::ExecutionFailure m_executionFailurePolicy; Policy::ExecutionFailureDrafting m_executionFailureDraftingPolicy; Policy::TestFailure m_testFailurePolicy; Policy::IntegrityFailure m_integrationFailurePolicy; Policy::TestSharding m_testShardingPolicy; - TargetOutputCapture m_targetOutputCapture; + Policy::TargetOutputCapture m_targetOutputCapture; size_t m_maxConcurrency = 0; AZStd::unique_ptr m_dynamicDependencyMap; + AZStd::unique_ptr m_testSelectorAndPrioritizer; AZStd::unique_ptr m_testEngine; AZStd::unordered_set m_testTargetExcludeList; AZStd::unordered_set m_testTargetShardList; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h index 751db43987..594c997c45 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h @@ -61,16 +61,16 @@ namespace TestImpact Never, //!< Do not shard any test targets. Always //!< Shard all test targets that have been marked for test sharding. }; - } - //! Standard output capture of test target runs. - enum class TargetOutputCapture - { - None, //!< Do not capture any output. - StdOut, //!< Send captured output to standard output - File, //!< Write captured output to file. - StdOutAndFile //!< Send captured output to standard output and write to file. - }; + //! Standard output capture of test target runs. + enum class TargetOutputCapture + { + None, //!< Do not capture any output. + StdOut, //!< Send captured output to standard output + File, //!< Write captured output to file. + StdOutAndFile //!< Send captured output to standard output and write to file. + }; + } //! Configuration for test targets that opt in to test sharding. enum class ShardConfiguration diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp index 31b3485c70..919c10d049 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp @@ -34,7 +34,7 @@ namespace TestImpact else { // This is a new entry on the dependency map so create an entry with this parent target and no covering targets - m_sourceDependencyMap.emplace(source, DependencyData{ {target}, {} }); + m_sourceDependencyMap.emplace(source.String(), DependencyData{ {target}, {} }); } } @@ -56,6 +56,7 @@ namespace TestImpact for (const auto& target : m_testTargets.GetTargets()) { mapBuildTargetSources(&target); + m_testTargetSourceCoverageCount[&target] = 0; } } @@ -142,10 +143,21 @@ namespace TestImpact DependencyException, AZStd::string::format("Couldn't replace source coverage for %s, source file is an autogen input file", sourceCoverage.GetPath().c_str()).c_str()); - auto [it, inserted] = m_sourceDependencyMap.insert(sourceCoverage.GetPath().String()); - auto& [key, sourceDependency] = *it; + auto [sourceDependencyIt, inserted] = m_sourceDependencyMap.insert(sourceCoverage.GetPath().String()); + auto& [key, sourceDependency] = *sourceDependencyIt; - // Clear any existing coverage for the delta + // Knock down the source coverage count for the test targets and clear any existing coverage for the delta + for (const auto& testTarget : sourceDependency.m_coveringTestTargets) + { + if (auto coveringTestTargetIt = m_testTargetSourceCoverageCount.find(testTarget); + coveringTestTargetIt != m_testTargetSourceCoverageCount.end()) + { + if (coveringTestTargetIt->second > 0) + { + coveringTestTargetIt->second--; + } + } + } sourceDependency.m_coveringTestTargets.clear(); // Update the dependency with any new coverage data @@ -157,10 +169,13 @@ namespace TestImpact // Source to covering test target mapping sourceDependency.m_coveringTestTargets.insert(testTarget); + // Test target covering sources count + m_testTargetSourceCoverageCount[testTarget]++; + // Build target to covering test target mapping for (const auto& parentTarget : sourceDependency.m_parentTargets) - { - m_buildTargetCoverage[parentTarget.GetBuildTarget()].insert(testTarget); + { + m_buildTargetCoverage[parentTarget.GetBuildTarget()].insert(testTarget); } } else @@ -173,7 +188,7 @@ namespace TestImpact // If the new coverage data results in a parentless and coverageless entry, consider it a dead entry and remove accordingly if (sourceDependency.m_coveringTestTargets.empty() && sourceDependency.m_parentTargets.empty()) { - m_sourceDependencyMap.erase(it); + m_sourceDependencyMap.erase(sourceDependencyIt); } } } @@ -198,6 +213,14 @@ namespace TestImpact } } + void DynamicDependencyMap::ClearAllSourceCoverage() + { + for (const auto& [path, coverage] : m_sourceDependencyMap) + { + ReplaceSourceCoverage(SourceCoveringTestsList(AZStd::vector{ SourceCoveringTests(RepoPath(path)) })); + } + } + const ProductionTargetList& DynamicDependencyMap::GetProductionTargetList() const { return m_productionTargets; @@ -405,4 +428,33 @@ namespace TestImpact return ChangeDependencyList(AZStd::move(createDependencies), AZStd::move(updateDependencies), AZStd::move(deleteDependencies)); } + + AZStd::vector DynamicDependencyMap::GetCoveringTests() const + { + AZStd::vector covering; + for (const auto& [testTarget, coveringSources] : m_testTargetSourceCoverageCount) + { + if (coveringSources > 0) + { + covering.push_back(testTarget); + } + } + + return covering; + } + + AZStd::vector DynamicDependencyMap::GetNotCoveringTests() const + { + AZStd::vector notCovering; + for(const auto& [testTarget, coveringSources] : m_testTargetSourceCoverageCount) + { + if(coveringSources == 0) + { + notCovering.push_back(testTarget); + } + } + + return notCovering; + } + } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h index 6faef76452..f209e22823 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h @@ -85,18 +85,27 @@ namespace TestImpact //! @param sourceCoverageDelta The source coverage delta to replace in the dependency map. void ReplaceSourceCoverage(const SourceCoveringTestsList& sourceCoverageDelta); + //! Clears all of the existing source coverage in the dependency map. + void ClearAllSourceCoverage(); + //! Exports the coverage of all sources in the dependency map. SourceCoveringTestsList ExportSourceCoverage() const; //! Gets the list of orphaned source files in the dependency map that have coverage data but belong to no parent build targets. AZStd::vector GetOrphanSourceFiles() const; - //! Applies the specified change list to the dynamic dependency map and resolves the change list to a change dependency list + //! Applies the specified change list to the dependency map and resolves the change list to a change dependency list //! containing the updated source dependencies for each source file in the change list. //! @param changeList The change list to apply and resolve. //! @returns The change list as resolved to the appropriate source dependencies. [[nodiscard]] ChangeDependencyList ApplyAndResoveChangeList(const ChangeList& changeList); + //! Returns the test targets that cover one or more sources in the repository. + AZStd::vector GetCoveringTests() const; + + //! Returns the test targets that do not cover any sources in the repository. + AZStd::vector GetNotCoveringTests() const; + private: //! Clears the source coverage of the specified sources. //! @note The covering targets for the parent test target(s) will not be pruned if those covering targets are removed. @@ -116,5 +125,8 @@ namespace TestImpact //! Mapping of autogen input sources to their generated output sources. AZStd::unordered_map> m_autogenInputToOutputMap; + + //! Number of sources that each test target in the repository covers. + AZStd::unordered_map m_testTargetSourceCoverageCount; }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp index 101bdf862c..ce8bf64678 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp @@ -65,7 +65,7 @@ namespace TestImpact { AZStd::sort(m_coverage.begin(), m_coverage.end(), [](const SourceCoveringTests& lhs, const SourceCoveringTests& rhs) { - return lhs.GetPath() < rhs.GetPath(); + return lhs.GetPath().String() < rhs.GetPath().String(); }); } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsSerializer.cpp new file mode 100644 index 0000000000..7b89f3bda9 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsSerializer.cpp @@ -0,0 +1,93 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include +#include + +namespace TestImpact +{ + // Tag used to indicate that a given line is the name o a covering test target + constexpr char TargetTag = '-'; + + AZStd::string SerializeSourceCoveringTestsList(const SourceCoveringTestsList& sourceCoveringTestsList) + { + AZStd::string output; + output.reserve(1U << 24); // Reserve approx. 16Mib as the outputs can be quite large + + for (const auto& source : sourceCoveringTestsList.GetCoverage()) + { + // Source file + output += source.GetPath().String(); + output += "\n"; + + // Covering test targets + for (const auto& testTarget : source.GetCoveringTestTargets()) + { + output += AZStd::string::format("%c%s\n", TargetTag, testTarget.c_str()); + } + } + + // Add the newline so the deserializer can properly terminate on the last read line + output += "\n"; + + return output; + } + + SourceCoveringTestsList DeserializeSourceCoveringTestsList(const AZStd::string& sourceCoveringTestsListString) + { + AZStd::vector sourceCoveringTests; + AZStd::string source; + AZStd::vector coveringTests; + sourceCoveringTests.reserve(1U << 16); // Reserve for approx. 65k source files + const AZStd::string delim = "\n"; + auto start = 0U; + auto end = sourceCoveringTestsListString.find(delim); + + while (end != AZStd::string::npos) + { + const auto line = sourceCoveringTestsListString.substr(start, end - start); + if (line.starts_with(TargetTag)) + { + // This is a test target covering the most recent source discovered + coveringTests.push_back(line.substr(1, line.length() - 1)); + } + else + { + // This is a new source file so assign the accumulated test targets to the current source file before proceeding + if (!coveringTests.empty()) + { + sourceCoveringTests.push_back(SourceCoveringTests(source, AZStd::move(coveringTests))); + coveringTests.clear(); + } + + source = line; + } + + start = end + delim.length(); + end = sourceCoveringTestsListString.find(delim, start); + } + + // Ensure we properly assign the accumulated test targets to the most recent source discovered + if (!coveringTests.empty()) + { + sourceCoveringTests.push_back(SourceCoveringTests(source, AZStd::move(coveringTests))); + coveringTests.clear(); + } + + return SourceCoveringTestsList(AZStd::move(sourceCoveringTests)); + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsSerializer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsSerializer.h new file mode 100644 index 0000000000..901e43233c --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsSerializer.h @@ -0,0 +1,26 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +namespace TestImpact +{ + //! Serializes the specified source covering tests list to plain text format. + AZStd::string SerializeSourceCoveringTestsList(const SourceCoveringTestsList& sourceCoveringTestsList); + + //! Deserializes a source covering tests list from the specified source covering tests data in plain text format. + SourceCoveringTestsList DeserializeSourceCoveringTestsList(const AZStd::string& sourceCoveringTestsListString); +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp index 549ef0c108..35e6679fdc 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp @@ -25,7 +25,7 @@ namespace TestImpact } AZStd::vector TestSelectorAndPrioritizer::SelectTestTargets( - const ChangeDependencyList& changeDependencyList, TestSelectionStrategy testSelectionStrategy) + const ChangeDependencyList& changeDependencyList, Policy::TestPrioritization testSelectionStrategy) { const auto selectedTestTargetAndDependerMap = SelectTestTargets(changeDependencyList); const auto prioritizedSelectedTests = PrioritizeSelectedTestTargets(selectedTestTargetAndDependerMap, testSelectionStrategy); @@ -210,7 +210,7 @@ namespace TestImpact AZStd::vector TestSelectorAndPrioritizer::PrioritizeSelectedTestTargets( const SelectedTestTargetAndDependerMap& selectedTestTargetAndDependerMap, - [[maybe_unused]]TestSelectionStrategy testSelectionStrategy) + [[maybe_unused]] Policy::TestPrioritization testSelectionStrategy) { AZStd::vector selectedTestTargets; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.h index c742176073..6ee3a616c4 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactTestSelectorAndPrioritizer.h @@ -12,6 +12,8 @@ #pragma once +#include + #include #include @@ -25,13 +27,6 @@ namespace TestImpact class BuildTarget; class TestTarget; - //! Strategy for selecting tests given a set of source changes. - enum class TestSelectionStrategy : bool - { - SelectOnly, //!< Select tests only, do not attempt prioritization of those selected tests. - SelectAndPriotitize //!< Select tests and prioritize according to dependency graph locality of coverer and coveree. - }; - //! Map of build targets and their dependency graph data. //! For test targets, the dependency graph data is that of the build targets which the test target depends on. //! For production targets, the dependency graph is that of the build targets that depend on it (dependers). @@ -52,7 +47,7 @@ namespace TestImpact //! Select the covering test targets for the given set of source changes and optionally prioritizes said test selection. //! @param changeDependencyList The resolved list of source dependencies for the CRUD source changes. //! @param testSelectionStrategy The test selection and prioritization strategy to apply to the given CRUD source changes. - AZStd::vector SelectTestTargets(const ChangeDependencyList& changeDependencyList, TestSelectionStrategy testSelectionStrategy); + AZStd::vector SelectTestTargets(const ChangeDependencyList& changeDependencyList, Policy::TestPrioritization testSelectionStrategy); private: //! Map of selected test targets and the production targets they cover for the given set of source changes. @@ -69,7 +64,7 @@ namespace TestImpact //! @param testSelectionStrategy The test selection strategy to prioritize the selected tests. //! @returns The selected tests either in either arbitrary order or in prioritized with highest priority first. AZStd::vector PrioritizeSelectedTestTargets( - const SelectedTestTargetAndDependerMap& selectedTestTargetAndDependerMap, TestSelectionStrategy testSelectionStrategy); + const SelectedTestTargetAndDependerMap& selectedTestTargetAndDependerMap, Policy::TestPrioritization testSelectionStrategy); const DynamicDependencyMap* m_dynamicDependencyMap; DependencyGraphDataMap m_dependencyGraphDataMap; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/TestImpactWin32_TestEngineJobFailure.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/TestImpactWin32_TestEngineJobFailure.cpp new file mode 100644 index 0000000000..578130882d --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/TestImpactWin32_TestEngineJobFailure.cpp @@ -0,0 +1,35 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + // Known error codes for test instrumentation + namespace ErrorCodes + { + namespace OpenCppCoverage + { + static constexpr ReturnCode InvalidArgs = 0x9F8C8E5C; + } + } + + AZStd::optional CheckForKnownTestInstrumentErrorCode(ReturnCode returnCode) + { + if (returnCode == ErrorCodes::OpenCppCoverage::InvalidArgs) + { + return Client::TestRunResult::FailedToExecute; + } + + return AZStd::nullopt; + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.cpp new file mode 100644 index 0000000000..debf1406f2 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.cpp @@ -0,0 +1,82 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + // Known error codes for test runner and test library + namespace ErrorCodes + { + namespace GTest + { + static constexpr ReturnCode Unsuccessful = 1; + } + + namespace AZTestRunner + { + static constexpr ReturnCode InvalidArgs = 101; + static constexpr ReturnCode FailedToFindTargetBinary = 102; + static constexpr ReturnCode SymbolNotFound = 103; + static constexpr ReturnCode ModuleSkipped = 104; + } + } + + AZStd::optional CheckForKnownTestRunnerErrorCode(int returnCode) + { + switch (returnCode) + { + // We will consider test targets that technically execute but their launcher or unit test library return a know error + // code that pertains to incorrect argument usage as test targets that failed to execute + case ErrorCodes::AZTestRunner::InvalidArgs: + case ErrorCodes::AZTestRunner::FailedToFindTargetBinary: + case ErrorCodes::AZTestRunner::ModuleSkipped: + case ErrorCodes::AZTestRunner::SymbolNotFound: + return Client::TestRunResult::FailedToExecute; + default: + return AZStd::nullopt; + } + } + + AZStd::optional CheckForKnownTestLibraryErrorCode(int returnCode) + { + if (returnCode == ErrorCodes::GTest::Unsuccessful) + { + return Client::TestRunResult::TestFailures; + } + + return AZStd::nullopt; + } + + AZStd::optional CheckForAnyKnownErrorCode(ReturnCode returnCode) + { + if (const auto result = CheckForKnownTestInstrumentErrorCode(returnCode); + result != AZStd::nullopt) + { + return result.value(); + } + + if (const auto result = CheckForKnownTestRunnerErrorCode(returnCode); + result != AZStd::nullopt) + { + return result.value(); + } + + if (const auto result = CheckForKnownTestLibraryErrorCode(returnCode); + result != AZStd::nullopt) + { + return result.value(); + } + + return AZStd::nullopt; + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h new file mode 100644 index 0000000000..aef4e962c5 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h @@ -0,0 +1,29 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +#include + +namespace TestImpact +{ + AZStd::optional CheckForKnownTestInstrumentErrorCode(ReturnCode returnCode); + AZStd::optional CheckForKnownTestRunnerErrorCode(ReturnCode returnCode); + AZStd::optional CheckForKnownTestLibraryErrorCode(ReturnCode returnCode); + + AZStd::optional CheckForAnyKnownErrorCode(ReturnCode returnCode); + +} From de38e5ad35895caff54edf0152cf9cc0d438bc42 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 11:46:18 +0100 Subject: [PATCH 059/233] Add missing API comments --- .../Code/Source/TestEngine/TestImpactTestEngineJobFailure.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h index aef4e962c5..8e88571957 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h @@ -20,10 +20,16 @@ namespace TestImpact { + //! Checks for known test instrumentation error return codes and returns the corresponding client test run result or empty. AZStd::optional CheckForKnownTestInstrumentErrorCode(ReturnCode returnCode); + + //! Checks for known test runner error return codes and returns the corresponding client test run result or empty. AZStd::optional CheckForKnownTestRunnerErrorCode(ReturnCode returnCode); + + //! Checks for known test library error return codes and returns the corresponding client test run result or empty. AZStd::optional CheckForKnownTestLibraryErrorCode(ReturnCode returnCode); + //! Checks for all known error return codes and returns the corresponding client test run result or empty. AZStd::optional CheckForAnyKnownErrorCode(ReturnCode returnCode); } From a91e9caaf9861d7b99eee717f9c09ba8ebb7775e Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 12:09:28 +0100 Subject: [PATCH 060/233] Add missing API comment to DeleteFile --- .../Code/Include/TestImpactFramework/TestImpactFileUtils.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFileUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFileUtils.h index aa64840e78..ffecd27c23 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFileUtils.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactFileUtils.h @@ -77,8 +77,9 @@ namespace TestImpact }); } - inline void DeleteFile(const RepoPath& path) + //! Deletes the specified file. + inline void DeleteFile(const RepoPath& file) { - DeleteFiles(path.ParentPath(), path.Filename().Native()); + DeleteFiles(file.ParentPath(), file.Filename().Native()); } } // namespace TestImpact From c3ea2d18baf38dc8a3fb64b22f9c8d06502254fb Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 12:15:19 +0100 Subject: [PATCH 061/233] Remove extra whitespace --- .../Code/Source/TestEngine/TestImpactTestEngineJobFailure.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h index 8e88571957..799e8fc718 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h @@ -31,5 +31,4 @@ namespace TestImpact //! Checks for all known error return codes and returns the corresponding client test run result or empty. AZStd::optional CheckForAnyKnownErrorCode(ReturnCode returnCode); - } From 8965061b9b6f890f3bfc60a9bb9886f801e1a4ac Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 13:21:11 +0100 Subject: [PATCH 062/233] Address PR comments --- .../TestImpactFramework/TestImpactClientFailureReport.h | 2 +- .../TestImpactFramework/TestImpactClientTestSelection.h | 2 +- .../Code/Include/TestImpactFramework/TestImpactRepoPath.h | 4 ++-- .../Code/Include/TestImpactFramework/TestImpactRuntime.h | 2 +- .../Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp | 4 ++-- .../Dependency/TestImpactSourceCoveringTestsSerializer.cpp | 2 +- .../TestEngine/TestImpactWin32_TestEngineJobFailure.cpp | 2 +- .../Code/Source/TestEngine/TestImpactTestEngineJobFailure.cpp | 2 +- .../Code/Source/TestEngine/TestImpactTestEngineJobFailure.h | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h index 19e98a7d1f..0a482e8c51 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientFailureReport.h @@ -113,7 +113,7 @@ namespace TestImpact //! Returns the test targets that contain failing tests. const AZStd::vector& GetTestRunFailures() const; - //! Returns the test targets in this sequence that were terminated for exceeding their allotted flight time. + //! Returns the test targets in this sequence that were terminated for exceeding their allotted runtime. const AZStd::vector& GetTimedOutTests() const; //! Returns the test targets in this sequence that were not executed due to the sequence terminating prematurely. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h index 76e6461206..7720bbc5ad 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientTestSelection.h @@ -40,7 +40,7 @@ namespace TestImpact //! Returns the number of selected test runs that will not be run. size_t GetNumExcludedTestRuns() const; - //! Returns the total number of tests runs selected regardless of whether or not they will actually be run. + //! Returns the total number of test runs selected regardless of whether or not they will actually be run. size_t GetTotalNumTests() const; private: diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h index 80f82e4785..9ac457e428 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h @@ -30,7 +30,7 @@ namespace TestImpact constexpr RepoPath() = default; constexpr RepoPath(const RepoPath&) = default; constexpr RepoPath(RepoPath&&) noexcept = default; - constexpr RepoPath::RepoPath(const string_type & path) noexcept; + constexpr RepoPath::RepoPath(const string_type& path) noexcept; constexpr RepoPath::RepoPath(const string_view_type& path) noexcept; constexpr RepoPath::RepoPath(const value_type* path) noexcept; constexpr RepoPath::RepoPath(const AZ::IO::PathView& path); @@ -53,11 +53,11 @@ namespace TestImpact constexpr AZ::IO::PathView RootName() const { return m_path.RootName(); } constexpr AZ::IO::PathView RelativePath() const { return m_path.RelativePath(); } + // Wrappers around the AZ::IO::Path concatenation operator friend RepoPath operator/(const RepoPath& lhs, const AZ::IO::PathView& rhs); friend RepoPath operator/(const RepoPath& lhs, AZStd::string_view rhs); friend RepoPath operator/(const RepoPath& lhs, const typename value_type* rhs); friend RepoPath operator/(const RepoPath& lhs, const RepoPath& rhs); - RepoPath& operator/=(const AZ::IO::PathView& rhs); RepoPath& operator/=(AZStd::string_view rhs); RepoPath& operator/=(const typename value_type* rhs); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h index 31b2d1edce..266bd5ef7a 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h @@ -83,7 +83,7 @@ namespace TestImpact AZStd::chrono::milliseconds duration)>; //! Callback for test runs that have completed for any reason. - //! selectedTests The test that has completed. + //! @param selectedTests The test that has completed. using TestRunCompleteCallback = AZStd::function; //! The API exposed to the client responsible for all test runs and persistent data management. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp index 919c10d049..826fa0a87d 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp @@ -174,8 +174,8 @@ namespace TestImpact // Build target to covering test target mapping for (const auto& parentTarget : sourceDependency.m_parentTargets) - { - m_buildTargetCoverage[parentTarget.GetBuildTarget()].insert(testTarget); + { + m_buildTargetCoverage[parentTarget.GetBuildTarget()].insert(testTarget); } } else diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsSerializer.cpp index 7b89f3bda9..5143f9b73c 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsSerializer.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsSerializer.cpp @@ -20,7 +20,7 @@ namespace TestImpact { - // Tag used to indicate that a given line is the name o a covering test target + // Tag used to indicate whether a given line is the name or a covering test target constexpr char TargetTag = '-'; AZStd::string SerializeSourceCoveringTestsList(const SourceCoveringTestsList& sourceCoveringTestsList) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/TestImpactWin32_TestEngineJobFailure.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/TestImpactWin32_TestEngineJobFailure.cpp index 578130882d..6561293d5a 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/TestImpactWin32_TestEngineJobFailure.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/TestImpactWin32_TestEngineJobFailure.cpp @@ -32,4 +32,4 @@ namespace TestImpact return AZStd::nullopt; } -} +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.cpp index debf1406f2..931e3a272b 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.cpp @@ -79,4 +79,4 @@ namespace TestImpact return AZStd::nullopt; } -} +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h index 799e8fc718..969aad9c84 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngineJobFailure.h @@ -31,4 +31,4 @@ namespace TestImpact //! Checks for all known error return codes and returns the corresponding client test run result or empty. AZStd::optional CheckForAnyKnownErrorCode(ReturnCode returnCode); -} +} // namespace TestImpact From 52e61fa3cac8b7bae1ae78019dc3306895669cb7 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 13:24:48 +0100 Subject: [PATCH 063/233] Remove JobExceptionPolicy and move knoen error handling --- .../TestImpactWin32_TestTargetExtension.cpp | 2 +- .../Enumeration/TestImpactTestEnumerator.cpp | 103 ++++++++---------- .../Enumeration/TestImpactTestEnumerator.h | 14 --- .../TestImpactTestJobInfoGenerator.cpp | 12 +- .../JobRunner/TestImpactTestJobRunner.h | 37 +------ .../Run/TestImpactInstrumentedTestRunner.cpp | 4 +- .../Run/TestImpactInstrumentedTestRunner.h | 1 - .../TestEngine/Run/TestImpactTestRunner.cpp | 4 +- .../TestEngine/Run/TestImpactTestRunner.h | 1 - .../TestEngine/TestImpactTestEngine.cpp | 91 +++++----------- .../Source/TestEngine/TestImpactTestEngine.h | 1 + 11 files changed, 85 insertions(+), 185 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp index 12c78f91cf..14a1a28ba7 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp @@ -41,4 +41,4 @@ namespace TestImpact } } } -} +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp index f7b6d4a264..afc40036e3 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp @@ -10,7 +10,7 @@ * */ -#include +#include #include #include @@ -22,35 +22,6 @@ namespace TestImpact { - namespace - { - void WriteCacheFile( - const TestEnumeration& enumeration, const RepoPath& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy) - { - const AZStd::string cacheJSON = SerializeTestEnumeration(enumeration); - const AZStd::vector cacheBytes(cacheJSON.begin(), cacheJSON.end()); - AZ::IO::SystemFile cacheFile; - - if (!cacheFile.Open( - path.c_str(), - AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY)) - { - AZ_TestImpact_Eval( - !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheWriteFailure), TestEngineException, - "Couldn't open cache file for writing"); - return; - } - - if (cacheFile.Write(cacheBytes.data(), cacheBytes.size()) == 0) - { - AZ_TestImpact_Eval( - !IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheWriteFailure), TestEngineException, - "Couldn't write cache file data"); - return; - } - } - } // namespace - TestEnumeration ParseTestEnumerationFile(const RepoPath& enumerationFile) { return TestEnumeration(GTest::TestEnumerationSuitesFactory(ReadFileContents(enumerationFile))); @@ -79,8 +50,6 @@ namespace TestImpact AZStd::pair> TestEnumerator::Enumerate( const AZStd::vector& jobInfos, - CacheExceptionPolicy cacheExceptionPolicy, - JobExceptionPolicy jobExceptionPolicy, AZStd::optional enumerationTimeout, AZStd::optional enumeratorTimeout, AZStd::optional clientCallback) @@ -88,50 +57,67 @@ namespace TestImpact AZStd::vector cachedJobs; AZStd::vector jobQueue; - for (const auto& jobInfo : jobInfos) + for (auto jobInfo = jobInfos.begin(); jobInfo != jobInfos.end(); ++jobInfo) { // If this job has a cache read policy attempt to read the cache - if (jobInfo.GetCache().has_value() && jobInfo.GetCache()->m_policy == JobData::CachePolicy::Read) + if (jobInfo->GetCache().has_value()) { - JobMeta meta; - AZStd::optional enumeration; - - try - { - enumeration = TestEnumeration(DeserializeTestEnumeration(ReadFileContents(jobInfo.GetCache()->m_file))); - } - catch (const TestEngineException& e) + if (jobInfo->GetCache()->m_policy == JobData::CachePolicy::Read) { - AZ_Printf("Enumerate", "Enumeration cache error: %s", e.what()); - } + JobMeta meta; + AZStd::optional enumeration; - // Even though cached jobs don't get executed we still give the client the opportunity to handle the job state - // change in order to make the caching process transparent to the client - if (enumeration.has_value()) - { - if (m_clientJobCallback.has_value()) + try { - (*m_clientJobCallback)(jobInfo, meta); + enumeration = TestEnumeration(DeserializeTestEnumeration(ReadFileContents(jobInfo->GetCache()->m_file))); } + catch (const TestEngineException& e) + { + AZ_Printf("Enumerate", "Enumeration cache error: %s", e.what()); + DeleteFile(jobInfo->GetCache()->m_file); + } + + // Even though cached jobs don't get executed we still give the client the opportunity to handle the job state + // change in order to make the caching process transparent to the client + if (enumeration.has_value()) + { + // Cache read successfully, this job will not be placed in the job queue + cachedJobs.emplace_back(Job(*jobInfo, AZStd::move(meta), AZStd::move(enumeration))); - // Cache read successfully, this job will not be placed in the job queue - cachedJobs.emplace_back(Job(jobInfo, AZStd::move(meta), AZStd::move(enumeration))); + if (m_clientJobCallback.has_value() && (*m_clientJobCallback)(*jobInfo, meta) == ProcessCallbackResult::Abort) + { + // Client chose to abort so we will copy over the existing cache enumerations and fill the rest with blanks + AZStd::vector jobs(cachedJobs); + for (auto emptyJobInfo = ++jobInfo; emptyJobInfo != jobInfos.end(); ++emptyJobInfo) + { + jobs.emplace_back(Job(*emptyJobInfo, {}, AZStd::nullopt)); + } + + return { ProcessSchedulerResult::UserAborted, jobs }; + } + } + else + { + // The cache read failed and exception policy for cache read failures is not to throw so instead place this + // job in the job queue + jobQueue.emplace_back(*jobInfo); + } } else { - // The cache read failed and exception policy for cache read failures is not to throw so instead place this job in the - // job queue - jobQueue.emplace_back(jobInfo); + // This job has no cache read policy so delete the cache and place in job queue + DeleteFile(jobInfo->GetCache()->m_file); + jobQueue.emplace_back(*jobInfo); } } else { // This job has no cache read policy so place in job queue - jobQueue.emplace_back(jobInfo); + jobQueue.emplace_back(*jobInfo); } } - const auto payloadGenerator = [this, cacheExceptionPolicy](const JobDataMap& jobDataMap) + const auto payloadGenerator = [this](const JobDataMap& jobDataMap) { PayloadMap enumerations; for (const auto& [jobId, jobData] : jobDataMap) @@ -146,7 +132,7 @@ namespace TestImpact // Write out the enumeration to a cache file if we have a cache write policy for this job if (jobInfo->GetCache().has_value() && jobInfo->GetCache()->m_policy == JobData::CachePolicy::Write) { - WriteCacheFile(enumeration.value(), jobInfo->GetCache()->m_file, cacheExceptionPolicy); + WriteFileContents(SerializeTestEnumeration(enumeration.value()), jobInfo->GetCache()->m_file); } } catch (const Exception& e) @@ -163,7 +149,6 @@ namespace TestImpact // Generate the enumeration results for the jobs that weren't cached auto [result, jobs] = ExecuteJobs( jobQueue, - jobExceptionPolicy, payloadGenerator, StdOutputRouting::None, StdErrorRouting::None, diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h index c802b92529..196700998f 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.h @@ -51,16 +51,6 @@ namespace TestImpact AZStd::optional m_cache = AZStd::nullopt; //!< No caching takes place if cache is empty. }; - namespace Bitwise - { - //! Exception policy for test enumeration cache reads/writes. - enum class CacheExceptionPolicy - { - Never = 0, //! Never throw. - OnCacheWriteFailure = 1 //! Throw when a cache write policy is in place but the cache file could not be written. - }; - } // namespace Bitwise - //! Enumerate a batch of test targets to determine the test suites and fixtures they contain, caching the results where applicable. class TestEnumerator : public TestJobRunner @@ -68,8 +58,6 @@ namespace TestImpact using JobRunner = TestJobRunner; public: - using CacheExceptionPolicy = Bitwise::CacheExceptionPolicy; - //! Constructs a test enumerator with the specified parameters common to all enumeration job runs of this enumerator. //! @param maxConcurrentEnumerations The maximum number of enumerations to be in flight at any given time. explicit TestEnumerator(size_t maxConcurrentEnumerations); @@ -84,8 +72,6 @@ namespace TestImpact //! @return The result of the run sequence and the enumeration jobs with their associated test enumeration payloads. AZStd::pair> Enumerate( const AZStd::vector& jobInfos, - CacheExceptionPolicy cacheExceptionPolicy, - JobExceptionPolicy jobExceptionPolicy, AZStd::optional enumerationTimeout, AZStd::optional enumeratorTimeout, AZStd::optional clientCallback); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp index 7e8e65e940..356688a128 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp @@ -38,7 +38,7 @@ namespace TestImpact { return AZStd::string::format( "%s%s %s", - (m_targetBinaryDir / testTarget->GetOutputName()).c_str(), + (m_targetBinaryDir / RepoPath(testTarget->GetOutputName())).c_str(), GetTestTargetExtension(testTarget).c_str(), testTarget->GetCustomArgs().c_str()).c_str(); } @@ -47,7 +47,7 @@ namespace TestImpact return AZStd::string::format( "\"%s\" \"%s%s\" %s", m_testRunnerBinary.c_str(), - (m_targetBinaryDir / testTarget->GetOutputName()).c_str(), + (m_targetBinaryDir / RepoPath(testTarget->GetOutputName())).c_str(), GetTestTargetExtension(testTarget).c_str(), testTarget->GetCustomArgs().c_str()).c_str(); } @@ -55,22 +55,22 @@ namespace TestImpact RepoPath TestJobInfoGenerator::GenerateTargetEnumerationCacheFilePath(const TestTarget* testTarget) const { - return AZStd::string::format("%s.cache", (m_artifactDir / testTarget->GetName()).c_str()); + return AZStd::string::format("%s.cache", (m_cacheDir / RepoPath(testTarget->GetName())).c_str()); } RepoPath TestJobInfoGenerator::GenerateTargetEnumerationArtifactFilePath(const TestTarget* testTarget) const { - return AZStd::string::format("%s.Enumeration.xml", (m_artifactDir / testTarget->GetName()).c_str()); + return AZStd::string::format("%s.Enumeration.xml", (m_artifactDir / RepoPath(testTarget->GetName())).c_str()); } RepoPath TestJobInfoGenerator::GenerateTargetRunArtifactFilePath(const TestTarget* testTarget) const { - return AZStd::string::format("%s.Run.xml", (m_artifactDir / testTarget->GetName()).c_str()); + return AZStd::string::format("%s.Run.xml", (m_artifactDir / RepoPath(testTarget->GetName())).c_str()); } RepoPath TestJobInfoGenerator::GenerateTargetCoverageArtifactFilePath(const TestTarget* testTarget) const { - return AZStd::string::format("%s.Coverage.xml", (m_artifactDir / testTarget->GetName()).c_str()); + return AZStd::string::format("%s.Coverage.xml", (m_artifactDir / RepoPath(testTarget->GetName())).c_str()); } TestEnumerator::JobInfo TestJobInfoGenerator::GenerateTestEnumerationJobInfo( diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h index 3078114ef4..0c1c8189e3 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/JobRunner/TestImpactTestJobRunner.h @@ -12,8 +12,6 @@ #pragma once -#include - #include #include @@ -23,17 +21,6 @@ namespace TestImpact { - namespace Bitwise - { - //! Exception policy for test jobs (and derived jobs). - enum class TestJobExceptionPolicy - { - Never = 0, //!< Never throw. - OnFailedToExecute = 1, //!< Throw when a job fails to execute. - OnExecutedWithFailure = 1 << 1 //!< Throw when a job returns with an error code. - }; - } // namespace Bitwise - //! Base class for test related job runners. //! @tparam AdditionalInfo The data structure containing the information additional to the command arguments necessary to execute and //! complete a job. @@ -47,9 +34,8 @@ namespace TestImpact using Command = typename JobInfo::Command; using JobPayload = Payload; using Job = Job; - using ClientJobCallback = AZStd::function; + using ClientJobCallback = AZStd::function; using DerivedJobCallback = JobCallback; - using JobExceptionPolicy = Bitwise::TestJobExceptionPolicy; using JobDataMap = JobDataMap; //! Constructs the job runner with the specified parameters common to all job runs of this runner. @@ -70,7 +56,6 @@ namespace TestImpact //! @returns The result of the run sequence and the jobs that the sequence produced. AZStd::pair> ExecuteJobs( const AZStd::vector& jobInfos, - JobExceptionPolicy jobExceptionPolicy, PayloadMapProducer payloadMapProducer, StdOutputRouting stdOutRouting, StdErrorRouting stdErrRouting, @@ -95,7 +80,6 @@ namespace TestImpact template AZStd::pair::Job>> TestJobRunner::ExecuteJobs( const AZStd::vector& jobInfos, - JobExceptionPolicy jobExceptionPolicy, PayloadMapProducer payloadMapProducer, StdOutputRouting stdOutRouting, StdErrorRouting stdErrRouting, @@ -105,32 +89,23 @@ namespace TestImpact AZStd::optional derivedJobCallback) { // Callback to handle job exception policies and client/derived callbacks - const auto jobCallback = [&clientCallback, &derivedJobCallback, &jobExceptionPolicy](const JobInfo& jobInfo, const JobMeta& meta, StdContent&& std) + const auto jobCallback = [&clientCallback, &derivedJobCallback](const JobInfo& jobInfo, const JobMeta& meta, StdContent&& std) { auto callbackResult = ProcessCallbackResult::Continue; - if (meta.m_result == JobResult::FailedToExecute && IsFlagSet(jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute)) - { - callbackResult = ProcessCallbackResult::Abort; - } - else if (meta.m_result == JobResult::ExecutedWithFailure && IsFlagSet(jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure)) + if (derivedJobCallback.has_value()) { - callbackResult = ProcessCallbackResult::Abort; + callbackResult = (*derivedJobCallback)(jobInfo, meta, AZStd::move(std)); } - if (derivedJobCallback.has_value()) + if (clientCallback.has_value()) { - if (const auto result = (*derivedJobCallback)(jobInfo, meta, AZStd::move(std)); + if (const auto result = (*clientCallback)(jobInfo, meta); result == ProcessCallbackResult::Abort) { callbackResult = ProcessCallbackResult::Abort; } } - if (clientCallback.has_value()) - { - (*clientCallback)(jobInfo, meta); - } - return callbackResult; }; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp index 3b109c3a77..aa8ff04657 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp @@ -10,7 +10,7 @@ * */ -#include +#include #include #include @@ -51,7 +51,6 @@ namespace TestImpact AZStd::pair> InstrumentedTestRunner::RunInstrumentedTests( const AZStd::vector& jobInfos, - JobExceptionPolicy jobExceptionPolicy, AZStd::optional runTimeout, AZStd::optional runnerTimeout, AZStd::optional clientCallback) @@ -84,7 +83,6 @@ namespace TestImpact return ExecuteJobs( jobInfos, - jobExceptionPolicy, payloadGenerator, StdOutputRouting::None, StdErrorRouting::None, diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h index b2d7f84b6d..2f3e6eb98e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h @@ -54,7 +54,6 @@ namespace TestImpact //! @return The result of the run sequence and the instrumented run jobs with their associated test run and coverage payloads. AZStd::pair> RunInstrumentedTests( const AZStd::vector& jobInfos, - JobExceptionPolicy jobExceptionPolicy, AZStd::optional runTimeout, AZStd::optional runnerTimeout, AZStd::optional clientCallback); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp index 401084c934..7ac13f85b2 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp @@ -10,7 +10,7 @@ * */ -#include +#include #include #include @@ -28,7 +28,6 @@ namespace TestImpact AZStd::pair> TestRunner::RunTests( const AZStd::vector& jobInfos, - JobExceptionPolicy jobExceptionPolicy, AZStd::optional runTimeout, AZStd::optional runnerTimeout, AZStd::optional clientCallback) @@ -58,7 +57,6 @@ namespace TestImpact return ExecuteJobs( jobInfos, - jobExceptionPolicy, payloadGenerator, StdOutputRouting::None, StdErrorRouting::None, diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h index 1fdbc16d58..eaa7b6de1d 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.h @@ -39,7 +39,6 @@ namespace TestImpact //! @return The result of the run sequence and the run jobs with their associated test run payloads. AZStd::pair> RunTests( const AZStd::vector& jobInfos, - JobExceptionPolicy jobExceptionPolicy, AZStd::optional runTimeout, AZStd::optional runnerTimeout, AZStd::optional clientCallback); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp index 5046b3f8ee..31c0d7c5de 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp @@ -17,34 +17,14 @@ #include #include #include +#include #include +#include + namespace TestImpact { - // Known error codes for test instrumentation, test runner and unit test library - // This could be refactored into a generic solution agnostic of the tool and library specific details - namespace ErrorCodes - { - namespace OpenCppCoverage - { - static constexpr ReturnCode InvalidArgs = -1618178468; - } - - namespace GTest - { - static constexpr ReturnCode Unsuccessful = 1; - } - - namespace AZTestRunner - { - static constexpr ReturnCode InvalidArgs = 101; - static constexpr ReturnCode FailedToFindTargetBinary = 102; - static constexpr ReturnCode SymbolNotFound = 103; - static constexpr ReturnCode ModuleSkipped = 104; - } - } - namespace { // Calculate the sequence result by analysing the state of the test targets that were run. @@ -101,21 +81,10 @@ namespace TestImpact // Attempt to determine why a given test target executed successfully but return with an error code if (meta.m_returnCode.has_value()) { - switch (meta.m_returnCode.value()) + if (const auto result = CheckForAnyKnownErrorCode(meta.m_returnCode.value()); + result != AZStd::nullopt) { - // We will consider test targets that technically execute but their launcher or unit test library return a know error - // code that pertains to incorrect argument usage as test targets that failed to execute - case ErrorCodes::OpenCppCoverage::InvalidArgs: - case ErrorCodes::AZTestRunner::InvalidArgs: - case ErrorCodes::AZTestRunner::FailedToFindTargetBinary: - case ErrorCodes::AZTestRunner::ModuleSkipped: - case ErrorCodes::AZTestRunner::SymbolNotFound: - return Client::TestRunResult::FailedToExecute; - // The trivial case: the test target has failing tests - case ErrorCodes::GTest::Unsuccessful: - return Client::TestRunResult::TestFailures; - default: - break; + return result.value(); } } @@ -186,14 +155,18 @@ namespace TestImpact TestJobRunnerCallbackHandler( const AZStd::vector& testTargets, TestEngineJobMap* engineJobs, + Policy::ExecutionFailure executionFailurePolicy, + Policy::TestFailure testFailurePolicy, AZStd::optional* callback) : m_testTargets(testTargets) , m_engineJobs(engineJobs) + , m_executionFailurePolicy(executionFailurePolicy) + , m_testFailurePolicy(testFailurePolicy) , m_callback(callback) { } - void operator()(const typename JobInfo& jobInfo, const TestImpact::JobMeta& meta) + [[nodiscard]] ProcessCallbackResult operator()(const typename JobInfo& jobInfo, const TestImpact::JobMeta& meta) { const auto id = jobInfo.GetId().m_value; const auto& args = jobInfo.GetCommand().m_args; @@ -208,11 +181,21 @@ namespace TestImpact { (*m_callback).value()(it->second); } + + if ((result == Client::TestRunResult::FailedToExecute && m_executionFailurePolicy == Policy::ExecutionFailure::Abort) || + (result == Client::TestRunResult::TestFailures && m_testFailurePolicy == Policy::TestFailure::Abort)) + { + return ProcessCallbackResult::Abort; + } + + return ProcessCallbackResult::Continue; } private: const AZStd::vector& m_testTargets; TestEngineJobMap* m_engineJobs; + Policy::ExecutionFailure m_executionFailurePolicy; + Policy::TestFailure m_testFailurePolicy; AZStd::optional* m_callback; }; @@ -250,23 +233,6 @@ namespace TestImpact return engineRuns; } - - Bitwise::TestJobExceptionPolicy GetTestJobExceptionPolicy( - Policy::ExecutionFailure executionFailurePolicy, Policy::TestFailure testFailurePolicy) - { - auto jobExecutionPolicy = Bitwise::TestJobExceptionPolicy::Never; - if (executionFailurePolicy == Policy::ExecutionFailure::Abort) - { - jobExecutionPolicy |= Bitwise::TestJobExceptionPolicy::OnFailedToExecute; - } - - if (testFailurePolicy == Policy::TestFailure::Abort) - { - jobExecutionPolicy |= Bitwise::TestJobExceptionPolicy::OnExecutedWithFailure; - } - - return jobExecutionPolicy; - } } TestEngine::TestEngine( @@ -291,24 +257,19 @@ namespace TestImpact AZStd::pair> TestEngine::UpdateEnumerationCache( const AZStd::vector& testTargets, Policy::ExecutionFailure executionFailurePolicy, + Policy::TestFailure testFailurePolicy, AZStd::optional testTargetTimeout, AZStd::optional globalTimeout, AZStd::optional callback) { TestEngineJobMap engineJobs; const auto jobInfos = m_testJobInfoGenerator->GenerateTestEnumerationJobInfos(testTargets, TestEnumerator::JobInfo::CachePolicy::Write); - - const auto jobExecutionPolicy = executionFailurePolicy == Policy::ExecutionFailure::Abort - ? (TestEnumerator::JobExceptionPolicy::OnExecutedWithFailure | TestEnumerator::JobExceptionPolicy::OnFailedToExecute) - : TestEnumerator::JobExceptionPolicy::Never; auto [result, runnerJobs] = m_testEnumerator->Enumerate( jobInfos, - TestEnumerator::CacheExceptionPolicy::OnCacheWriteFailure, - jobExecutionPolicy, testTargetTimeout, globalTimeout, - TestJobRunnerCallbackHandler(testTargets, &engineJobs, &callback)); + TestJobRunnerCallbackHandler(testTargets, &engineJobs, executionFailurePolicy, testFailurePolicy, &callback)); auto engineRuns = CompileTestEngineRuns(testTargets, runnerJobs, AZStd::move(engineJobs)); return { CalculateSequenceResult(result, engineRuns, executionFailurePolicy), AZStd::move(engineRuns) }; @@ -327,10 +288,9 @@ namespace TestImpact TestEngineJobMap engineJobs; const auto jobInfos = m_testJobInfoGenerator->GenerateRegularTestRunJobInfos(testTargets); - TestJobRunnerCallbackHandler jobCallback(testTargets, &engineJobs, &callback); + TestJobRunnerCallbackHandler jobCallback(testTargets, &engineJobs, executionFailurePolicy, testFailurePolicy, &callback); auto [result, runnerJobs] = m_testRunner->RunTests( jobInfos, - GetTestJobExceptionPolicy(executionFailurePolicy, testFailurePolicy), testTargetTimeout, globalTimeout, jobCallback); @@ -355,10 +315,9 @@ namespace TestImpact auto [result, runnerJobs] = m_instrumentedTestRunner->RunInstrumentedTests( jobInfos, - GetTestJobExceptionPolicy(executionFailurePolicy, testFailurePolicy), testTargetTimeout, globalTimeout, - TestJobRunnerCallbackHandler(testTargets, &engineJobs, &callback)); + TestJobRunnerCallbackHandler(testTargets, &engineJobs, executionFailurePolicy, testFailurePolicy, &callback)); auto engineRuns = CompileTestEngineRuns(testTargets, runnerJobs, AZStd::move(engineJobs)); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h index c67a10aa4c..83f57f00ef 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h @@ -70,6 +70,7 @@ namespace TestImpact AZStd::pair> UpdateEnumerationCache( const AZStd::vector& testTargets, Policy::ExecutionFailure executionFailurePolicy, + Policy::TestFailure testFailurePolicy, AZStd::optional testTargetTimeout, AZStd::optional globalTimeout, AZStd::optional callback); From 41c8490ebbbbccb0fe68c1dff4442cca1fe945b9 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 13:30:06 +0100 Subject: [PATCH 064/233] Add missing cache delete --- .../Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp index afc40036e3..eacf73efef 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp @@ -113,6 +113,7 @@ namespace TestImpact else { // This job has no cache read policy so place in job queue + DeleteFile(jobInfo->GetCache()->m_file); jobQueue.emplace_back(*jobInfo); } } From ec758edd0decbfe2962d682acc3efe08c77b6cac Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 13:45:44 +0100 Subject: [PATCH 065/233] Remove iostream include --- .../Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp index 31c0d7c5de..b563c1846b 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp @@ -21,8 +21,6 @@ #include -#include - namespace TestImpact { namespace From 6ca37bbf84e14118ccb1000ea51eed2d887a3473 Mon Sep 17 00:00:00 2001 From: igarri Date: Thu, 27 May 2021 14:04:02 +0100 Subject: [PATCH 066/233] Checking variable constness --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index fc2fc41656..2660b660e4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -26,6 +26,7 @@ AZ_POP_DISABLE_WARNING AZ_CVAR( bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Use the new AssetBrowser TableView for searching assets."); +#pragma optimize("", off) namespace AzToolsFramework { namespace AssetBrowser @@ -138,17 +139,18 @@ namespace AzToolsFramework { const auto& subFilters = compFilter->GetSubFilters(); - auto compositeFilterIterator = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool + const auto compositeFilterIterator = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool { const auto assetTypeFilter = qobject_cast >(filter); return !assetTypeFilter.isNull(); }); + if (compositeFilterIterator != subFilters.end()) { m_assetTypeFilter = qobject_cast >(*compositeFilterIterator); } - auto compStringFilterIter = AZStd::find_if(subFilters.begin(), subFilters.end(), [](FilterConstType filter) -> bool + const auto compStringFilterIter = AZStd::find_if(subFilters.begin(), subFilters.end(), [](FilterConstType filter) -> bool { //The real StringFilter is really a CompositeFilter with just one StringFilter in its subfilter list //To know if it is actually a StringFilter we have to get that subfilter and check if it is a Stringfilter. @@ -162,7 +164,7 @@ namespace AzToolsFramework auto strFilter = qobject_cast>(filt); return !strFilter.isNull(); }; - auto stringSubfliterConstIter = AZStd::find_if(stringSubfilters.begin(), stringSubfilters.end(), canBeCasted); + const auto stringSubfliterConstIter = AZStd::find_if(stringSubfilters.begin(), stringSubfilters.end(), canBeCasted); //A Composite StringFilter will only have just one subfilter and nothing more. if (stringSubfliterConstIter != stringSubfilters.end() && stringSubfilters.size() == 1) @@ -176,6 +178,7 @@ namespace AzToolsFramework if (compStringFilterIter != subFilters.end()) { const auto compStringFilter = qobject_cast>(*compStringFilterIter); + if (!compStringFilter->GetSubFilters().isEmpty() && compStringFilter->GetSubFilters()[0]) { m_stringFilter = qobject_cast>(compStringFilter->GetSubFilters()[0]); @@ -185,7 +188,8 @@ namespace AzToolsFramework } invalidateFilter(); Q_EMIT filterChanged(); - emit stringFilterPopulated(!m_stringFilter.isNull()); + bool isNullAB = m_stringFilter.isNull(); + emit stringFilterPopulated(!isNullAB); } void AssetBrowserFilterModel::filterUpdatedSlot() @@ -205,5 +209,6 @@ namespace AzToolsFramework } // namespace AssetBrowser } // namespace AzToolsFramework// namespace AssetBrowser +#pragma optimize("", on) #include "AssetBrowser/moc_AssetBrowserFilterModel.cpp" From e82b54d59b79959b75e609690ac4430017113832 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 14:04:16 +0100 Subject: [PATCH 067/233] Address PR comments --- .../Enumeration/TestImpactTestEnumerator.cpp | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp index eacf73efef..58ffb85cb3 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp @@ -112,12 +112,49 @@ namespace TestImpact } else { - // This job has no cache read policy so place in job queue + // This job has no cache read policy so delete the cache and place in job queue DeleteFile(jobInfo->GetCache()->m_file); jobQueue.emplace_back(*jobInfo); } } + /* As per comment on PR51, this suggestion will be explored once the test coverage code for this subsystem is revisited + bool aborted = false; + for (const auto& jobInfo : jobInfos) + { + if (!jobInfo->GetCache().has_value() || + jobInfo->GetCache()->m_policy != JobData::CachePolicy::Read) + { + DeleteFile(jobInfo->GetCache()->m_file); + jobQueue.emplace_back(*jobInfo); + continue; + } + + ... // try catch part + if (!enumeration.has_value()) + { + jobQueue.emplace_back(*jobInfo); + continue; + } + + // Cache read successfully, this job will not be placed in the job queue + cachedJobs.emplace_back(Job(*jobInfo, AZStd::move(meta), AZStd::move(enumeration))); + + if (m_clientJobCallback.has_value() && (*m_clientJobCallback)(*jobInfo, meta) == ProcessCallbackResult::Abort) + { + aborted = true; // catch the index too + break; + } + } + + if (aborted) + { + // do the abortion part + + return { ProcessSchedulerResult::UserAborted, jobs }; + } + */ + const auto payloadGenerator = [this](const JobDataMap& jobDataMap) { PayloadMap enumerations; From 042a025f619a9cc3c3a9c886e4e22d80f04e40f8 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 14:06:59 +0100 Subject: [PATCH 068/233] Private runtime implementation --- .../Source/TestImpactChangeListSerializer.cpp | 96 ++++ .../Source/TestImpactClientFailureReport.cpp | 124 +++++ .../Code/Source/TestImpactClientTestRun.cpp | 40 ++ .../Source/TestImpactClientTestSelection.cpp | 56 ++ .../Code/Source/TestImpactRepoPath.cpp | 104 ++++ .../Runtime/Code/Source/TestImpactRuntime.cpp | 486 ++++++++++++++++++ .../Code/Source/TestImpactRuntimeUtils.cpp | 133 +++++ .../Code/Source/TestImpactRuntimeUtils.h | 134 +++++ 8 files changed, 1173 insertions(+) create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactChangeListSerializer.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientFailureReport.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestRun.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestSelection.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRepoPath.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactChangeListSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactChangeListSerializer.cpp new file mode 100644 index 0000000000..ef157f1848 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactChangeListSerializer.cpp @@ -0,0 +1,96 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include +#include + +namespace TestImpact +{ + namespace ChangeListFields + { + // Keys for pertinent JSON node and attribute names + constexpr const char* Keys[] = + { + "createdFiles", + "updatedFiles", + "deletedFiles" + }; + + enum + { + CreateKey, + UpdateKey, + DeleteKey + }; + } // namespace + + AZStd::string SerializeChangeList(const ChangeList& changeList) + { + rapidjson::StringBuffer stringBuffer; + rapidjson::PrettyWriter writer(stringBuffer); + + const auto serializeFileList = [&writer](const char* key, const AZStd::vector& fileList) + { + writer.Key(key); + writer.StartArray(); + + for (const auto& file : fileList) + { + writer.String(file.c_str()); + } + + writer.EndArray(); + }; + + writer.StartObject(); + serializeFileList(ChangeListFields::Keys[ChangeListFields::CreateKey], changeList.m_createdFiles); + serializeFileList(ChangeListFields::Keys[ChangeListFields::UpdateKey], changeList.m_updatedFiles); + serializeFileList(ChangeListFields::Keys[ChangeListFields::DeleteKey], changeList.m_deletedFiles); + writer.EndObject(); + + return stringBuffer.GetString(); + } + + ChangeList DeserializeChangeList(const AZStd::string& changeListString) + { + ChangeList changeList; + rapidjson::Document doc; + + if (doc.Parse<0>(changeListString.c_str()).HasParseError()) + { + throw ChangeListException("Could not parse change list data"); + } + + const auto deserializeFileList = [&doc](const char* key) + { + AZStd::vector fileList; + + for (const auto& file : doc[key].GetArray()) + { + fileList.push_back(file.GetString()); + } + + return fileList; + }; + + changeList.m_createdFiles = deserializeFileList(ChangeListFields::Keys[ChangeListFields::CreateKey]); + changeList.m_updatedFiles = deserializeFileList(ChangeListFields::Keys[ChangeListFields::UpdateKey]); + changeList.m_deletedFiles = deserializeFileList(ChangeListFields::Keys[ChangeListFields::DeleteKey]); + + return changeList; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientFailureReport.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientFailureReport.cpp new file mode 100644 index 0000000000..f39d8ee426 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientFailureReport.cpp @@ -0,0 +1,124 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + namespace Client + { + TargetFailure::TargetFailure(const AZStd::string& targetName) + : m_targetName(targetName) + { + } + + const AZStd::string& TargetFailure::GetTargetName() const + { + return m_targetName; + } + + ExecutionFailure::ExecutionFailure(const AZStd::string& targetName, const AZStd::string& command) + : TargetFailure(targetName) + , m_commandString(command) + { + } + + const AZStd::string& ExecutionFailure::GetCommandString() const + { + return m_commandString; + } + + TestFailure::TestFailure(const AZStd::string& testName, const AZStd::string& errorMessage) + : m_name(testName) + , m_errorMessage(errorMessage) + { + } + + const AZStd::string& TestFailure::GetName() const + { + return m_name; + } + + const AZStd::string& TestFailure::GetErrorMessage() const + { + return m_errorMessage; + } + + TestCaseFailure::TestCaseFailure(const AZStd::string& testCaseName, AZStd::vector&& testFailures) + : m_name(testCaseName) + , m_testFailures(AZStd::move(testFailures)) + { + } + + const AZStd::string& TestCaseFailure::GetName() const + { + return m_name; + } + + const AZStd::vector& TestCaseFailure::GetTestFailures() const + { + return m_testFailures; + } + + TestRunFailure::TestRunFailure(const AZStd::string& targetName, AZStd::vector&& testFailures) + : TargetFailure(targetName) + , m_testCaseFailures(AZStd::move(testFailures)) + { + for (const auto& testCase : m_testCaseFailures) + { + m_numTestFailures += testCase.GetTestFailures().size(); + } + } + + size_t TestRunFailure::GetNumTestFailures() const + { + return m_numTestFailures; + } + + const AZStd::vector& TestRunFailure::GetTestCaseFailures() const + { + return m_testCaseFailures; + } + + SequenceFailure::SequenceFailure( + AZStd::vector&& executionFailures, + AZStd::vector&& testRunFailures, + AZStd::vector&& timedOutTests, + AZStd::vector&& unexecutionTests) + : m_executionFailures(AZStd::move(executionFailures)) + , m_testRunFailures(testRunFailures) + , m_timedOutTests(AZStd::move(timedOutTests)) + , m_unexecutedTests(AZStd::move(unexecutionTests)) + { + } + + const AZStd::vector& SequenceFailure::GetExecutionFailures() const + { + return m_executionFailures; + } + + const AZStd::vector& SequenceFailure::GetTestRunFailures() const + { + return m_testRunFailures; + } + + const AZStd::vector& SequenceFailure::GetTimedOutTests() const + { + return m_timedOutTests; + } + + const AZStd::vector& SequenceFailure::GetUnexecutedTests() const + { + return m_unexecutedTests; + } + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestRun.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestRun.cpp new file mode 100644 index 0000000000..6661de5d69 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestRun.cpp @@ -0,0 +1,40 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +namespace TestImpact +{ + namespace Client + { + TestRun::TestRun(const AZStd::string& name, TestRunResult result, AZStd::chrono::milliseconds duration) + : m_targetName(name) + , m_result(result) + , m_duration(duration) + { + } + + const AZStd::string& TestRun::GetTargetName() const + { + return m_targetName; + } + + AZStd::chrono::milliseconds TestRun::GetDuration() const + { + return m_duration; + } + + TestRunResult TestRun::GetResult() const + { + return m_result; + } + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestSelection.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestSelection.cpp new file mode 100644 index 0000000000..f6c88893d7 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestSelection.cpp @@ -0,0 +1,56 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + namespace Client + { + TestRunSelection::TestRunSelection(const AZStd::vector& includedTests, const AZStd::vector& excludedTests) + : m_includedTestRuns(includedTests) + , m_excludedTestRuns(excludedTests) + { + } + + TestRunSelection::TestRunSelection(AZStd::vector&& includedTests, AZStd::vector&& excludedTests) + : m_includedTestRuns(AZStd::move(includedTests)) + , m_excludedTestRuns(AZStd::move(excludedTests)) + { + } + + const AZStd::vector& TestRunSelection::GetIncludededTestRuns() const + { + return m_includedTestRuns; + } + + const AZStd::vector& TestRunSelection::GetExcludedTestRuns() const + { + return m_excludedTestRuns; + } + + size_t TestRunSelection::GetNumIncludedTestRuns() const + { + return m_includedTestRuns.size(); + } + + size_t TestRunSelection::GetNumExcludedTestRuns() const + { + return m_excludedTestRuns.size(); + } + + size_t TestRunSelection::GetTotalNumTests() const + { + return GetNumIncludedTestRuns() + GetNumExcludedTestRuns(); + } + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRepoPath.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRepoPath.cpp new file mode 100644 index 0000000000..11b927247f --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRepoPath.cpp @@ -0,0 +1,104 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + + + RepoPath& RepoPath::operator=(const string_type& other) noexcept + { + m_path = AZ::IO::Path(other).MakePreferred(); + return *this; + } + + RepoPath& RepoPath::operator=(const value_type* other) noexcept + { + m_path = AZ::IO::Path(other).MakePreferred(); + return *this; + } + + RepoPath& RepoPath::operator=(const AZ::IO::Path& other) noexcept + { + m_path = AZ::IO::Path(other).MakePreferred(); + return *this; + } + + inline RepoPath operator/(const RepoPath& lhs, const AZ::IO::PathView& rhs) + { + RepoPath result(lhs); + result.m_path /= RepoPath(rhs).m_path; + return result; + } + + inline RepoPath operator/(const RepoPath& lhs, AZStd::string_view rhs) + { + RepoPath result(lhs); + result.m_path /= RepoPath(rhs).m_path; + return result; + } + + inline RepoPath operator/(const RepoPath& lhs, const RepoPath::value_type* rhs) + { + RepoPath result(lhs); + result.m_path /= RepoPath(rhs).m_path; + return result; + } + + inline RepoPath operator/(const RepoPath& lhs, const RepoPath& rhs) + { + RepoPath result(lhs); + result.m_path /= rhs.m_path; + return result; + } + + inline RepoPath& RepoPath::operator/=(const AZ::IO::PathView& rhs) + { + m_path /= RepoPath(rhs).m_path; + return *this; + } + + + inline RepoPath& RepoPath::operator/=(AZStd::string_view rhs) + { + m_path /= RepoPath(rhs).m_path; + return *this; + } + + inline RepoPath& RepoPath::operator/=(const RepoPath::value_type* rhs) + { + m_path /= RepoPath(rhs).m_path; + return *this; + } + + inline RepoPath& RepoPath::operator/=(const RepoPath& rhs) + { + m_path /= rhs.m_path; + return *this; + } + + inline bool operator==(const RepoPath& lhs, const RepoPath& rhs) noexcept + { + return lhs.m_path.Compare(rhs.m_path) == 0; + } + + inline bool operator!=(const RepoPath& lhs, const RepoPath& rhs) noexcept + { + return lhs.m_path.Compare(rhs.m_path) != 0; + } + + inline bool operator<([[maybe_unused]] const RepoPath& lhs, [[maybe_unused]] const RepoPath& rhs) noexcept + { + return lhs.m_path.String() < rhs.m_path.String(); + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp new file mode 100644 index 0000000000..1bc55ab179 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp @@ -0,0 +1,486 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace TestImpact +{ + namespace + { + // Simple helper class for tracking basic timing information + class Timer + { + public: + Timer() + : m_startTime(AZStd::chrono::high_resolution_clock::now()) + { + } + + // Returns the time elapsed (in milliseconds) since the timer was instantiated + AZStd::chrono::milliseconds Elapsed() + { + const auto endTime = AZStd::chrono::high_resolution_clock::now(); + return AZStd::chrono::duration_cast(endTime - m_startTime); + } + + private: + AZStd::chrono::high_resolution_clock::time_point m_startTime; + }; + + // Handler for test run complete events + class TestRunCompleteCallbackHandler + { + public: + TestRunCompleteCallbackHandler(AZStd::optional testCompleteCallback) + : m_testCompleteCallback(testCompleteCallback) + { + } + + void operator()(const TestEngineJob& testJob) + { + if (m_testCompleteCallback.has_value()) + { + (*m_testCompleteCallback) + (Client::TestRun(testJob.GetTestTarget()->GetName(), testJob.GetTestResult(), testJob.GetDuration())); + } + } + + private: + AZStd::optional m_testCompleteCallback; + }; + } + + Runtime::Runtime( + RuntimeConfig&& config, + Policy::ExecutionFailure executionFailurePolicy, + Policy::ExecutionFailureDrafting executionFailureDraftingPolicy, + Policy::TestFailure testFailurePolicy, + Policy::IntegrityFailure integrationFailurePolicy, + Policy::TestSharding testShardingPolicy, + Policy::TargetOutputCapture targetOutputCapture, + AZStd::optional maxConcurrency) + : m_config(AZStd::move(config)) + , m_executionFailurePolicy(executionFailurePolicy) + , m_executionFailureDraftingPolicy(executionFailureDraftingPolicy) + , m_testFailurePolicy(testFailurePolicy) + , m_integrationFailurePolicy(integrationFailurePolicy) + , m_testShardingPolicy(testShardingPolicy) + , m_targetOutputCapture(targetOutputCapture) + , m_maxConcurrency(maxConcurrency.value_or(AZStd::thread::hardware_concurrency())) + { + // Construct the dynamic dependency map from the build target descriptors + m_dynamicDependencyMap = ConstructDynamicDependencyMap(m_config.m_buildTargetDescriptor, m_config.m_testTargetMeta); + + // Construct the test selector and prioritizer from the dependency graph data (NOTE: currently not implemented) + m_testSelectorAndPrioritizer = AZStd::make_unique(m_dynamicDependencyMap.get(), DependencyGraphDataMap{}); + + // Construct the target exclude list from the target configuration data + m_testTargetExcludeList = ConstructTestTargetExcludeList(m_dynamicDependencyMap->GetTestTargetList(), m_config.m_target.m_excludedTestTargets); + + // Construct the test engine with the workspace path and launcher binaries + m_testEngine = AZStd::make_unique( + m_config.m_repo.m_root, + m_config.m_target.m_outputDirectory, + m_config.m_workspace.m_active.m_relativePaths.m_enumerationCacheDirectory, + m_config.m_workspace.m_temp.m_relativePaths.m_artifactDirectory, + m_config.m_testEngine.m_testRunner.m_binary, + m_config.m_testEngine.m_instrumentation.m_binary, + m_maxConcurrency); + + try + { + // Populate the dynamic dependency map with the existing source coverage data (if any) + const auto tiaDataRaw = ReadFileContents(m_config.m_workspace.m_active.m_relativePaths.m_sparTIAFile); + const auto tiaData = DeserializeSourceCoveringTestsList(tiaDataRaw); + if (tiaData.GetNumSources()) + { + m_dynamicDependencyMap->ReplaceSourceCoverage(tiaData); + m_hasImpactAnalysisData = true; + + // Enumerate new test targets + m_testEngine->UpdateEnumerationCache( + m_dynamicDependencyMap->GetNotCoveringTests(), + Policy::ExecutionFailure::Ignore, + Policy::TestFailure::Continue, + AZStd::nullopt, + AZStd::nullopt, + AZStd::nullopt); + } + } + catch (const DependencyException& e) + { + if (integrationFailurePolicy == Policy::IntegrityFailure::Abort) + { + throw RuntimeException(e.what()); + } + } + catch ([[maybe_unused]]const Exception& e) + { + AZ_Printf("No test impact analysis data found at %s", m_config.m_workspace.m_active.m_relativePaths.m_sparTIAFile.c_str()); + } + } + + Runtime::~Runtime() = default; + + void Runtime::EnumerateMutatedTestTargets(const ChangeDependencyList& changeDependencyList) + { + AZStd::vector testTargets; + const auto addMutatedTestTargetsToEnumerationList = [this, &testTargets](const AZStd::vector& sourceDependency) + { + for (const auto& sourceDependency : sourceDependency) + { + for (const auto& parentTarget : sourceDependency.GetParentTargets()) + { + AZStd::visit([&testTargets]([[maybe_unused]] auto&& target) + { + if constexpr (IsTestTarget) + { + testTargets.push_back(target); + } + }, parentTarget.GetTarget()); + } + } + }; + + // Gather all of the test targets that have had any of their sources modified + addMutatedTestTargetsToEnumerationList(changeDependencyList.GetCreateSourceDependencies()); + addMutatedTestTargetsToEnumerationList(changeDependencyList.GetUpdateSourceDependencies()); + addMutatedTestTargetsToEnumerationList(changeDependencyList.GetDeleteSourceDependencies()); + + // Enumerate the mutated test targets to ensure their enumeration caches are up to date + m_testEngine->UpdateEnumerationCache( + testTargets, + Policy::ExecutionFailure::Ignore, + Policy::TestFailure::Continue, + AZStd::nullopt, + AZStd::nullopt, + AZStd::nullopt); + } + + AZStd::pair, AZStd::vector> Runtime::SelectCoveringTestTargetsAndUpdateEnumerationCache( + const ChangeList& changeList, + Policy::TestPrioritization testPrioritizationPolicy) + { + AZStd::vector discardedTestTargets; + + // Select and prioritize the test targets pertinent to this change list + const auto changeDependecyList = m_dynamicDependencyMap->ApplyAndResoveChangeList(changeList); + const auto selectedTestTargets = m_testSelectorAndPrioritizer->SelectTestTargets(changeDependecyList, testPrioritizationPolicy); + + // Populate a set with the selected test targets so that we can infer the discarded test target not selected for this change list + const AZStd::unordered_set selectedTestTargetSet(selectedTestTargets.begin(), selectedTestTargets.end()); + + if (m_testShardingPolicy == Policy::TestSharding::Always) + { + EnumerateMutatedTestTargets(changeDependecyList); + } + + // The test targets in the main list not in the selected test target set are the test targets not selected for this change list + for (const auto& testTarget : m_dynamicDependencyMap->GetTestTargetList().GetTargets()) + { + if (!selectedTestTargetSet.contains(&testTarget)) + { + discardedTestTargets.push_back(&testTarget); + } + } + + return { selectedTestTargets, discardedTestTargets }; + } + + AZStd::pair, AZStd::vector> Runtime::SelectTestTargetsByExcludeList( + AZStd::vector testTargets) const + { + AZStd::vector includedTestTargets; + AZStd::vector excludedTestTargets; + + if (m_testTargetExcludeList.empty()) + { + return { testTargets, {} }; + } + + for (const auto& testTarget : testTargets) + { + if (!m_testTargetExcludeList.contains(testTarget)) + { + includedTestTargets.push_back(testTarget); + } + else + { + excludedTestTargets.push_back(testTarget); + } + } + + return { includedTestTargets, excludedTestTargets }; + } + + void Runtime::ClearDynamicDependencyMapAndRemoveExistingFile() + { + DeleteFile(m_config.m_workspace.m_active.m_relativePaths.m_sparTIAFile); + m_dynamicDependencyMap->ClearAllSourceCoverage(); + } + + void Runtime::UpdateAndSerializeDynamicDependencyMap(const SourceCoveringTestsList& sourceCoverageTestsList) + { + if (!sourceCoverageTestsList.GetNumSources()) + { + return; + } + + m_dynamicDependencyMap->ReplaceSourceCoverage(sourceCoverageTestsList); + const auto sparTIA = m_dynamicDependencyMap->ExportSourceCoverage(); + const auto sparTIAData = SerializeSourceCoveringTestsList(sparTIA); + WriteFileContents(sparTIAData, m_config.m_workspace.m_active.m_relativePaths.m_sparTIAFile); + m_hasImpactAnalysisData = true; + } + + TestSequenceResult Runtime::RegularTestSequence( + const AZStd::unordered_set suitesFilter, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional testSequenceStartCallback, + AZStd::optional testSequenceEndCallback, + AZStd::optional testCompleteCallback) + { + Timer timer; + AZStd::vector includedTestTargets; + AZStd::vector excludedTestTargets; + + // Separate the test targets into those that are excluded by either the test filter or exclusion list and those that are not + for (const auto& testTarget : m_dynamicDependencyMap->GetTestTargetList().GetTargets()) + { + if (!m_testTargetExcludeList.contains(&testTarget)) + { + if (suitesFilter.empty()) + { + // Suite filter is empty, all tests that are not on the excluded list are included + includedTestTargets.push_back(&testTarget); + } + else if(suitesFilter.contains(testTarget.GetSuite())) + { + // Test target belonging to a suite in the suite filter are included, provided that are not on the exclude list + includedTestTargets.push_back(&testTarget); + } + else + { + // Test target not belonging to a suite in the suite filter are excluded + excludedTestTargets.push_back(&testTarget); + } + } + else + { + // Test targets on the exclude list are excluded + excludedTestTargets.push_back(&testTarget); + } + } + + // Sequence start callback + if (testSequenceStartCallback.has_value()) + { + (*testSequenceStartCallback)(Client::TestRunSelection(ExtractTestTargetNames(includedTestTargets), ExtractTestTargetNames(excludedTestTargets))); + } + + const auto [result, testJobs] = m_testEngine->RegularRun( + includedTestTargets, + m_testShardingPolicy, + m_executionFailurePolicy, + m_testFailurePolicy, + m_targetOutputCapture, + testTargetTimeout, + globalTimeout, + TestRunCompleteCallbackHandler(testCompleteCallback)); + + if (testSequenceEndCallback.has_value()) + { + (*testSequenceEndCallback)(CreateSequenceFailureReport(testJobs), timer.Elapsed()); + } + + return result; + } + + TestSequenceResult Runtime::ImpactAnalysisTestSequence( + const ChangeList& changeList, + Policy::TestPrioritization testPrioritizationPolicy, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional testSequenceStartCallback, + AZStd::optional testSequenceEndCallback, + AZStd::optional testCompleteCallback) + { + Timer timer; + AZStd::vector draftedTestTargets; + + auto [selectedTestTargets, discardedTestTargets] = SelectCoveringTestTargetsAndUpdateEnumerationCache(changeList, testPrioritizationPolicy); + auto [includedSelectedTestTargets, excludedSelectedTestTargets] = SelectTestTargetsByExcludeList(selectedTestTargets); + + if (testSequenceStartCallback.has_value()) + { + (*testSequenceStartCallback)( + Client::TestRunSelection(ExtractTestTargetNames(includedSelectedTestTargets), ExtractTestTargetNames(excludedSelectedTestTargets)), + ExtractTestTargetNames(discardedTestTargets), + ExtractTestTargetNames(draftedTestTargets)); + } + + const auto [result, testJobs] = m_testEngine->InstrumentedRun( + includedSelectedTestTargets, + m_testShardingPolicy, + m_executionFailurePolicy, + Policy::IntegrityFailure::Continue, + m_testFailurePolicy, + m_targetOutputCapture, + testTargetTimeout, + globalTimeout, + TestRunCompleteCallbackHandler(testCompleteCallback)); + + UpdateAndSerializeDynamicDependencyMap(CreateSourceCoveringTestFromTestCoverages(testJobs, m_config.m_repo.m_root)); + + if (testSequenceEndCallback.has_value()) + { + (*testSequenceEndCallback)(CreateSequenceFailureReport(testJobs), timer.Elapsed()); + } + + return result; + } + + AZStd::pair Runtime::SafeImpactAnalysisTestSequence( + const ChangeList& changeList, + const AZStd::unordered_set suitesFilter, + Policy::TestPrioritization testPrioritizationPolicy, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional testSequenceStartCallback, + AZStd::optional testSequenceEndCallback, + AZStd::optional testCompleteCallback) + { + Timer timer; + AZStd::vector draftedTestTargets; + + auto [selectedTestTargets, discardedTestTargets] = SelectCoveringTestTargetsAndUpdateEnumerationCache(changeList, testPrioritizationPolicy); + auto [includedSelectedTestTargets, excludedSelectedTestTargets] = SelectTestTargetsByExcludeList(selectedTestTargets); + auto [includedDiscardedTestTargets, excludedDiscardedTestTargets] = SelectTestTargetsByExcludeList(discardedTestTargets); + + if (testSequenceStartCallback.has_value()) + { + (*testSequenceStartCallback)( + Client::TestRunSelection(ExtractTestTargetNames(includedSelectedTestTargets), ExtractTestTargetNames(excludedSelectedTestTargets)), + Client::TestRunSelection(ExtractTestTargetNames(includedDiscardedTestTargets), ExtractTestTargetNames(excludedDiscardedTestTargets)), + ExtractTestTargetNames(draftedTestTargets)); + } + + // Impact analysis run of the selected test targets + const auto [selectedResult, selectedTestJobs] = m_testEngine->InstrumentedRun( + includedSelectedTestTargets, + m_testShardingPolicy, + m_executionFailurePolicy, + Policy::IntegrityFailure::Continue, + m_testFailurePolicy, + m_targetOutputCapture, + testTargetTimeout, + globalTimeout, + TestRunCompleteCallbackHandler(testCompleteCallback)); + + // Carry the remaining global sequence time over to the discarded test run + if (globalTimeout.has_value()) + { + const auto elapsed = timer.Elapsed(); + globalTimeout = elapsed < globalTimeout.value() ? globalTimeout.value() - elapsed : AZStd::chrono::milliseconds(0); + } + + // Regular run of the discarded test targets + const auto [discardedResult, discardedTestJobs] = m_testEngine->RegularRun( + includedDiscardedTestTargets, + m_testShardingPolicy, + m_executionFailurePolicy, + m_testFailurePolicy, + m_targetOutputCapture, + testTargetTimeout, + globalTimeout, + TestRunCompleteCallbackHandler(testCompleteCallback)); + + UpdateAndSerializeDynamicDependencyMap(CreateSourceCoveringTestFromTestCoverages(selectedTestJobs, m_config.m_repo.m_root)); + + if (testSequenceEndCallback.has_value()) + { + (*testSequenceEndCallback)( + CreateSequenceFailureReport(selectedTestJobs), + CreateSequenceFailureReport(discardedTestJobs), + timer.Elapsed()); + } + + return { selectedResult, discardedResult }; + } + + TestSequenceResult Runtime::SeededTestSequence( + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + AZStd::optional testSequenceStartCallback, + AZStd::optional testSequenceEndCallback, + AZStd::optional testCompleteCallback) + { + Timer timer; + AZStd::vector includedTestTargets; + AZStd::vector excludedTestTargets; + + for (const auto& testTarget : m_dynamicDependencyMap->GetTestTargetList().GetTargets()) + { + if (!m_testTargetExcludeList.contains(&testTarget)) + { + includedTestTargets.push_back(&testTarget); + } + else + { + excludedTestTargets.push_back(&testTarget); + } + } + + if (testSequenceStartCallback.has_value()) + { + (*testSequenceStartCallback)(Client::TestRunSelection(ExtractTestTargetNames(includedTestTargets), ExtractTestTargetNames(excludedTestTargets))); + } + + const auto [result, testJobs] = m_testEngine->InstrumentedRun( + includedTestTargets, + m_testShardingPolicy, + m_executionFailurePolicy, + Policy::IntegrityFailure::Continue, + m_testFailurePolicy, + m_targetOutputCapture, + testTargetTimeout, + globalTimeout, + TestRunCompleteCallbackHandler(testCompleteCallback)); + + ClearDynamicDependencyMapAndRemoveExistingFile(); + UpdateAndSerializeDynamicDependencyMap(CreateSourceCoveringTestFromTestCoverages(testJobs, m_config.m_repo.m_root)); + + if (testSequenceEndCallback.has_value()) + { + (*testSequenceEndCallback)(CreateSequenceFailureReport(testJobs), timer.Elapsed()); + } + + return result; + } + + bool Runtime::HasImpactAnalysisData() const + { + return m_hasImpactAnalysisData; + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp new file mode 100644 index 0000000000..e0f77c6479 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp @@ -0,0 +1,133 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include +#include + +#include + +namespace TestImpact +{ + TestTargetMetaMap ReadTestTargetMetaMapFile(const RepoPath& testTargetMetaConfigFile) + { + const auto masterTestListData = ReadFileContents(testTargetMetaConfigFile); + return TestTargetMetaMapFactory(masterTestListData); + } + + AZStd::vector ReadBuildTargetDescriptorFiles(const BuildTargetDescriptorConfig& buildTargetDescriptorConfig) + { + AZStd::vector buildTargetDescriptors; + for (const auto& buildTargetDescriptorFile : std::filesystem::directory_iterator(buildTargetDescriptorConfig.m_mappingDirectory.c_str())) + { + const auto buildTargetDescriptorContents = ReadFileContents(buildTargetDescriptorFile.path().string().c_str()); + auto buildTargetDescriptor = TestImpact::BuildTargetDescriptorFactory( + buildTargetDescriptorContents, + buildTargetDescriptorConfig.m_staticInclusionFilters, + buildTargetDescriptorConfig.m_inputInclusionFilters, + buildTargetDescriptorConfig.m_inputOutputPairer); + buildTargetDescriptors.emplace_back(AZStd::move(buildTargetDescriptor)); + } + + return buildTargetDescriptors; + } + + AZStd::unique_ptr ConstructDynamicDependencyMap( + const BuildTargetDescriptorConfig& buildTargetDescriptorConfig, + const TestTargetMetaConfig& testTargetMetaConfig) + { + auto testTargetmetaMap = ReadTestTargetMetaMapFile(testTargetMetaConfig.m_metaFile); + auto buildTargetDescriptors = ReadBuildTargetDescriptorFiles(buildTargetDescriptorConfig); + auto buildTargets = CompileTargetDescriptors(AZStd::move(buildTargetDescriptors), AZStd::move(testTargetmetaMap)); + auto&& [productionTargets, testTargets] = buildTargets; + return AZStd::make_unique(AZStd::move(productionTargets), AZStd::move(testTargets)); + } + + AZStd::unordered_set ConstructTestTargetExcludeList(const TestTargetList& testTargets, const AZStd::vector& excludedTestTargets) + { + AZStd::unordered_set testTargetExcludeList; + for (const auto& testTargetName : excludedTestTargets) + { + if (const auto* testTarget = testTargets.GetTarget(testTargetName); testTarget != nullptr) + { + testTargetExcludeList.insert(testTarget); + } + } + + return testTargetExcludeList; + } + + AZStd::vector ExtractTestTargetNames(const AZStd::vector testTargets) + { + AZStd::vector testNames; + AZStd::transform(testTargets.begin(), testTargets.end(), AZStd::back_inserter(testNames), [](const TestTarget* testTarget) + { + return testTarget->GetName(); + }); + + return testNames; + } + + SourceCoveringTestsList CreateSourceCoveringTestFromTestCoverages(const AZStd::vector& jobs, const RepoPath& root) + { + AZStd::unordered_map> coverage; + for (const auto& job : jobs) + { + if (const auto testResult = job.GetTestResult(); + testResult == Client::TestRunResult::AllTestsPass || testResult == Client::TestRunResult::TestFailures) + { + if (testResult == Client::TestRunResult::AllTestsPass) + { + // Passing tests should have coverage data, otherwise something is very wrong + AZ_TestImpact_Eval( + job.GetTestCoverge().has_value(), + RuntimeException, + AZStd::string::format( + "Test target '%s' completed its test run successfully but produced no coverage data", + job.GetTestTarget()->GetName().c_str())); + } + else if (!job.GetTestCoverge().has_value()) + { + // When a test run completes with failing tests but produces no coverage artifact that's typically a sign of the + // test aborting due to an unhandled exception, in which case ignore it and let it be picked up in the failure report + continue; + } + + for (const auto& source : job.GetTestCoverge().value().GetSourcesCovered()) + { + coverage[source.String()].insert(job.GetTestTarget()->GetName()); + } + } + } + + AZStd::vector sourceCoveringTests; + sourceCoveringTests.reserve(coverage.size()); + for (auto&& [source, testTargets] : coverage) + { + if (const auto sourcePath = RepoPath(source); + sourcePath.IsRelativeTo(root)) + { + sourceCoveringTests.push_back(SourceCoveringTests(RepoPath(sourcePath.LexicallyRelative(root)), AZStd::move(testTargets))); + } + else + { + AZ_Warning("TestImpact", false, "Ignoring source, source it outside of repo: %s", sourcePath.c_str()); + } + } + + return SourceCoveringTestsList(AZStd::move(sourceCoveringTests)); + } +} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h new file mode 100644 index 0000000000..3210ba808b --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h @@ -0,0 +1,134 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace TestImpact +{ + //! Construct a dynamic dependency map from the build target descriptors and test target metas. + AZStd::unique_ptr ConstructDynamicDependencyMap( + const BuildTargetDescriptorConfig& buildTargetDescriptorConfig, + const TestTargetMetaConfig& testTargetMetaConfig); + + //! Constructs the resolved test target exclude list from the specified list of targets and unresolved test target exclude list. + AZStd::unordered_set ConstructTestTargetExcludeList( + const TestTargetList& testTargets, + const AZStd::vector& excludedTestTargets); + + //! Extracts the name information from the specified test targets. + AZStd::vector ExtractTestTargetNames(const AZStd::vector testTargets); + + //! Creates the consolidates source covering tests list from the test engine instrumented run jobs. + SourceCoveringTestsList CreateSourceCoveringTestFromTestCoverages( + const AZStd::vector& jobs, + const RepoPath& root); + + //! Generates a test run failure report from the specified test engine job information. + //! @tparam TestJob The test engine job type. + template + Client::TestRunFailure GenerateTestRunFailure(const TestJob& testJob) + { + if (testJob.GetTestRun().has_value()) + { + AZStd::vector testCaseFailures; + for (const auto& testSuite : testJob.GetTestRun()->GetTestSuites()) + { + AZStd::vector testFailures; + for (const auto& testCase : testSuite.m_tests) + { + if(testCase.m_result.value_or(TestRunResult::Passed) == TestRunResult::Failed) + { + testFailures.push_back(Client::TestFailure(testCase.m_name, "No error message retrieved")); + } + } + + if (!testFailures.empty()) + { + testCaseFailures.push_back(Client::TestCaseFailure(testSuite.m_name, AZStd::move(testFailures))); + } + } + + return Client::TestRunFailure(Client::TestRunFailure(testJob.GetTestTarget()->GetName(), AZStd::move(testCaseFailures))); + } + else + { + return Client::TestRunFailure(testJob.GetTestTarget()->GetName(), { }); + } + } + + //! Generates a sequence failure report from the specified list of test engine jobs. + //! @tparam TestJob The test engine job type. + template + Client::SequenceFailure GenerateSequenceFailureReport(const AZStd::vector& testJobs) + { + AZStd::vector executionFailures; + AZStd::vector testRunFailures; + AZStd::vector timedOutTestRuns; + AZStd::vector unexecutedTestRuns; + + for (const auto& testJob : testJobs) + { + switch (testJob.GetTestResult()) + { + case Client::TestRunResult::FailedToExecute: + { + executionFailures.push_back(Client::ExecutionFailure(testJob.GetTestTarget()->GetName(), testJob.GetCommandString())); + break; + } + case Client::TestRunResult::NotRun: + { + unexecutedTestRuns.push_back(testJob.GetTestTarget()->GetName()); + break; + } + case Client::TestRunResult::Timeout: + { + timedOutTestRuns.push_back(testJob.GetTestTarget()->GetName()); + break; + } + case Client::TestRunResult::AllTestsPass: + { + break; + } + case Client::TestRunResult::TestFailures: + { + testRunFailures.push_back(ExtractTestRunFailure(testJob)); + break; + } + default: + { + throw Exception( + AZStd::string::format("Unexpected client test run result: %u", static_cast(testJob.GetTestResult()))); + } + } + } + + return Client::SequenceFailure( + AZStd::move(executionFailures), + AZStd::move(testRunFailures), + AZStd::move(timedOutTestRuns), + AZStd::move(unexecutedTestRuns)); + } +} From 1936dbc0d1e1939e3b449aad86e7b71c86e88f25 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 14:11:45 +0100 Subject: [PATCH 069/233] Fix long line length --- .../Runtime/Code/Source/TestImpactRuntimeUtils.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp index e0f77c6479..87a402bd6a 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp @@ -56,7 +56,8 @@ namespace TestImpact return AZStd::make_unique(AZStd::move(productionTargets), AZStd::move(testTargets)); } - AZStd::unordered_set ConstructTestTargetExcludeList(const TestTargetList& testTargets, const AZStd::vector& excludedTestTargets) + AZStd::unordered_set ConstructTestTargetExcludeList( + const TestTargetList& testTargets, const AZStd::vector& excludedTestTargets) { AZStd::unordered_set testTargetExcludeList; for (const auto& testTargetName : excludedTestTargets) From e9daed723a7496073fa29e5e65c37d4b74316d25 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 14:19:08 +0100 Subject: [PATCH 070/233] Ensure enumeration caches are always updated --- .../Runtime/Code/Source/TestImpactRuntime.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp index 1bc55ab179..538d435d16 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp @@ -190,10 +190,8 @@ namespace TestImpact // Populate a set with the selected test targets so that we can infer the discarded test target not selected for this change list const AZStd::unordered_set selectedTestTargetSet(selectedTestTargets.begin(), selectedTestTargets.end()); - if (m_testShardingPolicy == Policy::TestSharding::Always) - { - EnumerateMutatedTestTargets(changeDependecyList); - } + // Update the enumeration caches of mutated targets regardless of the current sharding policy + EnumerateMutatedTestTargets(changeDependecyList); // The test targets in the main list not in the selected test target set are the test targets not selected for this change list for (const auto& testTarget : m_dynamicDependencyMap->GetTestTargetList().GetTargets()) From b13a7e1d3c228078f7f67ff1da3e1bed755d7165 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 17:09:59 +0100 Subject: [PATCH 071/233] Remove whitespace --- .../Runtime/Code/Source/TestImpactRepoPath.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRepoPath.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRepoPath.cpp index 11b927247f..b2ffed2caf 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRepoPath.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRepoPath.cpp @@ -14,8 +14,6 @@ namespace TestImpact { - - RepoPath& RepoPath::operator=(const string_type& other) noexcept { m_path = AZ::IO::Path(other).MakePreferred(); From 990f90410deecda6c703bed209eb624319a24897 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 27 May 2021 18:21:41 +0100 Subject: [PATCH 072/233] Address PR comments --- .../Source/TestImpactClientFailureReport.cpp | 6 +++--- .../Code/Source/TestImpactClientTestRun.cpp | 4 ++-- .../Source/TestImpactClientTestSelection.cpp | 4 ++-- .../Runtime/Code/Source/TestImpactRuntime.cpp | 16 ++++++++-------- .../Runtime/Code/Source/TestImpactRuntimeUtils.h | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientFailureReport.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientFailureReport.cpp index f39d8ee426..d8e4709f21 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientFailureReport.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientFailureReport.cpp @@ -119,6 +119,6 @@ namespace TestImpact const AZStd::vector& SequenceFailure::GetUnexecutedTests() const { return m_unexecutedTests; - } - } -} + } + } // namespace Client +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestRun.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestRun.cpp index 6661de5d69..dfc9f9d46c 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestRun.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestRun.cpp @@ -36,5 +36,5 @@ namespace TestImpact { return m_result; } - } -} + } // namespace Client +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestSelection.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestSelection.cpp index f6c88893d7..067ab53fd5 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestSelection.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientTestSelection.cpp @@ -52,5 +52,5 @@ namespace TestImpact { return GetNumIncludedTestRuns() + GetNumExcludedTestRuns(); } - } -} + } // namespace Client +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp index 538d435d16..9c9351244f 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp @@ -27,7 +27,7 @@ namespace TestImpact { namespace { - // Simple helper class for tracking basic timing information + //! Simple helper class for tracking basic timing information. class Timer { public: @@ -36,7 +36,7 @@ namespace TestImpact { } - // Returns the time elapsed (in milliseconds) since the timer was instantiated + //! Returns the time elapsed (in milliseconds) since the timer was instantiated AZStd::chrono::milliseconds Elapsed() { const auto endTime = AZStd::chrono::high_resolution_clock::now(); @@ -47,7 +47,7 @@ namespace TestImpact AZStd::chrono::high_resolution_clock::time_point m_startTime; }; - // Handler for test run complete events + //! Handler for test run complete events. class TestRunCompleteCallbackHandler { public: @@ -145,9 +145,9 @@ namespace TestImpact void Runtime::EnumerateMutatedTestTargets(const ChangeDependencyList& changeDependencyList) { AZStd::vector testTargets; - const auto addMutatedTestTargetsToEnumerationList = [this, &testTargets](const AZStd::vector& sourceDependency) + const auto addMutatedTestTargetsToEnumerationList = [this, &testTargets](const AZStd::vector& sourceDependencies) { - for (const auto& sourceDependency : sourceDependency) + for (const auto& sourceDependency : sourceDependencies) { for (const auto& parentTarget : sourceDependency.GetParentTargets()) { @@ -184,14 +184,14 @@ namespace TestImpact AZStd::vector discardedTestTargets; // Select and prioritize the test targets pertinent to this change list - const auto changeDependecyList = m_dynamicDependencyMap->ApplyAndResoveChangeList(changeList); - const auto selectedTestTargets = m_testSelectorAndPrioritizer->SelectTestTargets(changeDependecyList, testPrioritizationPolicy); + const auto changeDependencyList = m_dynamicDependencyMap->ApplyAndResoveChangeList(changeList); + const auto selectedTestTargets = m_testSelectorAndPrioritizer->SelectTestTargets(changeDependencyList, testPrioritizationPolicy); // Populate a set with the selected test targets so that we can infer the discarded test target not selected for this change list const AZStd::unordered_set selectedTestTargetSet(selectedTestTargets.begin(), selectedTestTargets.end()); // Update the enumeration caches of mutated targets regardless of the current sharding policy - EnumerateMutatedTestTargets(changeDependecyList); + EnumerateMutatedTestTargets(changeDependencyList); // The test targets in the main list not in the selected test target set are the test targets not selected for this change list for (const auto& testTarget : m_dynamicDependencyMap->GetTestTargetList().GetTargets()) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h index 3210ba808b..e9d1c5425b 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h @@ -59,7 +59,7 @@ namespace TestImpact AZStd::vector testFailures; for (const auto& testCase : testSuite.m_tests) { - if(testCase.m_result.value_or(TestRunResult::Passed) == TestRunResult::Failed) + if (testCase.m_result.value_or(TestRunResult::Passed) == TestRunResult::Failed) { testFailures.push_back(Client::TestFailure(testCase.m_name, "No error message retrieved")); } From a71a583048488993482f509c08334f741d009a90 Mon Sep 17 00:00:00 2001 From: Yuriy Toporovskyy Date: Thu, 27 May 2021 14:52:50 -0400 Subject: [PATCH 073/233] Bug fix: handle the case where the data source type is `any' in the SCT converter command validation step. In this case, CreateAny will construct an `any' by `any(T())', with `T = any', which move-constructs from a temporary empty `any'. This causes the check for a empty `any' to fail, and the validation does not even run. In the case where `T=any', since we cannot construct an `any' which itself holds an `any' (except by super tricky shenanigans), we can simply use the default constructed `any' object (instead of the object stored by the `any') as the object storage for the validation step. --- Code/Tools/SerializeContextTools/Converter.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Code/Tools/SerializeContextTools/Converter.cpp b/Code/Tools/SerializeContextTools/Converter.cpp index 0b19d4e981..27bd1fc495 100644 --- a/Code/Tools/SerializeContextTools/Converter.cpp +++ b/Code/Tools/SerializeContextTools/Converter.cpp @@ -757,14 +757,21 @@ namespace AZ { using namespace AZ::JsonSerializationResult; + // Need special handling if the original type is `any', because `CreateAny' creates an empty `any' in that case, + // because it's not possible to store an any inside an any + const bool originalTypeIsAny = originalType == azrtti_typeid(); + AZStd::any convertedDeserialized = settings.m_serializeContext->CreateAny(originalType); - if (convertedDeserialized.empty()) + if (!originalTypeIsAny && convertedDeserialized.empty()) { AZ_Printf("Convert", " Failed to deserialized from converted document.\n"); return false; } - ResultCode loadResult = JsonSerialization::Load(AZStd::any_cast(&convertedDeserialized), originalType, convertedData, settings); + // Get a storage suitable to hold this data. + void* objectPtr = originalTypeIsAny ? &convertedDeserialized : AZStd::any_cast(&convertedDeserialized); + + ResultCode loadResult = JsonSerialization::Load(objectPtr, originalType, convertedData, settings); if (loadResult.GetProcessing() == Processing::Halted) { AZ_Printf("Convert", " Failed to verify converted document because it couldn't be loaded.\n"); @@ -782,7 +789,7 @@ namespace AZ bool result = false; if (data->m_serializer) { - result = data->m_serializer->CompareValueData(original, AZStd::any_cast(&convertedDeserialized)); + result = data->m_serializer->CompareValueData(original, objectPtr); } else { @@ -793,7 +800,7 @@ namespace AZ AZStd::vector loadedData; AZ::IO::ByteContainerStream loadedStream(&loadedData); AZ::Utils::SaveObjectToStream(loadedStream, AZ::ObjectStream::ST_BINARY, - AZStd::any_cast(&convertedDeserialized), convertedDeserialized.type()); + objectPtr, originalType); result = (originalData.size() == loadedData.size()) && From 684ef8046fad906a0952d2832f6473696fb17629 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 28 May 2021 10:46:25 +0100 Subject: [PATCH 074/233] Add console command line options --- .../Frontend/Console/CMakeLists.txt | 12 - .../Frontend/Console/Code/CMakeLists.txt | 23 - ...pactframework_frontend_console_files.cmake | 14 - .../Source/TestImpactCommandLineOptions.cpp | 463 ++++++++++++++++++ .../Source/TestImpactCommandLineOptions.h | 111 +++++ .../TestImpactCommandLineOptionsException.h} | 17 +- .../TestImpactCommandLineOptionsUtils.cpp | 77 +++ .../TestImpactCommandLineOptionsUtils.h | 119 +++++ 8 files changed, 783 insertions(+), 53 deletions(-) delete mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Code/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_files.cmake create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h rename Code/Tools/TestImpactFramework/Frontend/Console/{Code/Source/TestImpactConsole.cpp => Static/Code/Source/TestImpactCommandLineOptionsException.h} (65%) create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/CMakeLists.txt deleted file mode 100644 index 20a680bce9..0000000000 --- a/Code/Tools/TestImpactFramework/Frontend/Console/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -add_subdirectory(Code) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/Code/CMakeLists.txt deleted file mode 100644 index da2c707cb8..0000000000 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Code/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -ly_add_target( - NAME TestImpact.Frontend.Console EXECUTABLE - NAMESPACE AZ - FILES_CMAKE - testimpactframework_frontend_console_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - Source - BUILD_DEPENDENCIES - PRIVATE - AZ::TestImpact.Runtime.Static -) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_files.cmake b/Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_files.cmake deleted file mode 100644 index 19e99649a7..0000000000 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_files.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -set(FILES - Source/TestImpactConsole.cpp -) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp new file mode 100644 index 0000000000..e4083641e5 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp @@ -0,0 +1,463 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include + +namespace TestImpact +{ + namespace + { + enum + { + // Options + Config, + ChangeList, + OutputChangeList, + Sequence, + TestPrioritizationPolicy, + ExecutionFailurePolicy, + ExecutionFailureDraftingPolicy, + TestFailurePolicy, + IntegrityFailurePolicy, + TestShardingPolicy, + TargetOutputCapture, + MaxConcurrency, + TestTargetTimeout, + GlobalTimeout, + SuitesFilter, + SafeMode, + // Values + None, + Seed, + Regular, + ImpactAnalysis, + ImpactAnalysisOrSeed, + Locality, + Abort, + Continue, + Ignore, + StdOut, + File, + AllSuites + }; + + constexpr const char* OptionKeys[] = + { + // Options + "config", + "changelist", + "ochangelist", + "sequence", + "ppolicy", + "epolicy", + "rexecfailures", + "fpolicy", + "ipolicy", + "shard", + "targetout", + "maxconcurrency", + "ttimeout", + "gtimeout", + "suites", + "safemode", + // Values + "none", + "seed", + "regular", + "tia", + "tiaorseed", + "locality", + "abort", + "continue", + "ignore", + "stdout", + "file", + "*" + }; + + RepoPath ParseConfigurationFile(const AZ::CommandLine& cmd) + { + return ParsePathOption(OptionKeys[Config], cmd).value_or(LY_TEST_IMPACT_DEFAULT_CONFIG_FILE); + } + + AZStd::optional ParseChangeListFile(const AZ::CommandLine& cmd) + { + return ParsePathOption(OptionKeys[ChangeList], cmd); + } + + bool ParseOutputChangeList(const AZ::CommandLine& cmd) + { + return ParseOnOffOption(OptionKeys[OutputChangeList], BinaryStateValue{ false, true }, cmd).value_or(false); + } + + TestSequenceType ParseTestSequenceType(const AZ::CommandLine& cmd) + { + const AZStd::vector> states = + { + {OptionKeys[None], TestSequenceType::None}, + {OptionKeys[Seed], TestSequenceType::Seed}, + {OptionKeys[Regular], TestSequenceType::Regular}, + {OptionKeys[ImpactAnalysis], TestSequenceType::ImpactAnalysis}, + {OptionKeys[ImpactAnalysisOrSeed], TestSequenceType::ImpactAnalysisOrSeed} + }; + + return ParseMultiStateOption(OptionKeys[Sequence], states, cmd).value_or(TestSequenceType::None); + } + + Policy::TestPrioritization ParseTestPrioritizationPolicy(const AZ::CommandLine& cmd) + { + const BinaryStateOption states = + { + {OptionKeys[None], Policy::TestPrioritization::None}, + {OptionKeys[Locality], Policy::TestPrioritization::DependencyLocality} + }; + + return ParseBinaryStateOption(OptionKeys[TestPrioritizationPolicy], states, cmd).value_or(Policy::TestPrioritization::None); + } + + Policy::ExecutionFailure ParseExecutionFailurePolicy(const AZ::CommandLine& cmd) + { + const AZStd::vector> states = + { + {OptionKeys[Abort], Policy::ExecutionFailure::Abort}, + {OptionKeys[Continue], Policy::ExecutionFailure::Continue}, + {OptionKeys[Ignore], Policy::ExecutionFailure::Ignore} + }; + return ParseMultiStateOption(OptionKeys[ExecutionFailurePolicy], states, cmd).value_or(Policy::ExecutionFailure::Continue); + } + + Policy::ExecutionFailureDrafting ParseExecutionFailureDraftingPolicy(const AZ::CommandLine& cmd) + { + const BinaryStateValue states = + { + Policy::ExecutionFailureDrafting::Never, + Policy::ExecutionFailureDrafting::Always + }; + + return ParseOnOffOption(OptionKeys[ExecutionFailureDraftingPolicy], states, cmd).value_or(Policy::ExecutionFailureDrafting::Always); + } + + Policy::TestFailure ParseTestFailurePolicy(const AZ::CommandLine& cmd) + { + const BinaryStateValue states = + { + Policy::TestFailure::Abort, + Policy::TestFailure::Continue + }; + + return ParseAbortContinueOption(OptionKeys[TestFailurePolicy], states, cmd).value_or(Policy::TestFailure::Abort); + } + + Policy::IntegrityFailure ParseIntegrityFailurePolicy(const AZ::CommandLine& cmd) + { + const BinaryStateValue states = + { + Policy::IntegrityFailure::Abort, + Policy::IntegrityFailure::Continue + }; + + return ParseAbortContinueOption(OptionKeys[IntegrityFailurePolicy], states, cmd).value_or(Policy::IntegrityFailure::Abort); + } + + Policy::TestSharding ParseTestShardingPolicy(const AZ::CommandLine& cmd) + { + const BinaryStateValue states = + { + Policy::TestSharding::Never, + Policy::TestSharding::Always + }; + + return ParseOnOffOption("shard", states, cmd).value_or(Policy::TestSharding::Never); + } + + Policy::TargetOutputCapture ParseTargetOutputCapture(const AZ::CommandLine& cmd) + { + const auto numSwitchValues = cmd.GetNumSwitchValues(OptionKeys[TargetOutputCapture]); + if (numSwitchValues) + { + AZ_TestImpact_Eval( + numSwitchValues <= 2, CommandLineOptionsException, "Unexpected parameters for target output capture option"); + + Policy::TargetOutputCapture targetOutputCapture = Policy::TargetOutputCapture::None; + for (auto i = 0; i < numSwitchValues; i++) + { + const auto option = cmd.GetSwitchValue(OptionKeys[TargetOutputCapture], i); + if (option == OptionKeys[StdOut]) + { + if (targetOutputCapture == Policy::TargetOutputCapture::File) + { + targetOutputCapture = Policy::TargetOutputCapture::StdOutAndFile; + } + else + { + targetOutputCapture = Policy::TargetOutputCapture::StdOut; + } + } + else if (option == OptionKeys[File]) + { + if (targetOutputCapture == Policy::TargetOutputCapture::StdOut) + { + targetOutputCapture = Policy::TargetOutputCapture::StdOutAndFile; + } + else + { + targetOutputCapture = Policy::TargetOutputCapture::File; + } + } + else + { + throw CommandLineOptionsException( + AZStd::string::format("Unexpected value for target output capture option: %s", option.c_str())); + } + } + + return targetOutputCapture; + } + + return Policy::TargetOutputCapture::None; + } + + AZStd::optional ParseMaxConcurrency(const AZ::CommandLine& cmd) + { + return ParseUnsignedIntegerOption(OptionKeys[MaxConcurrency], cmd); + } + + AZStd::optional ParseTestTargetTimeout(const AZ::CommandLine& cmd) + { + return ParseSecondsOption(OptionKeys[TestTargetTimeout], cmd); + } + + AZStd::optional ParseGlobalTimeout(const AZ::CommandLine& cmd) + { + return ParseSecondsOption(OptionKeys[GlobalTimeout], cmd); + } + + bool ParseSafeMode(const AZ::CommandLine& cmd) + { + const BinaryStateValue states = { false, true }; + return ParseOnOffOption(OptionKeys[SafeMode], states, cmd).value_or(false); + } + + AZStd::unordered_set ParseSuitesFilter(const AZ::CommandLine& cmd) + { + AZStd::unordered_set suitesFilter; + const auto numSwitchValues = cmd.GetNumSwitchValues(OptionKeys[SuitesFilter]); + if (numSwitchValues) + { + for (auto i = 0; i < numSwitchValues; i++) + { + const auto value = cmd.GetSwitchValue(OptionKeys[SuitesFilter], i); + AZ_TestImpact_Eval(!value.empty(), CommandLineOptionsException, "Suites option value is empty"); + if (value == OptionKeys[AllSuites]) + { + AZ_TestImpact_Eval( + suitesFilter.empty(), CommandLineOptionsException, "The * suite cannot be used with other suites"); + } + + suitesFilter.insert(value); + } + } + + if (suitesFilter.find(OptionKeys[AllSuites]) != suitesFilter.end()) + { + return {}; + } + + return suitesFilter; + } + } + + CommandLineOptions::CommandLineOptions(int argc, char** argv) + { + AZ::CommandLine cmd; + cmd.Parse(argc, argv); + + m_configurationFile = ParseConfigurationFile(cmd); + m_changeListFile = ParseChangeListFile(cmd); + m_outputChangeList = ParseOutputChangeList(cmd); + m_testSequenceType = ParseTestSequenceType(cmd); + m_testPrioritizationPolicy = ParseTestPrioritizationPolicy(cmd); + m_executionFailurePolicy = ParseExecutionFailurePolicy(cmd); + m_executionFailureDraftingPolicy = ParseExecutionFailureDraftingPolicy(cmd); + m_testFailurePolicy = ParseTestFailurePolicy(cmd); + m_integrityFailurePolicy = ParseIntegrityFailurePolicy(cmd); + m_testShardingPolicy = ParseTestShardingPolicy(cmd); + m_targetOutputCapture = ParseTargetOutputCapture(cmd); + m_maxConcurrency = ParseMaxConcurrency(cmd); + m_testTargetTimeout = ParseTestTargetTimeout(cmd); + m_globalTimeout = ParseGlobalTimeout(cmd); + m_safeMode = ParseSafeMode(cmd); + m_suitesFilter = ParseSuitesFilter(cmd); + } + + bool CommandLineOptions::HasChangeListFile() const + { + return m_changeListFile.has_value(); + } + + bool CommandLineOptions::HasSafeMode() const + { + return m_safeMode; + } + + const AZStd::optional& CommandLineOptions::GetChangeListFile() const + { + return m_changeListFile; + } + + bool CommandLineOptions::HasOutputChangeList() const + { + return m_outputChangeList; + } + + const RepoPath& CommandLineOptions::GetConfigurationFile() const + { + return m_configurationFile; + } + + TestSequenceType CommandLineOptions::GetTestSequenceType() const + { + return m_testSequenceType; + } + + Policy::TestPrioritization CommandLineOptions::GetTestPrioritizationPolicy() const + { + return m_testPrioritizationPolicy; + } + + Policy::ExecutionFailure CommandLineOptions::GetExecutionFailurePolicy() const + { + return m_executionFailurePolicy; + } + + Policy::ExecutionFailureDrafting CommandLineOptions::GetExecutionFailureDraftingPolicy() const + { + return m_executionFailureDraftingPolicy; + } + + Policy::TestFailure CommandLineOptions::GetTestFailurePolicy() const + { + return m_testFailurePolicy; + } + + Policy::IntegrityFailure CommandLineOptions::GetIntegrityFailurePolicy() const + { + return m_integrityFailurePolicy; + } + + Policy::TestSharding CommandLineOptions::GetTestShardingPolicy() const + { + return m_testShardingPolicy; + } + + Policy::TargetOutputCapture CommandLineOptions::GetTargetOutputCapture() const + { + return m_targetOutputCapture; + } + + const AZStd::optional& CommandLineOptions::GetMaxConcurrency() const + { + return m_maxConcurrency; + } + + const AZStd::optional& CommandLineOptions::GetTestTargetTimeout() const + { + return m_testTargetTimeout; + } + + const AZStd::optional& CommandLineOptions::GetGlobalTimeout() const + { + return m_globalTimeout; + } + + const AZStd::unordered_set& CommandLineOptions::GetSuitesFilter() const + { + return m_suitesFilter; + } + + AZStd::string CommandLineOptions::GetCommandLineUsageString() + { + AZStd::string help = + "usage: tiaf [options]\n" + " options:\n" + " -config= Path to the configuration file for the TIAF runtime (default: \n" + " ..json).\n" + " -changelist= Path to the JSON of source file changes to perform test impact \n" + " analysis on.\n" + " -gtimeout= Global timeout value to terminate the entire test sequence should it \n" + " be exceeded.\n" + " -ttimeout= Timeout value to terminate individual test targets should it be \n" + " exceeded.\n" + " -sequence= The type of test sequence to perform, where none runs no tests and\n" + " will report a all tests successful, seed removes any prior coverage \n" + " data and runs all test targets with instrumentation to reseed the \n" + " data from scratch, regular runs all of the test targets without any \n" + " instrumentation to generate coverage data(any prior coverage data is \n" + " left intact), tia uses any prior coverage data to run the instrumented \n" + " subset of selected tests(if no prior coverage data a regular run is \n" + " performed instead) and tiaorseed uses any prior coverage data to run \n" + " the instrumented subset of selected tests(if no prior coverage data a \n" + " seed run is performed instead).\n" + " -safemode= Flag to specify a safe mode sequence where the set of unselected \n" + " tests is run without instrumentation after the set of selected \n" + " instrumented tests is run (this has the effect of ensuring all \n" + " tests are run regardless).\n" + " -shard= Break any test targets with a sharding policy into the number of \n" + " shards according to the maximum concurrency value.\n" + " -rexecfailures= Attempt to execute test targets that previously failed to execute.\n" + " -targetout= Capture of individual test run stdout, where stdout will capture \n" + " each individual test target's stdout and output each one to stdout \n" + " and file will capture each individual test target's stdout and output \n" + " each one individually to a file (multiple values are accepted).\n" + " -epolicy= Policy for handling test execution failure (test targets could not be \n" + " launched due to the binary not being built, incorrect paths, etc.), \n" + " where abort will abort the entire test sequence upon the first test\n" + " target execution failureand report a failure(along with the return \n" + " code of the test target that failed to launch), continue will continue \n" + " with the test sequence in the event of test target execution failures\n" + " and treat the test targets that failed to launch as as test failures\n" + " (along with the return codes of the test targets that failed to \n" + " launch), ignore will continue with the test sequence in the event of \n" + " test target execution failuresand treat the test targets that failed\n" + " to launch as as test passes(along with the return codes of the test \n" + " targets that failed to launch).\n" + " -fpolicy Policy for handling test failures (test targets report failing tests), \n" + " where abort will abort the entire test sequenceupon the first test \n" + " failureand report a failure and continue will continue with the test\n" + " sequence in the event of test failuresand report the test failures.\n" + " -ipolicy= Policy for handling coverage data integrity failures, where abort will \n" + " abort the test sequenceand report a failure, seed will attempt another \n" + " sequence using the seed sequence type, otherwise will abort and report \n" + " a failure (this option has no effect for regularand seed sequence \n" + " types) and rerun will attempt another sequence using the regular \n" + " sequence type, otherwise will abortand report a failure(this option has \n" + " no effect for regular sequence type).\n" + " -ppolicy= Policy for prioritizing selected test targets, where none will not \n" + " attempt any test target prioritization and locality will attempt to \n" + " prioritize test targets according to the locality of their covering \n" + " production targets in the dependency graph(if no dependency graph data \n" + " available, no prioritization will occur).\n" + " -maxconcurrency= The maximum number of concurrent test targets/shards to be in flight at \n" + " any given moment.\n" + " -ochangelist= Outputs the change list used for test selection.\n" + " -suites= The test suites to select from for this test sequence (multiple values are \n" + " allowed). The suite all has special significance and will allow tests from \n" + " any suite to be selected, however this particular suite is mutually exclusive\n" + " with other suite. Note: this option is only applicable to the regular sequence\n" + " and, if safe mode is enables, the tia and tiaorseed sequences."; + + return help; + } +} diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h new file mode 100644 index 0000000000..6958f03b80 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h @@ -0,0 +1,111 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace TestImpact +{ + //! The type of test sequence to run. + enum class TestSequenceType + { + None, //!< Runs no tests and will report a all tests successful. + Seed, //!< Removes any prior coverage data and runs all test targets with instrumentation to reseed the data from scratch. + Regular, //!< Runs all of the test targets without any instrumentation to generate coverage data (any prior coverage data is left intact). + ImpactAnalysis, //!< Uses any prior coverage data to run the instrumented subset of selected tests (if no prior coverage data a regular run is performed instead). + ImpactAnalysisOrSeed //!< Uses any prior coverage data to run the instrumented subset of selected tests (if no prior coverage data a seed run is performed instead). + }; + + //! Representation of the command line options supplied to the console frontend application. + class CommandLineOptions + { + public: + CommandLineOptions(int argc, char** argv); + static AZStd::string GetCommandLineUsageString(); + + //! Returns true if a change list file path has been supplied, otherwise false. + bool HasChangeListFile() const; + + //! Returns true if the safe mode option has been enabled, otherwise false. + bool HasSafeMode() const; + + //! Returns true if the output change list option has been enabled, otherwise false. + bool HasOutputChangeList() const; + + //! Returns the path to the runtime configuration file. + const RepoPath& GetConfigurationFile() const; + + //! Returns the path to the change list file (if any). + const AZStd::optional& GetChangeListFile() const; + + //! Returns the test sequence type to run. + TestSequenceType GetTestSequenceType() const; + + //! Returns the test prioritization policy to use. + Policy::TestPrioritization GetTestPrioritizationPolicy() const; + + //! Returns the test execution failure policy to use. + Policy::ExecutionFailure GetExecutionFailurePolicy() const; + + //! Returns the test historic test execution failure drafting policy to use. + Policy::ExecutionFailureDrafting GetExecutionFailureDraftingPolicy() const; + + //! Returns the test failure policy to use. + Policy::TestFailure GetTestFailurePolicy() const; + + //! Returns the integration failure policy to use. + Policy::IntegrityFailure GetIntegrityFailurePolicy() const; + + //! Returns the test sharding policy to use. + Policy::TestSharding GetTestShardingPolicy() const; + + //! Returns the test target standard output capture policy to use. + Policy::TargetOutputCapture GetTargetOutputCapture() const; + + //! Returns the maximum number of test targets to be in flight at any given time. + const AZStd::optional& GetMaxConcurrency() const; + + //! Returns the individual test target timeout to use (if any). + const AZStd::optional& GetTestTargetTimeout() const; + + //! Returns the global test sequence timeout to use (if any). + const AZStd::optional& GetGlobalTimeout() const; + + //! Returns the filter for test suites that will be allowed to be run. + const AZStd::unordered_set& GetSuitesFilter() const; + + private: + RepoPath m_configurationFile; + AZStd::optional m_changeListFile; + bool m_outputChangeList = false; + TestSequenceType m_testSequenceType; + Policy::TestPrioritization m_testPrioritizationPolicy = Policy::TestPrioritization::None; + Policy::ExecutionFailure m_executionFailurePolicy = Policy::ExecutionFailure::Continue; + Policy::ExecutionFailureDrafting m_executionFailureDraftingPolicy = Policy::ExecutionFailureDrafting::Always; + Policy::TestFailure m_testFailurePolicy = Policy::TestFailure::Abort; + Policy::IntegrityFailure m_integrityFailurePolicy = Policy::IntegrityFailure::Abort; + Policy::TestSharding m_testShardingPolicy = Policy::TestSharding::Never; + Policy::TargetOutputCapture m_targetOutputCapture = Policy::TargetOutputCapture::None; + AZStd::optional m_maxConcurrency; + AZStd::optional m_testTargetTimeout; + AZStd::optional m_globalTimeout; + AZStd::unordered_set m_suitesFilter; + bool m_safeMode = false; + }; +} diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsole.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsException.h similarity index 65% rename from Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsole.cpp rename to Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsException.h index 12743f4b38..fd0a58b0f5 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsole.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsException.h @@ -10,8 +10,17 @@ * */ -int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) -{ - return 0; -} +#pragma once + +#include +namespace TestImpact +{ + //! Exception for command line options. + class CommandLineOptionsException + : public Exception + { + public: + using Exception::Exception; + }; +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp new file mode 100644 index 0000000000..3315f68ae5 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp @@ -0,0 +1,77 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +namespace TestImpact +{ + //! Attempts to parse a path option value. + AZStd::optional ParsePathOption(const AZStd::string& optionName, const AZ::CommandLine& cmd) + { + const auto numSwitchValues = cmd.GetNumSwitchValues(optionName); + if (numSwitchValues) + { + AZ_TestImpact_Eval( + numSwitchValues == 1, + CommandLineOptionsException, + AZStd::string::format("Unexpected number of parameters for %s option", optionName.c_str())); + + const auto value = cmd.GetSwitchValue(optionName, 0); + AZ_TestImpact_Eval( + !value.empty(), + CommandLineOptionsException, + AZStd::string::format("%s file option value is empty", optionName.c_str())); + + return value; + } + + return AZStd::nullopt; + } + + //! Attempts to pass an unsigned integer option value. + AZStd::optional ParseUnsignedIntegerOption(const AZStd::string& optionName, const AZ::CommandLine& cmd) + { + const auto numSwitchValues = cmd.GetNumSwitchValues(optionName); + if (numSwitchValues) + { + AZ_TestImpact_Eval( + numSwitchValues == 1, + CommandLineOptionsException, + AZStd::string::format("Unexpected number of parameters for %s option", optionName.c_str())); + + const auto str = cmd.GetSwitchValue(optionName, 0); + char* end = nullptr; + auto value = strtoul(str.c_str(), &end, 0); + + AZ_TestImpact_Eval( + end != str, + CommandLineOptionsException, + AZStd::string::format("Couldn't parse unsigned integer option value: %s", str.c_str())); + + return aznumeric_caster(value); + } + + return AZStd::nullopt; + } + + //! Attempts to parse an option value in seconds. + AZStd::optional ParseSecondsOption(const AZStd::string& optionName, const AZ::CommandLine& cmd) + { + if (const auto option = ParseUnsignedIntegerOption(optionName, cmd); + option.has_value()) + { + return AZStd::chrono::seconds(option.value()); + } + + return AZStd::nullopt; + } +} diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h new file mode 100644 index 0000000000..0da7f1f16e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h @@ -0,0 +1,119 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +#include + +namespace TestImpact +{ + //! Representation of a command line option value name and its typed value. + template + using OptionValue = AZStd::pair; + + //! Representation of a binary state command line option with its two values. + template + using BinaryStateOption = AZStd::pair, OptionValue>; + + //! Representation of the values for a binary state option. + template + using BinaryStateValue = AZStd::pair; + + //! Attempts to parse the specified binary state option. + template + AZStd::optional ParseBinaryStateOption( + const AZStd::string& optionName, + const AZStd::pair, + OptionValue>& state, const AZ::CommandLine& cmd) + { + const auto numSwitchValues = cmd.GetNumSwitchValues(optionName); + if (numSwitchValues) + { + AZ_TestImpact_Eval( + numSwitchValues == 1, + CommandLineOptionsException, + AZStd::string::format("Unexpected number of parameters for %s option", optionName.c_str())); + + const auto option = cmd.GetSwitchValue(optionName, 0); + if (option == state.first.first) + { + return state.first.second; + } + else if (option == state.second.first) + { + return state.second.second; + } + + throw CommandLineOptionsException( + AZStd::string::format("Unexpected value for %s option: %s", optionName.c_str(), option.c_str())); + } + + return AZStd::nullopt; + } + + //! Attempts to pass an arbitrarily sized state option. + template + AZStd::optional ParseMultiStateOption( + const AZStd::string& optionName, + const AZStd::vector>& states, + const AZ::CommandLine& cmd) + { + const auto numSwitchValues = cmd.GetNumSwitchValues(optionName); + if (numSwitchValues) + { + AZ_TestImpact_Eval( + numSwitchValues == 1, + CommandLineOptionsException, + AZStd::string::format("Unexpected number of parameters for %s option", optionName.c_str())); + + const auto option = cmd.GetSwitchValue(optionName, 0); + for (const auto& state : states) + { + if (option == state.first) + { + return state.second; + } + } + + throw CommandLineOptionsException( + AZStd::string::format("Unexpected value for %s option: %s", optionName.c_str(), option.c_str())); + } + + return AZStd::nullopt; + } + + //! Attempts to pass a specialization of the binary state option where the command line values are "on" and "off". + template + AZStd::optional ParseOnOffOption(const AZStd::string& optionName, const AZStd::pair& states, const AZ::CommandLine& cmd) + { + return ParseBinaryStateOption(optionName, BinaryStateOption{ {"off", states.first}, { "on", states.second } }, cmd); + } + + //! Attempts to pass a specialization of the binary state option where the command line values are "abort" and "continue". + template + AZStd::optional ParseAbortContinueOption(const AZStd::string& optionName, const AZStd::pair& states, const AZ::CommandLine& cmd) + { + return ParseBinaryStateOption(optionName, BinaryStateOption{ {"abort", states.first}, { "continue", states.second } }, cmd); + } + + //! Attempts to parse a path option value. + AZStd::optional ParsePathOption(const AZStd::string& optionName, const AZ::CommandLine& cmd); + + //! Attempts to pass an unsigned integer option value. + AZStd::optional ParseUnsignedIntegerOption(const AZStd::string& optionName, const AZ::CommandLine& cmd); + + //! Attempts to parse an option value in seconds. + AZStd::optional ParseSecondsOption(const AZStd::string& optionName, const AZ::CommandLine& cmd); +} From f50f02eb4a86951b896b3c82e7d431d2284052e9 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 28 May 2021 11:38:02 +0100 Subject: [PATCH 075/233] Add console entry points --- .../Code/Source/TestImpactConsole.cpp | 29 ++ .../TestImpactConsoleApplication.h | 34 +++ .../Source/TestImpactConsoleApplication.cpp | 276 ++++++++++++++++++ 3 files changed, 339 insertions(+) create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/Source/TestImpactConsole.cpp create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleApplication.h create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleApplication.cpp diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/Source/TestImpactConsole.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/Source/TestImpactConsole.cpp new file mode 100644 index 0000000000..079519576f --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/Source/TestImpactConsole.cpp @@ -0,0 +1,29 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +int main(int argc, char** argv) +{ + AZ::AllocatorInstance::Create(); + AZ::AllocatorInstance::Create(); + + TestImpact::Console::ReturnCode returnCode = TestImpact::Console::Main(argc, argv); + + AZ::AllocatorInstance::Destroy(); + AZ::AllocatorInstance::Destroy(); + + return static_cast(returnCode); +} diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleApplication.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleApplication.h new file mode 100644 index 0000000000..aabc4f70fc --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleApplication.h @@ -0,0 +1,34 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +namespace TestImpact +{ + namespace Console + { + enum class ReturnCode : int + { + Success = 0, //!< The instigation operation(s) returned without error. + InvalidArgs, //!< The specified command line arguments were incorrect. + InvalidUnifiedDiff, //!< The specified unified diff could not be transformed into a valid change list. + InvalidConfiguration, //!< The runtime configuration is malformed. + RuntimeError, //!< The runtime encountered an error that it could not recover from. + UnhandledError, //!< The framework encountered an error that it anticipated but did not handle and could not recover from. + UnknownError, //!< An error of unknown origin was encountered that the console or runtime could not recover from. + TestFailure, //!< The test sequence had one or more test failures. + Timeout //!< The test sequence runtime exceeded the global timeout value. + }; + + [[nodiscard]] ReturnCode Main(int argc, char** argv); + } +} diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleApplication.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleApplication.cpp new file mode 100644 index 0000000000..80c08142e9 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleApplication.cpp @@ -0,0 +1,276 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace TestImpact +{ + namespace Console + { + //! Generates a string to be used for printing to the console for the specified change list. + AZStd::string GenerateChangeListString(const ChangeList& changeList) + { + AZStd::string output; + + const auto& outputFiles = [&output](const AZStd::vector& files) + { + for (const auto& file : files) + { + output += AZStd::string::format("\t%s\n", file.c_str()); + } + }; + + output += AZStd::string::format("Created files (%u):\n", changeList.m_createdFiles.size()); + outputFiles(changeList.m_createdFiles); + + output += AZStd::string::format("Updated files (%u):\n", changeList.m_updatedFiles.size()); + outputFiles(changeList.m_updatedFiles); + + output += AZStd::string::format("Deleted files (%u):\n", changeList.m_deletedFiles.size()); + outputFiles(changeList.m_deletedFiles); + + return output; + } + + //! Gets the appropriate console return code for the specified test sequence result. + ReturnCode GetReturnCodeForTestSequenceResult(TestSequenceResult result) + { + switch (result) + { + case TestSequenceResult::Success: + return ReturnCode::Success; + case TestSequenceResult::Failure: + return ReturnCode::TestFailure; + case TestSequenceResult::Timeout: + return ReturnCode::Timeout; + default: + std::cout << "Unexpected TestSequenceResult value: " << aznumeric_cast(result) << std::endl; + return ReturnCode::UnknownError; + } + } + + //! Entry point for the test impact analysis framework console front end application. + ReturnCode Main(int argc, char** argv) + { + try + { + CommandLineOptions options(argc, argv); + AZStd::optional changeList; + + // If we have a change list, check to see whether or not the client has requested the printing of said change list + if (options.HasChangeListFile()) + { + changeList = DeserializeChangeList(ReadFileContents(*options.GetChangeListFile())); + if (options.HasOutputChangeList()) + { + std::cout << "Change List:\n"; + std::cout << GenerateChangeListString(*changeList).c_str(); + + if (options.GetTestSequenceType() == TestSequenceType::None) + { + return ReturnCode::Success; + } + } + } + + // As of now, there are no other non-test operations other than printing a change list so getting this far is considered an error + AZ_TestImpact_Eval(options.GetTestSequenceType() != TestSequenceType::None, CommandLineOptionsException, "No action specified"); + + std::cout << "Constructing in-memory model of source tree and test coverage, this may take a moment...\n"; + Runtime runtime( + ConfigurationFactory(ReadFileContents(options.GetConfigurationFile())), + options.GetExecutionFailurePolicy(), + options.GetExecutionFailureDraftingPolicy(), + options.GetTestFailurePolicy(), + options.GetIntegrityFailurePolicy(), + options.GetTestShardingPolicy(), + options.GetTargetOutputCapture(), + options.GetMaxConcurrency()); + + if (runtime.HasImpactAnalysisData()) + { + std::cout << "Test impact analysis data for this repository was found.\n"; + } + else + { + + std::cout << "Test impact analysis data for this repository was not found, seed or regular sequence fallbacks will be used.\n"; + } + + TestSequence sequence(&options.GetSuitesFilter()); + + // Wrapper around impact analysis sequences to handle the case where the safe mode option is active + const auto impactAnalysisTestSequence = [&sequence, &options, &runtime, &changeList]() + { + AZ_TestImpact_Eval( + changeList.has_value(), + CommandLineOptionsException, + "Expected a change list for impact analysis but none was provided"); + + TestSequenceResult result = TestSequenceResult::Failure; + if (options.HasSafeMode()) + { + auto [selectedResult, discardedResult] = runtime.SafeImpactAnalysisTestSequence( + changeList.value(), + options.GetSuitesFilter(), + options.GetTestPrioritizationPolicy(), + options.GetTestTargetTimeout(), + options.GetGlobalTimeout(), + AZStd::ref(sequence), + AZStd::ref(sequence), + AZStd::ref(sequence)); + + // Handling the possible timeout and failure permutations of the selected and discarded test results is splitting hairs + // so apply the following, admittedly arbitrary, rules to determine what the composite test sequence result should be + if (selectedResult == TestSequenceResult::Success && discardedResult == TestSequenceResult::Success) + { + // Trivial case: both sequences succeeded + result = TestSequenceResult::Success; + } + else if (selectedResult == TestSequenceResult::Failure || discardedResult == TestSequenceResult::Failure) + { + // One sequence failed whilst the other sequence either succeeded or timed out + result = TestSequenceResult::Failure; + } + else + { + // One sequence timed out whilst the other sequence succeeded or both sequences timed out + result = TestSequenceResult::Timeout; + } + } + else + { + result = runtime.ImpactAnalysisTestSequence( + changeList.value(), + options.GetTestPrioritizationPolicy(), + options.GetTestTargetTimeout(), + options.GetGlobalTimeout(), + AZStd::ref(sequence), + AZStd::ref(sequence), + AZStd::ref(sequence)); + } + + return GetReturnCodeForTestSequenceResult(result); + }; + + switch (const auto type = options.GetTestSequenceType()) + { + case TestSequenceType::Regular: + { + const auto result = runtime.RegularTestSequence( + options.GetSuitesFilter(), + options.GetTestTargetTimeout(), + options.GetGlobalTimeout(), + AZStd::ref(sequence), + AZStd::ref(sequence), + AZStd::ref(sequence)); + + return GetReturnCodeForTestSequenceResult(result); + } + case TestSequenceType::Seed: + { + const auto result = runtime.SeededTestSequence( + options.GetTestTargetTimeout(), + options.GetGlobalTimeout(), + AZStd::ref(sequence), + AZStd::ref(sequence), + AZStd::ref(sequence)); + + return GetReturnCodeForTestSequenceResult(result); + } + case TestSequenceType::ImpactAnalysis: + { + return impactAnalysisTestSequence(); + } + case TestSequenceType::ImpactAnalysisOrSeed: + { + if (runtime.HasImpactAnalysisData()) + { + return impactAnalysisTestSequence(); + } + else + { + const auto result = runtime.SeededTestSequence( + options.GetTestTargetTimeout(), + options.GetGlobalTimeout(), + AZStd::ref(sequence), + AZStd::ref(sequence), + AZStd::ref(sequence)); + + return GetReturnCodeForTestSequenceResult(result); + } + } + default: + std::cout << "Unexpected TestSequenceType value: " << static_cast(type) << std::endl; + return ReturnCode::UnknownError; + } + } + catch (const CommandLineOptionsException& e) + { + std::cout << e.what() << std::endl; + std::cout << CommandLineOptions::GetCommandLineUsageString().c_str() << std::endl; + return ReturnCode::InvalidArgs; + } + catch (const ChangeListException& e) + { + std::cout << e.what() << std::endl; + return ReturnCode::InvalidUnifiedDiff; + } + catch (const ConfigurationException& e) + { + std::cout << e.what() << std::endl; + return ReturnCode::InvalidConfiguration; + } + catch (const RuntimeException& e) + { + std::cout << e.what() << std::endl; + return ReturnCode::RuntimeError; + } + catch (const Exception& e) + { + std::cout << e.what() << std::endl; + return ReturnCode::UnhandledError; + } + catch (const std::exception& e) + { + std::cout << e.what() << std::endl; + return ReturnCode::UnknownError; + } + catch (...) + { + std::cout << "An unknown error occurred" << std::endl; + return ReturnCode::UnknownError; + } + } + } +} From 728d3b4b980d29637d9b528548284e5772b45dd5 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 28 May 2021 12:33:27 +0100 Subject: [PATCH 076/233] Address PR comments --- .../Source/TestImpactCommandLineOptions.cpp | 10 +++++----- .../Source/TestImpactCommandLineOptions.h | 4 ++-- .../TestImpactCommandLineOptionsUtils.cpp | 20 ++++++++++--------- .../TestImpactCommandLineOptionsUtils.h | 10 +++++----- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp index e4083641e5..a6eb2e886e 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp @@ -184,8 +184,8 @@ namespace TestImpact Policy::TargetOutputCapture ParseTargetOutputCapture(const AZ::CommandLine& cmd) { - const auto numSwitchValues = cmd.GetNumSwitchValues(OptionKeys[TargetOutputCapture]); - if (numSwitchValues) + if (const auto numSwitchValues = cmd.GetNumSwitchValues(OptionKeys[TargetOutputCapture]); + numSwitchValues) { AZ_TestImpact_Eval( numSwitchValues <= 2, CommandLineOptionsException, "Unexpected parameters for target output capture option"); @@ -253,8 +253,8 @@ namespace TestImpact AZStd::unordered_set ParseSuitesFilter(const AZ::CommandLine& cmd) { AZStd::unordered_set suitesFilter; - const auto numSwitchValues = cmd.GetNumSwitchValues(OptionKeys[SuitesFilter]); - if (numSwitchValues) + if (const auto numSwitchValues = cmd.GetNumSwitchValues(OptionKeys[SuitesFilter]); + numSwitchValues) { for (auto i = 0; i < numSwitchValues; i++) { @@ -460,4 +460,4 @@ namespace TestImpact return help; } -} +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h index 6958f03b80..73961b5fcc 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h @@ -25,7 +25,7 @@ namespace TestImpact //! The type of test sequence to run. enum class TestSequenceType { - None, //!< Runs no tests and will report a all tests successful. + None, //!< Runs no tests and will report all tests successful. Seed, //!< Removes any prior coverage data and runs all test targets with instrumentation to reseed the data from scratch. Regular, //!< Runs all of the test targets without any instrumentation to generate coverage data (any prior coverage data is left intact). ImpactAnalysis, //!< Uses any prior coverage data to run the instrumented subset of selected tests (if no prior coverage data a regular run is performed instead). @@ -108,4 +108,4 @@ namespace TestImpact AZStd::unordered_set m_suitesFilter; bool m_safeMode = false; }; -} +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp index 3315f68ae5..c03ba98303 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp @@ -12,13 +12,15 @@ #include +#include + namespace TestImpact { //! Attempts to parse a path option value. AZStd::optional ParsePathOption(const AZStd::string& optionName, const AZ::CommandLine& cmd) - { - const auto numSwitchValues = cmd.GetNumSwitchValues(optionName); - if (numSwitchValues) + { + if (const auto numSwitchValues = cmd.GetNumSwitchValues(optionName); + numSwitchValues) { AZ_TestImpact_Eval( numSwitchValues == 1, @@ -40,8 +42,8 @@ namespace TestImpact //! Attempts to pass an unsigned integer option value. AZStd::optional ParseUnsignedIntegerOption(const AZStd::string& optionName, const AZ::CommandLine& cmd) { - const auto numSwitchValues = cmd.GetNumSwitchValues(optionName); - if (numSwitchValues) + if (const auto numSwitchValues = cmd.GetNumSwitchValues(optionName); + numSwitchValues) { AZ_TestImpact_Eval( numSwitchValues == 1, @@ -49,11 +51,11 @@ namespace TestImpact AZStd::string::format("Unexpected number of parameters for %s option", optionName.c_str())); const auto str = cmd.GetSwitchValue(optionName, 0); - char* end = nullptr; - auto value = strtoul(str.c_str(), &end, 0); + size_t end = 0; + auto value = AZStd::stoul(str, &end, 0); AZ_TestImpact_Eval( - end != str, + end, CommandLineOptionsException, AZStd::string::format("Couldn't parse unsigned integer option value: %s", str.c_str())); @@ -74,4 +76,4 @@ namespace TestImpact return AZStd::nullopt; } -} +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h index 0da7f1f16e..e635d05710 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h @@ -38,8 +38,8 @@ namespace TestImpact const AZStd::pair, OptionValue>& state, const AZ::CommandLine& cmd) { - const auto numSwitchValues = cmd.GetNumSwitchValues(optionName); - if (numSwitchValues) + if (const auto numSwitchValues = cmd.GetNumSwitchValues(optionName); + numSwitchValues) { AZ_TestImpact_Eval( numSwitchValues == 1, @@ -70,8 +70,8 @@ namespace TestImpact const AZStd::vector>& states, const AZ::CommandLine& cmd) { - const auto numSwitchValues = cmd.GetNumSwitchValues(optionName); - if (numSwitchValues) + if (const auto numSwitchValues = cmd.GetNumSwitchValues(optionName); + numSwitchValues) { AZ_TestImpact_Eval( numSwitchValues == 1, @@ -116,4 +116,4 @@ namespace TestImpact //! Attempts to parse an option value in seconds. AZStd::optional ParseSecondsOption(const AZStd::string& optionName, const AZ::CommandLine& cmd); -} +} // namespace TestImpact From c63de35b688aa4479133b8f456339148cd4476b3 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 28 May 2021 15:39:28 +0100 Subject: [PATCH 077/233] Address PR feedback --- .../Source/TestImpactCommandLineOptionsUtils.cpp | 10 +++++----- .../Source/TestImpactCommandLineOptionsUtils.h | 15 +++++++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp index c03ba98303..b96716059f 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp @@ -50,14 +50,14 @@ namespace TestImpact CommandLineOptionsException, AZStd::string::format("Unexpected number of parameters for %s option", optionName.c_str())); - const auto str = cmd.GetSwitchValue(optionName, 0); - size_t end = 0; - auto value = AZStd::stoul(str, &end, 0); + const auto strValue = cmd.GetSwitchValue(optionName, 0); + size_t successfulParse = 0; // Will be non-zero if the parse was successful + auto value = AZStd::stoul(strValue, &successfulParse, 0); AZ_TestImpact_Eval( - end, + successfulParse, CommandLineOptionsException, - AZStd::string::format("Couldn't parse unsigned integer option value: %s", str.c_str())); + AZStd::string::format("Couldn't parse unsigned integer option value: %s", strValue.c_str())); return aznumeric_caster(value); } diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h index e635d05710..1708efadd9 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h @@ -47,13 +47,15 @@ namespace TestImpact AZStd::string::format("Unexpected number of parameters for %s option", optionName.c_str())); const auto option = cmd.GetSwitchValue(optionName, 0); - if (option == state.first.first) + if (const auto& [optionValueText, optionValue] = state.first; + option == optionValueText) { - return state.first.second; + return optionValue; } - else if (option == state.second.first) + if (const auto& [optionValueText, optionValue] = state.second; + option == optionValueText) { - return state.second.second; + return optionValue; } throw CommandLineOptionsException( @@ -81,9 +83,10 @@ namespace TestImpact const auto option = cmd.GetSwitchValue(optionName, 0); for (const auto& state : states) { - if (option == state.first) + if (const auto& [optionValueText, optionValue] = state; + option == optionValueText) { - return state.second; + return optionValue; } } From f21794b193b314c63a5404fd5df75569266d5af0 Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 28 May 2021 15:55:13 +0100 Subject: [PATCH 078/233] Add missing namespace comments --- .../TestImpactFramework/TestImpactConsoleApplication.h | 5 +++-- .../Static/Code/Source/TestImpactConsoleApplication.cpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleApplication.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleApplication.h index aabc4f70fc..c6ad90a36f 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleApplication.h +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleApplication.h @@ -29,6 +29,7 @@ namespace TestImpact Timeout //!< The test sequence runtime exceeded the global timeout value. }; + //! Entry point for the console front end application. [[nodiscard]] ReturnCode Main(int argc, char** argv); - } -} + } // namespace Console +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleApplication.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleApplication.cpp index 80c08142e9..b3bdf6ec25 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleApplication.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleApplication.cpp @@ -272,5 +272,5 @@ namespace TestImpact return ReturnCode::UnknownError; } } - } -} + } // namespace Console +} // namespace TestImpact From 00a529ad74d139419ea2e4f331b21509a01dcf85 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri, 28 May 2021 14:56:52 -0500 Subject: [PATCH 079/233] Fix AssImpTransformImporter logic for bone nodes For bone nodes, the Transform is computed by multiplying the parent offsetMatrix by the inverse of the node's offsetMatrix Note that this currently disables the LimitBoneWeights option since that results in the removal of bone nodes that are not attached to a mesh. Without the bones there is no way to retrieve the offsetMatrix, so the Transform cannot be computed correctly Fixes LYN-3755 --- .../Importers/AssImpTransformImporter.cpp | 59 ++++++++++++++++--- .../SDKWrapper/AssImpSceneWrapper.cpp | 5 +- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp index 5357c32fa9..bcc007e3a7 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp @@ -46,22 +46,69 @@ namespace AZ serializeContext->Class()->Version(1); } } - + + void GetAllBones(const aiScene* scene, AZStd::unordered_map& boneLookup) + { + for (unsigned meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex) + { + const aiMesh* mesh = scene->mMeshes[meshIndex]; + + for (unsigned boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex) + { + const aiBone* bone = mesh->mBones[boneIndex]; + + boneLookup[bone->mName.C_Str()] = bone; + } + } + } + Events::ProcessingResult AssImpTransformImporter::ImportTransform(AssImpSceneNodeAppendedContext& context) { AZ_TraceContext("Importer", "transform"); const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); - + if (currentNode == scene->mRootNode || IsPivotNode(currentNode->mName)) { return Events::ProcessingResult::Ignored; } - aiMatrix4x4 combinedTransform = GetConcatenatedLocalTransform(currentNode); + AZStd::unordered_map boneLookup; + GetAllBones(scene, boneLookup); + + auto boneIterator = boneLookup.find(currentNode->mName.C_Str()); + const bool isBone = boneIterator != boneLookup.end(); + + aiMatrix4x4 combinedTransform; + + if (isBone) + { + auto parentNode = currentNode->mParent; + + aiMatrix4x4 offsetMatrix = boneIterator->second->mOffsetMatrix; + aiMatrix4x4 parentOffset {}; + + auto parentBoneIterator = boneLookup.find(parentNode->mName.C_Str()); + + if (parentNode && parentBoneIterator != boneLookup.end()) + { + const auto& parentBone = parentBoneIterator->second; + + parentOffset = parentBone->mOffsetMatrix; + } + + auto inverseOffset = offsetMatrix; + inverseOffset.Inverse(); + + combinedTransform = parentOffset * inverseOffset; + } + else + { + combinedTransform = GetConcatenatedLocalTransform(currentNode); + } DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform); - + context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform); context.m_sourceSceneSystem.ConvertUnit(localTransform); @@ -105,9 +152,7 @@ namespace AZ } else { - bool addedData = context.m_scene.GetGraph().SetContent( - context.m_currentGraphPosition, - transformData); + bool addedData = context.m_scene.GetGraph().SetContent(context.m_currentGraphPosition, transformData); AZ_Error(SceneAPI::Utilities::ErrorWindow, addedData, "Failed to add node data"); return addedData ? Events::ProcessingResult::Success : Events::ProcessingResult::Failure; diff --git a/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp b/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp index 791af4bf68..9186a2fb7a 100644 --- a/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp +++ b/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp @@ -69,13 +69,14 @@ namespace AZ // aiProcess_JoinIdenticalVertices is not enabled because O3DE has a mesh optimizer that also does this, // this flag is disabled to keep AssImp output similar to FBX SDK to reduce downstream bugs for the initial AssImp release. // There's currently a minimum of properties and flags set to maximize compatibility with the existing node graph. + + // aiProcess_LimitBoneWeights is not enabled because it will remove bones which are not associated with a mesh. + // This results in the loss of the offset matrix data for nodes without a mesh which is required for the Transform Importer. m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false); m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, false); m_sceneFileName = fileName; m_assImpScene = m_importer.ReadFile(fileName, aiProcess_Triangulate //Triangulates all faces of all meshes - | aiProcess_LimitBoneWeights //Limits the number of bones that can affect a vertex to a maximum value - //dropping the least important and re-normalizing | aiProcess_GenNormals); //Generate normals for meshes #if AZ_TRAIT_COMPILER_SUPPORT_CSIGNAL From 9fe91ec7056b27dca039dbf80e43e23278c3402b Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 1 Jun 2021 12:46:00 +0100 Subject: [PATCH 080/233] Address PR comments --- .../Code/Source/TestImpactConsole.cpp | 4 +- ...eApplication.h => TestImpactConsoleMain.h} | 0 ...lication.cpp => TestImpactConsoleMain.cpp} | 141 +++++++++--------- .../Windows/platform_windows_files.cmake | 1 + .../Code/Source/TestImpactRepoPath.cpp | 22 +-- .../Runtime/Code/Source/TestImpactRuntime.cpp | 10 +- .../Code/Source/TestImpactRuntimeUtils.h | 2 +- 7 files changed, 92 insertions(+), 88 deletions(-) rename Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/{TestImpactConsoleApplication.h => TestImpactConsoleMain.h} (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/{TestImpactConsoleApplication.cpp => TestImpactConsoleMain.cpp} (69%) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/Source/TestImpactConsole.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/Source/TestImpactConsole.cpp index 079519576f..631eb17b32 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/Source/TestImpactConsole.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/Source/TestImpactConsole.cpp @@ -10,7 +10,7 @@ * */ -#include +#include #include #include @@ -22,8 +22,8 @@ int main(int argc, char** argv) TestImpact::Console::ReturnCode returnCode = TestImpact::Console::Main(argc, argv); - AZ::AllocatorInstance::Destroy(); AZ::AllocatorInstance::Destroy(); + AZ::AllocatorInstance::Destroy(); return static_cast(returnCode); } diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleApplication.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleMain.h similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleApplication.h rename to Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleMain.h diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleApplication.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp similarity index 69% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleApplication.cpp rename to Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp index b3bdf6ec25..0d4f08406e 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleApplication.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include #include @@ -79,6 +79,64 @@ namespace TestImpact } } + //! Wrapper around impact analysis sequences to handle the case where the safe mode option is active. + ReturnCode WrappedImpactAnalysisTestSequence( + TestSequenceEventHandler& sequenceEventHandler, + const CommandLineOptions& options, + Runtime& runtime, + const AZStd::optional& changeList) + { + AZ_TestImpact_Eval( + changeList.has_value(), + CommandLineOptionsException, + "Expected a change list for impact analysis but none was provided"); + + TestSequenceResult result = TestSequenceResult::Failure; + if (options.HasSafeMode()) + { + auto [selectedResult, discardedResult] = runtime.SafeImpactAnalysisTestSequence( + changeList.value(), + options.GetSuitesFilter(), + options.GetTestPrioritizationPolicy(), + options.GetTestTargetTimeout(), + options.GetGlobalTimeout(), + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler)); + + // Handling the possible timeout and failure permutations of the selected and discarded test results is splitting hairs + // so apply the following, admittedly arbitrary, rules to determine what the composite test sequence result should be + if (selectedResult == TestSequenceResult::Success && discardedResult == TestSequenceResult::Success) + { + // Trivial case: both sequences succeeded + result = TestSequenceResult::Success; + } + else if (selectedResult == TestSequenceResult::Failure || discardedResult == TestSequenceResult::Failure) + { + // One sequence failed whilst the other sequence either succeeded or timed out + result = TestSequenceResult::Failure; + } + else + { + // One sequence timed out whilst the other sequence succeeded or both sequences timed out + result = TestSequenceResult::Timeout; + } + } + else + { + result = runtime.ImpactAnalysisTestSequence( + changeList.value(), + options.GetTestPrioritizationPolicy(), + options.GetTestTargetTimeout(), + options.GetGlobalTimeout(), + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler)); + } + + return GetReturnCodeForTestSequenceResult(result); + }; + //! Entry point for the test impact analysis framework console front end application. ReturnCode Main(int argc, char** argv) { @@ -123,65 +181,10 @@ namespace TestImpact } else { - std::cout << "Test impact analysis data for this repository was not found, seed or regular sequence fallbacks will be used.\n"; } - TestSequence sequence(&options.GetSuitesFilter()); - - // Wrapper around impact analysis sequences to handle the case where the safe mode option is active - const auto impactAnalysisTestSequence = [&sequence, &options, &runtime, &changeList]() - { - AZ_TestImpact_Eval( - changeList.has_value(), - CommandLineOptionsException, - "Expected a change list for impact analysis but none was provided"); - - TestSequenceResult result = TestSequenceResult::Failure; - if (options.HasSafeMode()) - { - auto [selectedResult, discardedResult] = runtime.SafeImpactAnalysisTestSequence( - changeList.value(), - options.GetSuitesFilter(), - options.GetTestPrioritizationPolicy(), - options.GetTestTargetTimeout(), - options.GetGlobalTimeout(), - AZStd::ref(sequence), - AZStd::ref(sequence), - AZStd::ref(sequence)); - - // Handling the possible timeout and failure permutations of the selected and discarded test results is splitting hairs - // so apply the following, admittedly arbitrary, rules to determine what the composite test sequence result should be - if (selectedResult == TestSequenceResult::Success && discardedResult == TestSequenceResult::Success) - { - // Trivial case: both sequences succeeded - result = TestSequenceResult::Success; - } - else if (selectedResult == TestSequenceResult::Failure || discardedResult == TestSequenceResult::Failure) - { - // One sequence failed whilst the other sequence either succeeded or timed out - result = TestSequenceResult::Failure; - } - else - { - // One sequence timed out whilst the other sequence succeeded or both sequences timed out - result = TestSequenceResult::Timeout; - } - } - else - { - result = runtime.ImpactAnalysisTestSequence( - changeList.value(), - options.GetTestPrioritizationPolicy(), - options.GetTestTargetTimeout(), - options.GetGlobalTimeout(), - AZStd::ref(sequence), - AZStd::ref(sequence), - AZStd::ref(sequence)); - } - - return GetReturnCodeForTestSequenceResult(result); - }; + TestSequenceEventHandler sequenceEventHandler(&options.GetSuitesFilter()); switch (const auto type = options.GetTestSequenceType()) { @@ -191,9 +194,9 @@ namespace TestImpact options.GetSuitesFilter(), options.GetTestTargetTimeout(), options.GetGlobalTimeout(), - AZStd::ref(sequence), - AZStd::ref(sequence), - AZStd::ref(sequence)); + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler)); return GetReturnCodeForTestSequenceResult(result); } @@ -202,30 +205,30 @@ namespace TestImpact const auto result = runtime.SeededTestSequence( options.GetTestTargetTimeout(), options.GetGlobalTimeout(), - AZStd::ref(sequence), - AZStd::ref(sequence), - AZStd::ref(sequence)); + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler)); return GetReturnCodeForTestSequenceResult(result); } case TestSequenceType::ImpactAnalysis: { - return impactAnalysisTestSequence(); + return WrappedImpactAnalysisTestSequence(sequenceEventHandler, options, runtime, changeList); } case TestSequenceType::ImpactAnalysisOrSeed: { if (runtime.HasImpactAnalysisData()) { - return impactAnalysisTestSequence(); + return WrappedImpactAnalysisTestSequence(sequenceEventHandler, options, runtime, changeList); } else { const auto result = runtime.SeededTestSequence( options.GetTestTargetTimeout(), options.GetGlobalTimeout(), - AZStd::ref(sequence), - AZStd::ref(sequence), - AZStd::ref(sequence)); + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler)); return GetReturnCodeForTestSequenceResult(result); } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/platform_windows_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/platform_windows_files.cmake index 5f5e8fc3d1..e994bcdbbd 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/platform_windows_files.cmake +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -17,4 +17,5 @@ set(FILES Process/TestImpactWin32_Pipe.cpp Process/TestImpactWin32_Pipe.h TestEngine/JobRunner/TestImpactWin32_TestTargetExtension.cpp + TestEngine/TestImpactWin32_TestEngineJobFailure.cpp ) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRepoPath.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRepoPath.cpp index b2ffed2caf..86b9cbbed5 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRepoPath.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRepoPath.cpp @@ -32,70 +32,70 @@ namespace TestImpact return *this; } - inline RepoPath operator/(const RepoPath& lhs, const AZ::IO::PathView& rhs) + RepoPath operator/(const RepoPath& lhs, const AZ::IO::PathView& rhs) { RepoPath result(lhs); result.m_path /= RepoPath(rhs).m_path; return result; } - inline RepoPath operator/(const RepoPath& lhs, AZStd::string_view rhs) + RepoPath operator/(const RepoPath& lhs, AZStd::string_view rhs) { RepoPath result(lhs); result.m_path /= RepoPath(rhs).m_path; return result; } - inline RepoPath operator/(const RepoPath& lhs, const RepoPath::value_type* rhs) + RepoPath operator/(const RepoPath& lhs, const RepoPath::value_type* rhs) { RepoPath result(lhs); result.m_path /= RepoPath(rhs).m_path; return result; } - inline RepoPath operator/(const RepoPath& lhs, const RepoPath& rhs) + RepoPath operator/(const RepoPath& lhs, const RepoPath& rhs) { RepoPath result(lhs); result.m_path /= rhs.m_path; return result; } - inline RepoPath& RepoPath::operator/=(const AZ::IO::PathView& rhs) + RepoPath& RepoPath::operator/=(const AZ::IO::PathView& rhs) { m_path /= RepoPath(rhs).m_path; return *this; } - inline RepoPath& RepoPath::operator/=(AZStd::string_view rhs) + RepoPath& RepoPath::operator/=(AZStd::string_view rhs) { m_path /= RepoPath(rhs).m_path; return *this; } - inline RepoPath& RepoPath::operator/=(const RepoPath::value_type* rhs) + RepoPath& RepoPath::operator/=(const RepoPath::value_type* rhs) { m_path /= RepoPath(rhs).m_path; return *this; } - inline RepoPath& RepoPath::operator/=(const RepoPath& rhs) + RepoPath& RepoPath::operator/=(const RepoPath& rhs) { m_path /= rhs.m_path; return *this; } - inline bool operator==(const RepoPath& lhs, const RepoPath& rhs) noexcept + bool operator==(const RepoPath& lhs, const RepoPath& rhs) noexcept { return lhs.m_path.Compare(rhs.m_path) == 0; } - inline bool operator!=(const RepoPath& lhs, const RepoPath& rhs) noexcept + bool operator!=(const RepoPath& lhs, const RepoPath& rhs) noexcept { return lhs.m_path.Compare(rhs.m_path) != 0; } - inline bool operator<([[maybe_unused]] const RepoPath& lhs, [[maybe_unused]] const RepoPath& rhs) noexcept + bool operator<([[maybe_unused]] const RepoPath& lhs, [[maybe_unused]] const RepoPath& rhs) noexcept { return lhs.m_path.String() < rhs.m_path.String(); } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp index 9c9351244f..e02bee03c6 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp @@ -309,7 +309,7 @@ namespace TestImpact if (testSequenceEndCallback.has_value()) { - (*testSequenceEndCallback)(CreateSequenceFailureReport(testJobs), timer.Elapsed()); + (*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed()); } return result; @@ -353,7 +353,7 @@ namespace TestImpact if (testSequenceEndCallback.has_value()) { - (*testSequenceEndCallback)(CreateSequenceFailureReport(testJobs), timer.Elapsed()); + (*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed()); } return result; @@ -419,8 +419,8 @@ namespace TestImpact if (testSequenceEndCallback.has_value()) { (*testSequenceEndCallback)( - CreateSequenceFailureReport(selectedTestJobs), - CreateSequenceFailureReport(discardedTestJobs), + GenerateSequenceFailureReport(selectedTestJobs), + GenerateSequenceFailureReport(discardedTestJobs), timer.Elapsed()); } @@ -471,7 +471,7 @@ namespace TestImpact if (testSequenceEndCallback.has_value()) { - (*testSequenceEndCallback)(CreateSequenceFailureReport(testJobs), timer.Elapsed()); + (*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed()); } return result; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h index e9d1c5425b..56775b1c58 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h @@ -114,7 +114,7 @@ namespace TestImpact } case Client::TestRunResult::TestFailures: { - testRunFailures.push_back(ExtractTestRunFailure(testJob)); + testRunFailures.push_back(GenerateTestRunFailure(testJob)); break; } default: From 6432582b7f228147b22b864d96db0a7efcb51761 Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 1 Jun 2021 15:58:02 +0100 Subject: [PATCH 081/233] Add configuration factory --- .../Source/TestImpactConfigurationFactory.cpp | 210 ++++++++++++++++++ .../Source/TestImpactConfigurationFactory.h | 18 ++ 2 files changed, 228 insertions(+) create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.cpp create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.h diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.cpp new file mode 100644 index 0000000000..a6e8ee9639 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.cpp @@ -0,0 +1,210 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include + +#include +#include +#include + + +namespace TestImpact +{ + + //! Returns an absolute path for a path relative to the specified root. + RepoPath GetAbsPathFromRelPath(const RepoPath& root, const RepoPath& rel) + { + return root / rel; + } + + ConfigMeta ParseConfigMeta(const rapidjson::Value& meta) + { + ConfigMeta configMeta; + configMeta.m_platform = meta["platform"].GetString(); + return configMeta; + } + + RepoConfig ParseRepoConfig(const rapidjson::Value& repo) + { + RepoConfig repoConfig; + repoConfig.m_root = repo["root"].GetString(); + return repoConfig; + } + + WorkspaceConfig::Temp ParseTempWorkspaceConfig(const rapidjson::Value& tempWorkspace) + { + WorkspaceConfig::Temp tempWorkspaceConfig; + tempWorkspaceConfig.m_root = tempWorkspace["root"].GetString(); + tempWorkspaceConfig.m_artifactDirectory = + GetAbsPathFromRelPath(tempWorkspaceConfig.m_root, tempWorkspace["relative_paths"]["artifact_dir"].GetString()); + return tempWorkspaceConfig; + } + + WorkspaceConfig::Active ParseActiveWorkspaceConfig(const rapidjson::Value& activeWorkspace) + { + WorkspaceConfig::Active activeWorkspaceConfig; + const auto& relativePaths = activeWorkspace["relative_paths"]; + activeWorkspaceConfig.m_root = activeWorkspace["root"].GetString(); + activeWorkspaceConfig.m_enumerationCacheDirectory + = GetAbsPathFromRelPath(activeWorkspaceConfig.m_root, relativePaths["enumeration_cache_dir"].GetString()); + activeWorkspaceConfig.m_sparTIAFile + = GetAbsPathFromRelPath(activeWorkspaceConfig.m_root, relativePaths["test_impact_data_file"].GetString()); + return activeWorkspaceConfig; + } + + WorkspaceConfig ParseWorkspaceConfig(const rapidjson::Value& workspace) + { + WorkspaceConfig workspaceConfig; + workspaceConfig.m_temp = ParseTempWorkspaceConfig(workspace["temp"]); + workspaceConfig.m_active = ParseActiveWorkspaceConfig(workspace["active"]); + return workspaceConfig; + } + + BuildTargetDescriptorConfig ParseBuildTargetDescriptorConfig(const rapidjson::Value& buildTargetDescriptor) + { + BuildTargetDescriptorConfig buildTargetDescriptorConfig; + const auto& targetSources = buildTargetDescriptor["target_sources"]; + const auto& staticTargetSources = targetSources["static"]; + const auto& autogenTargetSources = targetSources["autogen"]; + buildTargetDescriptorConfig.m_mappingDirectory = buildTargetDescriptor["dir"].GetString(); + const auto& staticInclusionFilters = staticTargetSources["include_filters"].GetArray(); + if (!staticInclusionFilters.Empty()) + { + buildTargetDescriptorConfig.m_staticInclusionFilters.reserve(staticInclusionFilters.Size()); + for (const auto& staticInclusionFilter : staticInclusionFilters) + { + buildTargetDescriptorConfig.m_staticInclusionFilters.push_back(staticInclusionFilter.GetString()); + } + } + + buildTargetDescriptorConfig.m_inputOutputPairer = autogenTargetSources["input_output_pairer"].GetString(); + const auto& inputInclusionFilters = autogenTargetSources["input"]["include_filters"].GetArray(); + if (!inputInclusionFilters.Empty()) + { + buildTargetDescriptorConfig.m_inputInclusionFilters.reserve(inputInclusionFilters.Size()); + for (const auto& inputInclusionFilter : inputInclusionFilters) + { + buildTargetDescriptorConfig.m_inputInclusionFilters.push_back(inputInclusionFilter.GetString()); + } + } + + return buildTargetDescriptorConfig; + } + + DependencyGraphDataConfig ParseDependencyGraphDataConfig(const rapidjson::Value& dependencyGraphData) + { + DependencyGraphDataConfig dependencyGraphDataConfig; + const auto& matchers = dependencyGraphData["matchers"]; + dependencyGraphDataConfig.m_graphDirectory = dependencyGraphData["dir"].GetString(); + dependencyGraphDataConfig.m_targetDependencyFileMatcher = matchers["target_dependency_file"].GetString(); + dependencyGraphDataConfig.m_targetVertexMatcher = matchers["target_vertex"].GetString(); + return dependencyGraphDataConfig; + } + + TestTargetMetaConfig ParseTestTargetMetaConfig(const rapidjson::Value& testTargetMeta) + { + TestTargetMetaConfig testTargetMetaConfig; + testTargetMetaConfig.m_metaFile = testTargetMeta["file"].GetString(); + return testTargetMetaConfig; + } + + TestEngineConfig ParseTestEngineConfig(const rapidjson::Value& testEngine) + { + TestEngineConfig testEngineConfig; + testEngineConfig.m_testRunner.m_binary = testEngine["test_runner"]["bin"].GetString(); + testEngineConfig.m_instrumentation.m_binary = testEngine["instrumentation"]["bin"].GetString(); + return testEngineConfig; + } + TargetConfig ParseTargetConfig(const rapidjson::Value& target) + { + TargetConfig targetConfig; + targetConfig.m_outputDirectory = target["dir"].GetString(); + const auto& testExcludes = + target["exclude"].GetArray(); + if (!testExcludes.Empty()) + { + targetConfig.m_excludedTestTargets.reserve(testExcludes.Size()); + for (const auto& testExclude : testExcludes) + { + targetConfig.m_excludedTestTargets.push_back(testExclude.GetString()); + } + } + const auto& testShards = + target["shard"].GetArray(); + if (!testShards.Empty()) + { + targetConfig.m_shardedTestTargets.reserve(testShards.Size()); + for (const auto& testShard : testShards) + { + const auto getShardingConfiguration = [](const AZStd::string& config) + { + if (config == "fixture_contiguous") + { + return ShardConfiguration::FixtureContiguous; + } + else if (config == "fixture_interleaved") + { + return ShardConfiguration::FixtureInterleaved; + } + else if (config == "test_contiguous") + { + return ShardConfiguration::TestContiguous; + } + else if (config == "test_interleaved") + { + return ShardConfiguration::TestInterleaved; + } + else if (config == "never") + { + return ShardConfiguration::Never; + } + else + { + throw ConfigurationException(AZStd::string::format("Unexpected sharding configuration: %s", config.c_str())); + } + }; + + TargetConfig::ShardedTarget shard; + shard.m_name = testShard["target"].GetString(); + shard.m_configuration = getShardingConfiguration(testShard["policy"].GetString()); + targetConfig.m_shardedTestTargets.push_back(AZStd::move(shard)); + } + } + + return targetConfig; + } + + RuntimeConfig ConfigurationFactory(const AZStd::string& configurationData) + { + RuntimeConfig runtimeConfig; + rapidjson::Document configurationFile; + + if (configurationFile.Parse(configurationData.c_str()).HasParseError()) + { + throw TestImpact::ConfigurationException("Could not parse runtimeConfig data, JSON has errors"); + } + + const auto& staticArtifacts = configurationFile["artifacts"]["static"]; + runtimeConfig.m_meta = ParseConfigMeta(configurationFile["meta"]); + runtimeConfig.m_repo = ParseRepoConfig(configurationFile["repo"]); + runtimeConfig.m_workspace = ParseWorkspaceConfig(configurationFile["workspace"]); + runtimeConfig.m_buildTargetDescriptor = ParseBuildTargetDescriptorConfig(staticArtifacts["build_target_descriptor"]); + runtimeConfig.m_dependencyGraphData = ParseDependencyGraphDataConfig(staticArtifacts["dependency_graph_data"]); + runtimeConfig.m_testTargetMeta = ParseTestTargetMetaConfig(staticArtifacts["static"]["test_target_meta"]); + runtimeConfig.m_testEngine = ParseTestEngineConfig(configurationFile["test_engine"]); + runtimeConfig.m_target = ParseTargetConfig(configurationFile["target"]); + + return runtimeConfig; + } +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.h new file mode 100644 index 0000000000..2cc75ab0fa --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.h @@ -0,0 +1,18 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + RuntimeConfig ConfigurationFactory(const AZStd::string& configurationData); +} // namespace TestImpact From c7753f472bbd7e248689a273125b3b75d7b63ead Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 1 Jun 2021 16:11:25 +0100 Subject: [PATCH 082/233] Add API comment and rename factory --- .../Console/Static/Code/Source/TestImpactConsoleMain.cpp | 4 ++-- ...nFactory.cpp => TestImpactRuntimeConfigurationFactory.cpp} | 4 ++-- ...ationFactory.h => TestImpactRuntimeConfigurationFactory.h} | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) rename Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/{TestImpactConfigurationFactory.cpp => TestImpactRuntimeConfigurationFactory.cpp} (98%) rename Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/{TestImpactConfigurationFactory.h => TestImpactRuntimeConfigurationFactory.h} (76%) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp index 0d4f08406e..84e16c2332 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -166,7 +166,7 @@ namespace TestImpact std::cout << "Constructing in-memory model of source tree and test coverage, this may take a moment...\n"; Runtime runtime( - ConfigurationFactory(ReadFileContents(options.GetConfigurationFile())), + RuntimeConfigurationFactory(ReadFileContents(options.GetConfigurationFile())), options.GetExecutionFailurePolicy(), options.GetExecutionFailureDraftingPolicy(), options.GetTestFailurePolicy(), diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp similarity index 98% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.cpp rename to Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp index a6e8ee9639..4ac97bef49 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp @@ -12,7 +12,7 @@ #include -#include +#include #include #include @@ -185,7 +185,7 @@ namespace TestImpact return targetConfig; } - RuntimeConfig ConfigurationFactory(const AZStd::string& configurationData) + RuntimeConfig RuntimeConfigurationFactory(const AZStd::string& configurationData) { RuntimeConfig runtimeConfig; rapidjson::Document configurationFile; diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.h similarity index 76% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.h rename to Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.h index 2cc75ab0fa..ac7dbbac94 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConfigurationFactory.h +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.h @@ -14,5 +14,6 @@ namespace TestImpact { - RuntimeConfig ConfigurationFactory(const AZStd::string& configurationData); + //! Parses the configuration data (in JSON format) and returns the constructed runtime configuration. + RuntimeConfig RuntimeConfigurationFactory(const AZStd::string& configurationData); } // namespace TestImpact From 9be54d4e5b595484c60097529cdeda2d6880ed12 Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 1 Jun 2021 16:31:23 +0100 Subject: [PATCH 083/233] Address PR comments --- .../TestImpactRuntimeConfigurationFactory.cpp | 105 ++++++++---------- 1 file changed, 47 insertions(+), 58 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp index 4ac97bef49..4b473b7190 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp @@ -79,24 +79,19 @@ namespace TestImpact const auto& autogenTargetSources = targetSources["autogen"]; buildTargetDescriptorConfig.m_mappingDirectory = buildTargetDescriptor["dir"].GetString(); const auto& staticInclusionFilters = staticTargetSources["include_filters"].GetArray(); - if (!staticInclusionFilters.Empty()) + + buildTargetDescriptorConfig.m_staticInclusionFilters.reserve(staticInclusionFilters.Size()); + for (const auto& staticInclusionFilter : staticInclusionFilters) { - buildTargetDescriptorConfig.m_staticInclusionFilters.reserve(staticInclusionFilters.Size()); - for (const auto& staticInclusionFilter : staticInclusionFilters) - { - buildTargetDescriptorConfig.m_staticInclusionFilters.push_back(staticInclusionFilter.GetString()); - } + buildTargetDescriptorConfig.m_staticInclusionFilters.push_back(staticInclusionFilter.GetString()); } buildTargetDescriptorConfig.m_inputOutputPairer = autogenTargetSources["input_output_pairer"].GetString(); const auto& inputInclusionFilters = autogenTargetSources["input"]["include_filters"].GetArray(); - if (!inputInclusionFilters.Empty()) + buildTargetDescriptorConfig.m_inputInclusionFilters.reserve(inputInclusionFilters.Size()); + for (const auto& inputInclusionFilter : inputInclusionFilters) { - buildTargetDescriptorConfig.m_inputInclusionFilters.reserve(inputInclusionFilters.Size()); - for (const auto& inputInclusionFilter : inputInclusionFilters) - { - buildTargetDescriptorConfig.m_inputInclusionFilters.push_back(inputInclusionFilter.GetString()); - } + buildTargetDescriptorConfig.m_inputInclusionFilters.push_back(inputInclusionFilter.GetString()); } return buildTargetDescriptorConfig; @@ -126,60 +121,54 @@ namespace TestImpact testEngineConfig.m_instrumentation.m_binary = testEngine["instrumentation"]["bin"].GetString(); return testEngineConfig; } + TargetConfig ParseTargetConfig(const rapidjson::Value& target) { TargetConfig targetConfig; targetConfig.m_outputDirectory = target["dir"].GetString(); - const auto& testExcludes = - target["exclude"].GetArray(); - if (!testExcludes.Empty()) + const auto& testExcludes = target["exclude"].GetArray(); + targetConfig.m_excludedTestTargets.reserve(testExcludes.Size()); + for (const auto& testExclude : testExcludes) { - targetConfig.m_excludedTestTargets.reserve(testExcludes.Size()); - for (const auto& testExclude : testExcludes) - { - targetConfig.m_excludedTestTargets.push_back(testExclude.GetString()); - } + targetConfig.m_excludedTestTargets.push_back(testExclude.GetString()); } - const auto& testShards = - target["shard"].GetArray(); - if (!testShards.Empty()) + + const auto& testShards = target["shard"].GetArray(); + targetConfig.m_shardedTestTargets.reserve(testShards.Size()); + for (const auto& testShard : testShards) { - targetConfig.m_shardedTestTargets.reserve(testShards.Size()); - for (const auto& testShard : testShards) + const auto getShardingConfiguration = [](const AZStd::string& config) { - const auto getShardingConfiguration = [](const AZStd::string& config) + if (config == "fixture_contiguous") + { + return ShardConfiguration::FixtureContiguous; + } + else if (config == "fixture_interleaved") { - if (config == "fixture_contiguous") - { - return ShardConfiguration::FixtureContiguous; - } - else if (config == "fixture_interleaved") - { - return ShardConfiguration::FixtureInterleaved; - } - else if (config == "test_contiguous") - { - return ShardConfiguration::TestContiguous; - } - else if (config == "test_interleaved") - { - return ShardConfiguration::TestInterleaved; - } - else if (config == "never") - { - return ShardConfiguration::Never; - } - else - { - throw ConfigurationException(AZStd::string::format("Unexpected sharding configuration: %s", config.c_str())); - } - }; - - TargetConfig::ShardedTarget shard; - shard.m_name = testShard["target"].GetString(); - shard.m_configuration = getShardingConfiguration(testShard["policy"].GetString()); - targetConfig.m_shardedTestTargets.push_back(AZStd::move(shard)); - } + return ShardConfiguration::FixtureInterleaved; + } + else if (config == "test_contiguous") + { + return ShardConfiguration::TestContiguous; + } + else if (config == "test_interleaved") + { + return ShardConfiguration::TestInterleaved; + } + else if (config == "never") + { + return ShardConfiguration::Never; + } + else + { + throw ConfigurationException(AZStd::string::format("Unexpected sharding configuration: %s", config.c_str())); + } + }; + + TargetConfig::ShardedTarget shard; + shard.m_name = testShard["target"].GetString(); + shard.m_configuration = getShardingConfiguration(testShard["policy"].GetString()); + targetConfig.m_shardedTestTargets.push_back(AZStd::move(shard)); } return targetConfig; @@ -187,7 +176,6 @@ namespace TestImpact RuntimeConfig RuntimeConfigurationFactory(const AZStd::string& configurationData) { - RuntimeConfig runtimeConfig; rapidjson::Document configurationFile; if (configurationFile.Parse(configurationData.c_str()).HasParseError()) @@ -195,6 +183,7 @@ namespace TestImpact throw TestImpact::ConfigurationException("Could not parse runtimeConfig data, JSON has errors"); } + RuntimeConfig runtimeConfig; const auto& staticArtifacts = configurationFile["artifacts"]["static"]; runtimeConfig.m_meta = ParseConfigMeta(configurationFile["meta"]); runtimeConfig.m_repo = ParseRepoConfig(configurationFile["repo"]); From 0d5247be345493fb699e54dc0eaad41af35fa87b Mon Sep 17 00:00:00 2001 From: moudgils Date: Tue, 1 Jun 2021 09:58:45 -0700 Subject: [PATCH 084/233] Fix metal shader pipeline crashes for LuminanceHistogramGenerator and MorphTargetCS due to the use of atomic operations with typed buffers. Switching them to use Structured buffers. Plus misc cleanup --- .../Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader | 4 +--- .../Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli | 2 +- .../Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl | 4 ++-- .../Shaders/PostProcessing/LuminanceHistogramGenerator.azsl | 2 +- .../PostProcessing/LuminanceHistogramGenerator.shader | 4 +--- .../Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli | 2 +- .../PostProcessing/LuminanceHistogramGeneratorPass.cpp | 2 +- .../Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp | 4 ++-- .../RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h | 1 - Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h | 6 ++++++ 10 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader index 95ffc36a11..08b1e7c298 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader @@ -10,7 +10,5 @@ "type": "Compute" } ] - }, - "DisabledRHIBackends": ["metal"] - + } } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli index 7ec5b43368..171e803c2c 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli @@ -16,7 +16,7 @@ ShaderResourceGroup MorphTargetPassSrg : SRG_PerPass { - RWBuffer m_accumulatedDeltas; + RWStruturedBuffer m_accumulatedDeltas; } // This class represents the data that is passed to the morph target compute shader of an individual delta diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl index df92e36f9a..4559b6c9ef 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl @@ -1,4 +1,4 @@ -/* + /* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * @@ -37,7 +37,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_sceneLuminance; // This should be of size NUM_HISTOGRAM_BINS. - Buffer m_histogram; + StructuredBuffer m_histogram; Sampler LinearSampler { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl index 05cc870eea..9d01a12fd6 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl @@ -20,7 +20,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass { Texture2D m_inputTexture; - RWBuffer m_outputTexture; + RWStructuredBuffer m_outputTexture; } groupshared uint shared_histogramBins[NUM_HISTOGRAM_BINS]; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader index 566144bab8..f3dd11e11a 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader @@ -12,7 +12,5 @@ "type": "Compute" } ] - }, - "DisabledRHIBackends": ["metal"] - + } } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli index 5a9e44bade..e5407d2d9e 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli @@ -16,7 +16,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass { - RWBuffer m_skinnedMeshOutputStream; + RWStructuredBuffer m_skinnedMeshOutputStream; } ShaderResourceGroup InstanceSrg : SRG_PerDraw diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp index 758c21bc4e..a3c494dee9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp @@ -72,7 +72,7 @@ namespace AZ desc.m_bufferName = AZStd::string::format("LuminanceHistogramBuffer_%s", uuidString.c_str()); desc.m_elementSize = sizeof(uint32_t); desc.m_byteCount = NumHistogramBins * sizeof(uint32_t); - desc.m_elementFormat = RHI::Format::R32_UINT; + desc.m_elementFormat = RHI::Format::Unknown; m_histogram = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); AZ_Assert(m_histogram != nullptr, "Unable to allocate buffer"); } diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp index 1cb782d8b1..1b14611e84 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp @@ -67,8 +67,8 @@ namespace AZ creator.SetBuffer(nullptr, 0, bufferDescriptor); RHI::BufferViewDescriptor viewDescriptor; - viewDescriptor.m_elementFormat = RHI::Format::R32_FLOAT; - viewDescriptor.m_elementSize = RHI::GetFormatSize(viewDescriptor.m_elementFormat); + viewDescriptor.m_elementFormat = RHI::Format::Unknown; + viewDescriptor.m_elementSize = sizeof(float); viewDescriptor.m_elementCount = aznumeric_cast(m_sizeInBytes) / viewDescriptor.m_elementSize; viewDescriptor.m_elementOffset = 0; creator.SetBufferViewDescriptor(viewDescriptor); diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h index ce2c6c77ec..afcab28a1d 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h @@ -410,7 +410,6 @@ namespace AZ // For any other type the buffer view's element size should match the stride. if (shaderInputBuffer.m_strideSize != bufferViewDescriptor.m_elementSize) { - // [GFX TODO][ATOM-5735][AZSL] ByteAddressBuffer shader input is setting a stride of 16 instead of 4 AZ_Error("ShaderResourceGroupData", false, "Buffer Input '%s[%d]': Does not match expected stride size %d", shaderInputBuffer.m_name.GetCStr(), arrayIndex, bufferViewDescriptor.m_elementSize); return false; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h index a6fe57f6d8..6f09ea1d5f 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h @@ -271,6 +271,12 @@ namespace AZ ShaderResourceBindings& bindings = GetShaderResourceBindingsByPipelineType(pipelineType); const PipelineState* pipelineState = static_cast(item.m_pipelineState); + if(!pipelineState) + { + AZ_Assert(false, "Pipeline state not provided"); + return false; + } + bool updatePipelineState = m_state.m_pipelineState != pipelineState; // The pipeline state gets set first. if (updatePipelineState) From 105eca52d7f3b06fb72f2f9905246f5c449d073a Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 2 Jun 2021 11:16:36 +0100 Subject: [PATCH 085/233] Address PR comments --- .../TestImpactRuntimeConfigurationFactory.cpp | 182 +++++++++++++----- .../TestImpactConfiguration.h | 18 +- .../Runtime/Code/Source/TestImpactRuntime.cpp | 12 +- 3 files changed, 147 insertions(+), 65 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp index 4b473b7190..9db99d3c2e 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp @@ -18,9 +18,101 @@ #include #include - namespace TestImpact { + namespace Config + { + // Keys for pertinent JSON elements + constexpr const char* Keys[] = + { + "root", + "platform", + "relative_paths", + "artifact_dir", + "enumeration_cache_dir", + "test_impact_data_file", + "temp", + "active", + "target_sources", + "static", + "autogen", + "static", + "include_filters", + "input_output_pairer", + "input", + "dir", + "matchers", + "target_dependency_file", + "target_vertex", + "file", + "test_runner", + "instrumentation", + "bin", + "exclude", + "shard", + "fixture_contiguous", + "fixture_interleaved", + "test_contiguous", + "test_interleaved", + "never", + "target", + "policy", + "artifacts", + "meta", + "repo", + "workspace", + "build_target_descriptor", + "dependency_graph_data", + "test_target_meta", + "test_engine", + "target" + }; + + enum + { + Root = 0, + PlatformName, + RelativePaths, + ArtifactDir, + EnumerationCacheDir, + TestImpactDataFile, + TempWorkspace, + ActiveWorkspace, + TargetSources, + StaticSources, + AutogenSources, + StaticArtifacts, + SourceIncludeFilters, + AutogenInputOutputPairer, + AutogenInputSources, + Directory, + DependencyGraphMatchers, + TargetDependencyFileMatcher, + TargetVertexMatcher, + TestTargetMetaFile, + TestRunner, + TestInstrumentation, + BinaryFile, + TargetExcludeFilter, + TestSharding, + ContinuousFixtureSharding, + InterleavedFixtureSharding, + ContinuousTestSharding, + InterleavedTestSharding, + NeverShard, + TargetName, + TestShardingPolicy, + Artifacts, + Meta, + Repository, + Workspace, + BuildTargetDescriptor, + DependencyGraphData, + TestTargetMeta, + TestEngine, + TargetConfig + }; + } //! Returns an absolute path for a path relative to the specified root. RepoPath GetAbsPathFromRelPath(const RepoPath& root, const RepoPath& rel) @@ -31,54 +123,55 @@ namespace TestImpact ConfigMeta ParseConfigMeta(const rapidjson::Value& meta) { ConfigMeta configMeta; - configMeta.m_platform = meta["platform"].GetString(); + configMeta.m_platform = meta[Config::Keys[Config::PlatformName]].GetString(); return configMeta; } RepoConfig ParseRepoConfig(const rapidjson::Value& repo) { RepoConfig repoConfig; - repoConfig.m_root = repo["root"].GetString(); + repoConfig.m_root = repo[Config::Keys[Config::Root]].GetString(); return repoConfig; } WorkspaceConfig::Temp ParseTempWorkspaceConfig(const rapidjson::Value& tempWorkspace) { WorkspaceConfig::Temp tempWorkspaceConfig; - tempWorkspaceConfig.m_root = tempWorkspace["root"].GetString(); + tempWorkspaceConfig.m_root = tempWorkspace[Config::Keys[Config::Root]].GetString(); tempWorkspaceConfig.m_artifactDirectory = - GetAbsPathFromRelPath(tempWorkspaceConfig.m_root, tempWorkspace["relative_paths"]["artifact_dir"].GetString()); + GetAbsPathFromRelPath( + tempWorkspaceConfig.m_root, tempWorkspace[Config::Keys[Config::RelativePaths]][Config::Keys[Config::ArtifactDir]].GetString()); return tempWorkspaceConfig; } WorkspaceConfig::Active ParseActiveWorkspaceConfig(const rapidjson::Value& activeWorkspace) { WorkspaceConfig::Active activeWorkspaceConfig; - const auto& relativePaths = activeWorkspace["relative_paths"]; - activeWorkspaceConfig.m_root = activeWorkspace["root"].GetString(); + const auto& relativePaths = activeWorkspace[Config::Keys[Config::RelativePaths]]; + activeWorkspaceConfig.m_root = activeWorkspace[Config::Keys[Config::Root]].GetString(); activeWorkspaceConfig.m_enumerationCacheDirectory - = GetAbsPathFromRelPath(activeWorkspaceConfig.m_root, relativePaths["enumeration_cache_dir"].GetString()); + = GetAbsPathFromRelPath(activeWorkspaceConfig.m_root, relativePaths[Config::Keys[Config::EnumerationCacheDir]].GetString()); activeWorkspaceConfig.m_sparTIAFile - = GetAbsPathFromRelPath(activeWorkspaceConfig.m_root, relativePaths["test_impact_data_file"].GetString()); + = GetAbsPathFromRelPath(activeWorkspaceConfig.m_root, relativePaths[Config::Keys[Config::TestImpactDataFile]].GetString()); return activeWorkspaceConfig; } WorkspaceConfig ParseWorkspaceConfig(const rapidjson::Value& workspace) { WorkspaceConfig workspaceConfig; - workspaceConfig.m_temp = ParseTempWorkspaceConfig(workspace["temp"]); - workspaceConfig.m_active = ParseActiveWorkspaceConfig(workspace["active"]); + workspaceConfig.m_temp = ParseTempWorkspaceConfig(workspace[Config::Keys[Config::TempWorkspace]]); + workspaceConfig.m_active = ParseActiveWorkspaceConfig(workspace[Config::Keys[Config::ActiveWorkspace]]); return workspaceConfig; } BuildTargetDescriptorConfig ParseBuildTargetDescriptorConfig(const rapidjson::Value& buildTargetDescriptor) { BuildTargetDescriptorConfig buildTargetDescriptorConfig; - const auto& targetSources = buildTargetDescriptor["target_sources"]; - const auto& staticTargetSources = targetSources["static"]; - const auto& autogenTargetSources = targetSources["autogen"]; - buildTargetDescriptorConfig.m_mappingDirectory = buildTargetDescriptor["dir"].GetString(); - const auto& staticInclusionFilters = staticTargetSources["include_filters"].GetArray(); + const auto& targetSources = buildTargetDescriptor[Config::Keys[Config::TargetSources]]; + const auto& staticTargetSources = targetSources[Config::Keys[Config::StaticSources]]; + const auto& autogenTargetSources = targetSources[Config::Keys[Config::AutogenSources]]; + buildTargetDescriptorConfig.m_mappingDirectory = buildTargetDescriptor[Config::Keys[Config::Directory]].GetString(); + const auto& staticInclusionFilters = staticTargetSources[Config::Keys[Config::SourceIncludeFilters]].GetArray(); buildTargetDescriptorConfig.m_staticInclusionFilters.reserve(staticInclusionFilters.Size()); for (const auto& staticInclusionFilter : staticInclusionFilters) @@ -86,8 +179,9 @@ namespace TestImpact buildTargetDescriptorConfig.m_staticInclusionFilters.push_back(staticInclusionFilter.GetString()); } - buildTargetDescriptorConfig.m_inputOutputPairer = autogenTargetSources["input_output_pairer"].GetString(); - const auto& inputInclusionFilters = autogenTargetSources["input"]["include_filters"].GetArray(); + buildTargetDescriptorConfig.m_inputOutputPairer = autogenTargetSources[Config::Keys[Config::AutogenInputOutputPairer]].GetString(); + const auto& inputInclusionFilters = + autogenTargetSources[Config::Keys[Config::AutogenInputSources]][Config::Keys[Config::SourceIncludeFilters]].GetArray(); buildTargetDescriptorConfig.m_inputInclusionFilters.reserve(inputInclusionFilters.Size()); for (const auto& inputInclusionFilter : inputInclusionFilters) { @@ -100,62 +194,62 @@ namespace TestImpact DependencyGraphDataConfig ParseDependencyGraphDataConfig(const rapidjson::Value& dependencyGraphData) { DependencyGraphDataConfig dependencyGraphDataConfig; - const auto& matchers = dependencyGraphData["matchers"]; - dependencyGraphDataConfig.m_graphDirectory = dependencyGraphData["dir"].GetString(); - dependencyGraphDataConfig.m_targetDependencyFileMatcher = matchers["target_dependency_file"].GetString(); - dependencyGraphDataConfig.m_targetVertexMatcher = matchers["target_vertex"].GetString(); + const auto& matchers = dependencyGraphData[Config::Keys[Config::DependencyGraphMatchers]]; + dependencyGraphDataConfig.m_graphDirectory = dependencyGraphData[Config::Keys[Config::Directory]].GetString(); + dependencyGraphDataConfig.m_targetDependencyFileMatcher = matchers[Config::Keys[Config::TargetDependencyFileMatcher]].GetString(); + dependencyGraphDataConfig.m_targetVertexMatcher = matchers[Config::Keys[Config::TargetVertexMatcher]].GetString(); return dependencyGraphDataConfig; } TestTargetMetaConfig ParseTestTargetMetaConfig(const rapidjson::Value& testTargetMeta) { TestTargetMetaConfig testTargetMetaConfig; - testTargetMetaConfig.m_metaFile = testTargetMeta["file"].GetString(); + testTargetMetaConfig.m_metaFile = testTargetMeta[Config::Keys[Config::TestTargetMetaFile]].GetString(); return testTargetMetaConfig; } TestEngineConfig ParseTestEngineConfig(const rapidjson::Value& testEngine) { TestEngineConfig testEngineConfig; - testEngineConfig.m_testRunner.m_binary = testEngine["test_runner"]["bin"].GetString(); - testEngineConfig.m_instrumentation.m_binary = testEngine["instrumentation"]["bin"].GetString(); + testEngineConfig.m_testRunner.m_binary = testEngine[Config::Keys[Config::TestRunner]][Config::Keys[Config::BinaryFile]].GetString(); + testEngineConfig.m_instrumentation.m_binary = testEngine[Config::Keys[Config::TestInstrumentation]][Config::Keys[Config::BinaryFile]].GetString(); return testEngineConfig; } TargetConfig ParseTargetConfig(const rapidjson::Value& target) { TargetConfig targetConfig; - targetConfig.m_outputDirectory = target["dir"].GetString(); - const auto& testExcludes = target["exclude"].GetArray(); + targetConfig.m_outputDirectory = target[Config::Keys[Config::Directory]].GetString(); + const auto& testExcludes = target[Config::Keys[Config::TargetExcludeFilter]].GetArray(); targetConfig.m_excludedTestTargets.reserve(testExcludes.Size()); for (const auto& testExclude : testExcludes) { targetConfig.m_excludedTestTargets.push_back(testExclude.GetString()); } - const auto& testShards = target["shard"].GetArray(); + const auto& testShards = target[Config::Keys[Config::TestSharding]].GetArray(); targetConfig.m_shardedTestTargets.reserve(testShards.Size()); for (const auto& testShard : testShards) { const auto getShardingConfiguration = [](const AZStd::string& config) { - if (config == "fixture_contiguous") + if (config == Config::Keys[Config::ContinuousFixtureSharding]) { return ShardConfiguration::FixtureContiguous; } - else if (config == "fixture_interleaved") + else if (config == Config::Keys[Config::InterleavedFixtureSharding]) { return ShardConfiguration::FixtureInterleaved; } - else if (config == "test_contiguous") + else if (config == Config::Keys[Config::ContinuousTestSharding]) { return ShardConfiguration::TestContiguous; } - else if (config == "test_interleaved") + else if (config == Config::Keys[Config::InterleavedTestSharding]) { return ShardConfiguration::TestInterleaved; } - else if (config == "never") + else if (config == Config::Keys[Config::NeverShard]) { return ShardConfiguration::Never; } @@ -166,8 +260,8 @@ namespace TestImpact }; TargetConfig::ShardedTarget shard; - shard.m_name = testShard["target"].GetString(); - shard.m_configuration = getShardingConfiguration(testShard["policy"].GetString()); + shard.m_name = testShard[Config::Keys[Config::TargetName]].GetString(); + shard.m_configuration = getShardingConfiguration(testShard[Config::Keys[Config::TestShardingPolicy]].GetString()); targetConfig.m_shardedTestTargets.push_back(AZStd::move(shard)); } @@ -184,15 +278,15 @@ namespace TestImpact } RuntimeConfig runtimeConfig; - const auto& staticArtifacts = configurationFile["artifacts"]["static"]; - runtimeConfig.m_meta = ParseConfigMeta(configurationFile["meta"]); - runtimeConfig.m_repo = ParseRepoConfig(configurationFile["repo"]); - runtimeConfig.m_workspace = ParseWorkspaceConfig(configurationFile["workspace"]); - runtimeConfig.m_buildTargetDescriptor = ParseBuildTargetDescriptorConfig(staticArtifacts["build_target_descriptor"]); - runtimeConfig.m_dependencyGraphData = ParseDependencyGraphDataConfig(staticArtifacts["dependency_graph_data"]); - runtimeConfig.m_testTargetMeta = ParseTestTargetMetaConfig(staticArtifacts["static"]["test_target_meta"]); - runtimeConfig.m_testEngine = ParseTestEngineConfig(configurationFile["test_engine"]); - runtimeConfig.m_target = ParseTargetConfig(configurationFile["target"]); + const auto& staticArtifacts = configurationFile[Config::Keys[Config::Artifacts]][Config::Keys[Config::StaticArtifacts]]; + runtimeConfig.m_meta = ParseConfigMeta(configurationFile[Config::Keys[Config::Meta]]); + runtimeConfig.m_repo = ParseRepoConfig(configurationFile[Config::Keys[Config::Repository]]); + runtimeConfig.m_workspace = ParseWorkspaceConfig(configurationFile[Config::Keys[Config::Workspace]]); + runtimeConfig.m_buildTargetDescriptor = ParseBuildTargetDescriptorConfig(staticArtifacts[Config::Keys[Config::BuildTargetDescriptor]]); + runtimeConfig.m_dependencyGraphData = ParseDependencyGraphDataConfig(staticArtifacts[Config::Keys[Config::DependencyGraphData]]); + runtimeConfig.m_testTargetMeta = ParseTestTargetMetaConfig(staticArtifacts[Config::Keys[Config::TestTargetMeta]]); + runtimeConfig.m_testEngine = ParseTestEngineConfig(configurationFile[Config::Keys[Config::TestEngine]]); + runtimeConfig.m_target = ParseTargetConfig(configurationFile[Config::Keys[Config::TargetConfig]]); return runtimeConfig; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h index 3406789c9e..91d748edfe 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h @@ -38,28 +38,16 @@ namespace TestImpact //! Temporary workspace configuration. struct Temp { - //! Paths relative to root. - struct RelativePaths - { - RepoPath m_artifactDirectory; //!< Path to read and write runtime artifacts to and from. - }; - RepoPath m_root; //!< Path to the temporary workspace (cleaned prior to use). - RelativePaths m_relativePaths; + RepoPath m_artifactDirectory; //!< Path to read and write runtime artifacts to and from. }; //! Active persistent data workspace configuration. struct Active { - //! Paths relative to root. - struct RelativePaths - { - RepoPath m_sparTIAFile; //!< Path to the test impact analysis data. - RepoPath m_enumerationCacheDirectory; //!< Path to the test enumerations cache. - }; - RepoPath m_root; //!< Path to the persistent workspace tracked by the repository. - RelativePaths m_relativePaths; + RepoPath m_sparTIAFile; //!< Path to the test impact analysis data. + RepoPath m_enumerationCacheDirectory; //!< Path to the test enumerations cache. }; Temp m_temp; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp index e02bee03c6..83808f4ed2 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp @@ -101,8 +101,8 @@ namespace TestImpact m_testEngine = AZStd::make_unique( m_config.m_repo.m_root, m_config.m_target.m_outputDirectory, - m_config.m_workspace.m_active.m_relativePaths.m_enumerationCacheDirectory, - m_config.m_workspace.m_temp.m_relativePaths.m_artifactDirectory, + m_config.m_workspace.m_active.m_enumerationCacheDirectory, + m_config.m_workspace.m_temp.m_artifactDirectory, m_config.m_testEngine.m_testRunner.m_binary, m_config.m_testEngine.m_instrumentation.m_binary, m_maxConcurrency); @@ -110,7 +110,7 @@ namespace TestImpact try { // Populate the dynamic dependency map with the existing source coverage data (if any) - const auto tiaDataRaw = ReadFileContents(m_config.m_workspace.m_active.m_relativePaths.m_sparTIAFile); + const auto tiaDataRaw = ReadFileContents(m_config.m_workspace.m_active.m_sparTIAFile); const auto tiaData = DeserializeSourceCoveringTestsList(tiaDataRaw); if (tiaData.GetNumSources()) { @@ -136,7 +136,7 @@ namespace TestImpact } catch ([[maybe_unused]]const Exception& e) { - AZ_Printf("No test impact analysis data found at %s", m_config.m_workspace.m_active.m_relativePaths.m_sparTIAFile.c_str()); + AZ_Printf("No test impact analysis data found at %s", m_config.m_workspace.m_active.m_sparTIAFile.c_str()); } } @@ -233,7 +233,7 @@ namespace TestImpact void Runtime::ClearDynamicDependencyMapAndRemoveExistingFile() { - DeleteFile(m_config.m_workspace.m_active.m_relativePaths.m_sparTIAFile); + DeleteFile(m_config.m_workspace.m_active.m_sparTIAFile); m_dynamicDependencyMap->ClearAllSourceCoverage(); } @@ -247,7 +247,7 @@ namespace TestImpact m_dynamicDependencyMap->ReplaceSourceCoverage(sourceCoverageTestsList); const auto sparTIA = m_dynamicDependencyMap->ExportSourceCoverage(); const auto sparTIAData = SerializeSourceCoveringTestsList(sparTIA); - WriteFileContents(sparTIAData, m_config.m_workspace.m_active.m_relativePaths.m_sparTIAFile); + WriteFileContents(sparTIAData, m_config.m_workspace.m_active.m_sparTIAFile); m_hasImpactAnalysisData = true; } From d72bc3a4489c6c1bc3b5a5bedd6ad41760a00a00 Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 2 Jun 2021 14:30:09 +0100 Subject: [PATCH 086/233] Add provisional Jenkins job --- scripts/build/TestImpactAnalysis/git_utils.py | 42 ++++ scripts/build/TestImpactAnalysis/tiaf.py | 202 ++++++++++++++++++ .../build/TestImpactAnalysis/tiaf_driver.py | 64 ++++++ 3 files changed, 308 insertions(+) create mode 100644 scripts/build/TestImpactAnalysis/git_utils.py create mode 100644 scripts/build/TestImpactAnalysis/tiaf.py create mode 100644 scripts/build/TestImpactAnalysis/tiaf_driver.py diff --git a/scripts/build/TestImpactAnalysis/git_utils.py b/scripts/build/TestImpactAnalysis/git_utils.py new file mode 100644 index 0000000000..5abedd16f0 --- /dev/null +++ b/scripts/build/TestImpactAnalysis/git_utils.py @@ -0,0 +1,42 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import os +import subprocess +import git + +# Returns True if the dst commit descends from the src commit, otherwise False +def is_descendent(src_commit_hash, dst_commit_hash): + if src_commit_hash is None or dst_commit_hash is None: + return False + result = subprocess.run(["git", "merge-base", "--is-ancestor", src_commit_hash, dst_commit_hash]) + return result.returncode == 0 + +# Attempts to create a diff from the src and dst commits and write to the specified output file +def create_diff_file(src_commit_hash, dst_commit_hash, output_path): + if os.path.isfile(output_path): + os.remove(output_path) + os.makedirs(os.path.dirname(output_path), exist_ok=True) + # git diff will only write to the output file if both commit hashes are valid + subprocess.run(["git", "diff", "--name-status", f"--output={output_path}", src_commit_hash, dst_commit_hash]) + if not os.path.isfile(output_path): + raise FileNotFoundError(f"Source commit '{src_commit_hash}' and/or destination commit '{dst_commit_hash}' are invalid") + +# Basic representation of a repository +class Repo: + def __init__(self, repo_path): + self.__repo = git.Repo(repo_path) + + # Returns the current branch + @property + def current_branch(self): + branch = self.__repo.active_branch + return branch.name diff --git a/scripts/build/TestImpactAnalysis/tiaf.py b/scripts/build/TestImpactAnalysis/tiaf.py new file mode 100644 index 0000000000..4686c08f93 --- /dev/null +++ b/scripts/build/TestImpactAnalysis/tiaf.py @@ -0,0 +1,202 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import os +import json +import subprocess +import re +import git_utils +from git_utils import Repo +from enum import Enum + +# Returns True if the specified child path is a child of the specified parent path, otherwise False +def is_child_path(parent_path, child_path): + parent_path = os.path.abspath(parent_path) + child_path = os.path.abspath(child_path) + return os.path.commonpath([os.path.abspath(parent_path)]) == os.path.commonpath([os.path.abspath(parent_path), os.path.abspath(child_path)]) + +# Enumerations for test sequence types +class SequenceType(Enum): + REGULAR = 1 + TEST_IMPACT_ANALYSIS = 2 + +class TestImpact: + def __init__(self, config_file, dst_commit): + self.__parse_config_file(config_file) + self.__init_repo(dst_commit) + self.__generate_change_list() + + # Parse the configuration file and retrieve the data needed for launching the test impact analysis runtime + def __parse_config_file(self, config_file): + print(f"Attempting to parse configuration file '{config_file}'...") + with open(config_file, "r") as config_data: + config = json.load(config_data) + self.__repo_dir = config["repo"]["root"] + self.__tiaf_bin = config["repo"]["tiaf_bin"] + if not os.path.isfile(self.__tiaf_bin): + raise FileNotFoundError("Could not find tiaf binary") + self.__source_of_truth = config["repo"]["source_of_truth"] + self.__active_workspace = config["workspace"]["active"]["root"] + self.__historic_workspace = config["workspace"]["historic"]["root"] + self.__temp_workspace = config["workspace"]["temp"]["root"] + last_commit_hash_path_rel = config["workspace"]["historic"]["relative_paths"]["last_run_hash_file"] + self.__last_commit_hash_path = os.path.join(self.__historic_workspace, last_commit_hash_path_rel) + print("The configuration file was parsed successfully.") + + # Initializes the internal representation of the repository being targeted for test impact analysis + def __init_repo(self, dst_commit): + self.__repo = Repo(self.__repo_dir) + self.__branch = self.__repo.current_branch + self.__dst_commit = dst_commit + self.__src_commit = None + self.__has_src_commit = False + if self.__repo.current_branch == self.__source_of_truth: + self.__is_source_of_truth = True + else: + self.__is_source_of_truth = False + print(f"The repository is located at '{self.__repo_dir}' and the current branch is '{self.__branch}'.") + print(f"The source of truth branch is '{self.__source_of_truth}'.") + if self.__is_source_of_truth: + print("I am the source of truth.") + else: + print("I am *not* the source of truth.") + + # Restricts change lists from checking in test impact analysis files + def __check_for_restricted_files(self, file_path): + if is_child_path(self.__active_workspace, file_path) or is_child_path(self.__historic_workspace, file_path) or is_child_path(self.__temp_workspace, file_path): + raise ValueError(f"Checking in test impact analysis framework files is illegal: '{file_path}''.") + + def __read_last_run_hash(self): + self.__has_src_commit = False + if os.path.isfile(self.__last_commit_hash_path): + print(f"Previous commit hash found at 'self.__last_commit_hash_path'") + with open(self.__last_commit_hash_path) as file: + self.__src_commit = file.read() + self.__has_src_commit = True + + def __write_last_run_hash(self, last_run_hash): + f = open(self.__last_commit_hash_path, "w") + f.write(last_run_hash) + f.close() + + # Determines the change list bewteen now and the last tiaf run (if any) + def __generate_change_list(self): + self.__has_change_list = False + self.__change_list_path = None + # Check whether or not a previous commit hash exists (no hash is not a failure) + self.__read_last_run_hash() + if self.__has_src_commit == True: + if git_utils.is_descendent(self.__src_commit, self.__dst_commit) == False: + print(f"Source commit '{self.__src_commit}' and destination commit '{self.__dst_commit}' are not related.") + return + diff_path = os.path.join(self.__temp_workspace, "changelist.diff") + try: + git_utils.create_diff_file(self.__src_commit, self.__dst_commit, diff_path) + except FileNotFoundError as e: + print(e) + return + # A diff was generated, attempt to parse the diff and construct the change list + print(f"Generated diff between commits {self.__src_commit} and {self.__dst_commit}.") + change_list = {} + change_list["createdFiles"] = [] + change_list["updatedFiles"] = [] + change_list["deletedFiles"] = [] + with open(diff_path, "r") as diff_data: + lines = diff_data.readlines() + for line in lines: + match = re.split("^R[0-9]+\\s(\\S+)\\s(\\S+)", line) + if len(match) > 1: + # File rename + self.__check_for_restricted_files(match[1]) + self.__check_for_restricted_files(match[2]) + # Treat renames as a deletion and an addition + change_list["deletedFiles"].append(match[1]) + change_list["createdFiles"].append(match[2]) + else: + match = re.split("^[AMD]\\s(\\S+)", line) + self.__check_for_restricted_files(match[1]) + if len(match) > 1: + if line[0] == 'A': + # File addition + change_list["createdFiles"].append(match[1]) + elif line[0] == 'M': + # File modification + change_list["updatedFiles"].append(match[1]) + elif line[0] == 'D': + # File Deletion + change_list["deletedFiles"].append(match[1]) + # Serialize the change list to the JSON format the test impact analysis runtime expects + change_list_json = json.dumps(change_list, indent = 4) + change_list_path = os.path.join(self.__temp_workspace, "changelist.json") + f = open(change_list_path, "w") + f.write(change_list_json) + f.close() + print(f"Change list constructed successfully: '{change_list_path}'.") + print(f"{len(change_list['createdFiles'])} created files, {len(change_list['updatedFiles'])} updated files and {len(change_list['deletedFiles'])} deleted files.") + # Note: an empty change list generated due to no changes between last and current commit is valid + self.__has_change_list = True + self.__change_list_path = change_list_path + else: + print("No previous commit hash found, regular or seeded sequences only will be run.") + self.__has_change_list = False + return + + # Runs the specified test sequence + def run(self, sequence_type, safe_mode, test_timeout, global_timeout): + args = [] + if self.__has_change_list: + args.append(f"-changelist={self.__change_list_path}") + if sequence_type == SequenceType.REGULAR: + print("Sequence type: regular.") + args.append("--sequence=regular") + elif sequence_type == SequenceType.TEST_IMPACT_ANALYSIS: + print("Sequence type: test impact analysis.") + if self.__is_source_of_truth: + # Source of truth branch will allways attempt a seed if no test impact analysis data is available + print("This branch is the source of truth, a seed sequence will be run if there is no test impact analysis data.") + args.append("--sequence=tiaorseed") + args.append("--fpolicy=continue") + else: + # Non source of truth branches will fall back to a regular test run if no test impact analysis data is available + print("This branch is not the source of truth, a regular sequence will be run if there is no test impact analysis data.") + args.append("--sequence=tia") + args.append("--fpolicy=abort") + + if safe_mode == True: + print("Safe mode is on, the discarded test targets will be run after the selected test targets.") + args.append("--safemode=on") + else: + print("Safe mode is off, the discarded test targets will not be run.") + else: + raise ValueError(sequence_type) + else: + print(f"No change list was generated, this will cause test impact analysis sequences on branches other than the source of truth to fall back to a regular sequence.") + print("Sequence type: Regular.") + args.append("--sequence=regular") + + if test_timeout != None: + args.append(f"--ttimeout={test_timeout}") + print(f"Test target timeout is set to {test_timeout} seconds.") + if global_timeout != None: + args.append(f"--gtimeout={global_timeout}") + print(f"Global sequence timeout is set to {test_timeout} seconds.") + + print("Args: ", end='') + print(*args) + result = subprocess.run([self.__tiaf_bin] + args) + if result.returncode == 0: + print("Test impact analysis runtime returned successfully. Updating historical artifacts...") + self.__write_last_run_hash(self.__dst_commit) + print("Complete!") + else: + print(f"The test impact analysis runtime returned with error: '{result.returncode}'.") + return result.returncode + diff --git a/scripts/build/TestImpactAnalysis/tiaf_driver.py b/scripts/build/TestImpactAnalysis/tiaf_driver.py new file mode 100644 index 0000000000..4f20fa1070 --- /dev/null +++ b/scripts/build/TestImpactAnalysis/tiaf_driver.py @@ -0,0 +1,64 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import argparse +from tiaf import TestImpact +from tiaf import SequenceType + +import sys +import os +import datetime +import json +import socket + +def parse_args(): + def file_path(value): + if os.path.isfile(value): + return value + else: + raise FileNotFoundError(value) + + def sequence_type(value): + if value == "regular": + return SequenceType.REGULAR + elif value == "tia": + return SequenceType.TEST_IMPACT_ANALYSIS + else: + raise ValueError(value) + + def timout_type(value): + value = int(value) + if value <= 0: + raise ValueError("Timer values must be positive integers") + return value + + parser = argparse.ArgumentParser() + parser.add_argument('--config', dest="config", type=file_path, help="Path to the test impact analysis framework configuration file", required=True) + parser.add_argument('--destCommit', dest="dst_commit", help="Commit to run test impact analysis on (if empty, HEAD^ will be used)") + parser.add_argument('--sequenceType', dest="sequence_type", type=sequence_type, help="Test sequence type to run ('regular' or 'tia')", required=True) + parser.add_argument('--suites', dest="suites", nargs='*', help="Suites to include for regular tes sequences (use '*' for all suites)") + parser.add_argument('--safeMode', dest='safe_mode', help="If set, will run any test impact analysis runs in safe mode (unselected tests will still be run)") + parser.add_argument('--testTimeout', dest="test_timeout", type=timout_type, help="Maximum flight time (in seconds) of any test target before being terminated", required=False) + parser.add_argument('--globalTimeout', dest="global_timeout", type=timout_type, help="Maximum tun time of the sequence before being terminated", required=False) + parser.set_defaults(dst_commit="HEAD^") + parser.set_defaults(suites="*") + parser.set_defaults(safe_mode=False) + parser.set_defaults(test_timeout=None) + parser.set_defaults(global_timeout=None) + args = parser.parse_args() + + return args + +if __name__ == "__main__": + args = parse_args() + tiaf = TestImpact(args.config, args.dst_commit) + return_code = tiaf.run(args.sequence_type, args.safe_mode, args.test_timeout, args.global_timeout) + sys.exit(return_code) \ No newline at end of file From 485a45d3c2a12bd7194210939ca75a58f9640bd2 Mon Sep 17 00:00:00 2001 From: igarri Date: Wed, 2 Jun 2021 15:32:03 +0100 Subject: [PATCH 087/233] Made some corrections --- .../AssetBrowser/AssetBrowserFilterModel.cpp | 11 +-- .../AssetBrowser/AssetBrowserTableModel.cpp | 10 ++- .../Views/AssetBrowserTableView.cpp | 9 +- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 83 ++++++++++--------- 4 files changed, 60 insertions(+), 53 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index 2660b660e4..adb4ae66c4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -26,7 +26,6 @@ AZ_POP_DISABLE_WARNING AZ_CVAR( bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Use the new AssetBrowser TableView for searching assets."); -#pragma optimize("", off) namespace AzToolsFramework { namespace AssetBrowser @@ -139,7 +138,7 @@ namespace AzToolsFramework { const auto& subFilters = compFilter->GetSubFilters(); - const auto compositeFilterIterator = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool + const auto compositeFilterIterator = AZStd::find_if(subFilters.cbegin(), subFilters.cend(), [subFilters](FilterConstType filter) -> bool { const auto assetTypeFilter = qobject_cast >(filter); return !assetTypeFilter.isNull(); @@ -150,7 +149,7 @@ namespace AzToolsFramework m_assetTypeFilter = qobject_cast >(*compositeFilterIterator); } - const auto compStringFilterIter = AZStd::find_if(subFilters.begin(), subFilters.end(), [](FilterConstType filter) -> bool + const auto compStringFilterIter = AZStd::find_if(subFilters.cbegin(), subFilters.cend(), [](FilterConstType filter) -> bool { //The real StringFilter is really a CompositeFilter with just one StringFilter in its subfilter list //To know if it is actually a StringFilter we have to get that subfilter and check if it is a Stringfilter. @@ -164,7 +163,7 @@ namespace AzToolsFramework auto strFilter = qobject_cast>(filt); return !strFilter.isNull(); }; - const auto stringSubfliterConstIter = AZStd::find_if(stringSubfilters.begin(), stringSubfilters.end(), canBeCasted); + const auto stringSubfliterConstIter = AZStd::find_if(stringSubfilters.cbegin(), stringSubfilters.cend(), canBeCasted); //A Composite StringFilter will only have just one subfilter and nothing more. if (stringSubfliterConstIter != stringSubfilters.end() && stringSubfilters.size() == 1) @@ -188,8 +187,7 @@ namespace AzToolsFramework } invalidateFilter(); Q_EMIT filterChanged(); - bool isNullAB = m_stringFilter.isNull(); - emit stringFilterPopulated(!isNullAB); + emit stringFilterPopulated(!m_stringFilter.isNull()); } void AssetBrowserFilterModel::filterUpdatedSlot() @@ -209,6 +207,5 @@ namespace AzToolsFramework } // namespace AssetBrowser } // namespace AzToolsFramework// namespace AssetBrowser -#pragma optimize("", on) #include "AssetBrowser/moc_AssetBrowserFilterModel.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index 40f8318a2d..70d66f4e1f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -9,8 +9,8 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ -#include #include +#include #include namespace AzToolsFramework @@ -26,7 +26,9 @@ namespace AzToolsFramework void AssetBrowserTableModel::setSourceModel(QAbstractItemModel* sourceModel) { m_filterModel = qobject_cast(sourceModel); - AZ_Assert(m_filterModel, "Error in AssetBrowserTableModel initialization, class expects source model to be an AssetBrowserFilterModel."); + AZ_Assert( + m_filterModel, + "Error in AssetBrowserTableModel initialization, class expects source model to be an AssetBrowserFilterModel."); QSortFilterProxyModel::setSourceModel(sourceModel); } @@ -86,7 +88,8 @@ namespace AzToolsFramework return !parent.isValid() ? m_indexMap.size() : 0; } - int AssetBrowserTableModel::BuildTableModelMap(const QAbstractItemModel* model, const QModelIndex& parent /*= QModelIndex()*/, int row /*= 0*/) + int AssetBrowserTableModel::BuildTableModelMap( + const QAbstractItemModel* model, const QModelIndex& parent /*= QModelIndex()*/, int row /*= 0*/) { int rows = model ? model->rowCount(parent) : 0; for (int i = 0; i < rows; ++i) @@ -134,7 +137,6 @@ namespace AzToolsFramework } BuildTableModelMap(sourceModel()); emit layoutChanged(); - } } // namespace AssetBrowser } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp index f7128cb504..894bbd8700 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -37,7 +37,6 @@ AZ_PUSH_DISABLE_WARNING( #include #include AZ_POP_DISABLE_WARNING -#pragma optimize("", off) namespace AzToolsFramework { namespace AssetBrowser @@ -128,7 +127,8 @@ namespace AzToolsFramework QTableView::rowsAboutToBeRemoved(parent, start, end); } - void AssetBrowserTableView::layoutChangedSlot([[maybe_unused]] const QList& parents,[[maybe_unused]] QAbstractItemModel::LayoutChangeHint hint) + void AssetBrowserTableView::layoutChangedSlot( + [[maybe_unused]] const QList& parents, [[maybe_unused]] QAbstractItemModel::LayoutChangeHint hint) { scrollToTop(); } @@ -159,7 +159,6 @@ namespace AzToolsFramework void AssetBrowserTableView::OnContextMenu([[maybe_unused]] const QPoint& point) { - const auto& selectedAssets = GetSelectedAssets(); if (selectedAssets.size() != 1) { @@ -167,7 +166,8 @@ namespace AzToolsFramework } QMenu menu(this); - AssetBrowserInteractionNotificationBus::Broadcast(&AssetBrowserInteractionNotificationBus::Events::AddContextMenuActions, this, &menu, selectedAssets); + AssetBrowserInteractionNotificationBus::Broadcast( + &AssetBrowserInteractionNotificationBus::Events::AddContextMenuActions, this, &menu, selectedAssets); if (!menu.isEmpty()) { menu.exec(QCursor::pos()); @@ -175,5 +175,4 @@ namespace AzToolsFramework } } // namespace AssetBrowser } // namespace AzToolsFramework -#pragma optimize("", on) #include "AssetBrowser/Views/moc_AssetBrowserTableView.cpp" diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index e722d6d03e..90b597e273 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -1,26 +1,26 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "EditorDefs.h" #include "AzAssetBrowserWindow.h" // AzToolsFramework +#include #include #include +#include #include #include -#include -#include // AzQtComponents #include @@ -35,7 +35,6 @@ AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserTableView); - class ListenerForShowAssetEditorEvent : public QObject , private AzToolsFramework::EditorEvents::Bus::Handler @@ -113,22 +112,29 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); - connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, - m_filterModel.data(), &AssetBrowserFilterModel::filterUpdatedSlot); - connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, this, [this]() - { - const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty(); - const bool selectFirstFilteredIndex = false; - m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); - }); + connect( + m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), + &AssetBrowserFilterModel::filterUpdatedSlot); + connect( + m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, this, + [this]() + { + const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty(); + const bool selectFirstFilteredIndex = false; + m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); + }); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, - this, &AzAssetBrowserWindow::SelectionChangedSlot); + connect( + m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, this, + &AzAssetBrowserWindow::SelectionChangedSlot); connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, &SearchWidget::ClearStringFilter); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter); + connect( + m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, + &SearchWidget::ClearStringFilter); + connect( + m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter); m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); } @@ -199,18 +205,21 @@ void AzAssetBrowserWindow::SelectAsset(const QString& assetPath) // interferes with the update from the select and expand, and if you don't // queue it, the tree doesn't expand reliably. - QTimer::singleShot(0, this, [this, filteredIndex = index] { - // the treeview has a filter model so we have to backwards go from that - QModelIndex index = m_filterModel->mapFromSource(filteredIndex); + QTimer::singleShot( + 0, this, + [this, filteredIndex = index] + { + // the treeview has a filter model so we have to backwards go from that + QModelIndex index = m_filterModel->mapFromSource(filteredIndex); - QTreeView* treeView = m_ui->m_assetBrowserTreeViewWidget; - ExpandTreeToIndex(treeView, index); + QTreeView* treeView = m_ui->m_assetBrowserTreeViewWidget; + ExpandTreeToIndex(treeView, index); - treeView->scrollTo(index); - treeView->setCurrentIndex(index); + treeView->scrollTo(index); + treeView->setCurrentIndex(index); - treeView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); - }); + treeView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); + }); } } @@ -243,11 +252,12 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& assetIdToOpen = AZ::Data::AssetId(sourceEntry->GetSourceUuid(), 0); fullFilePath = entry->GetFullPath(); } - + bool handledBySomeone = false; if (assetIdToOpen.IsValid()) { - AssetBrowserInteractionNotificationBus::Broadcast(&AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); + AssetBrowserInteractionNotificationBus::Broadcast( + &AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); } if (!handledBySomeone && !fullFilePath.empty()) @@ -255,7 +265,6 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& AzAssetBrowserRequestHandler::OpenWithOS(fullFilePath); } } - } void AzAssetBrowserWindow::DoubleClickedItemTableModel([[maybe_unused]] const QModelIndex& element) From 786b406d9824d09bbbbe82e27e8df75f6a5956f7 Mon Sep 17 00:00:00 2001 From: jonawals Date: Thu, 3 Jun 2021 11:34:33 +0100 Subject: [PATCH 088/233] Add seed and tia jobs --- .../build/Platform/Windows/build_config.json | 24 ++++++- scripts/build/TestImpactAnalysis/tiaf.py | 70 ++++++++----------- .../build/TestImpactAnalysis/tiaf_driver.py | 13 ++-- 3 files changed, 59 insertions(+), 48 deletions(-) diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index bd10c9e59a..081855b05a 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -21,6 +21,7 @@ ], "steps": [ "debug_vs2019", + "test_impact_seed", "test_debug_vs2019" ] }, @@ -30,6 +31,7 @@ ], "steps": [ "profile_vs2019", + "test_impact_analysis", "asset_profile_vs2019", "test_cpu_profile_vs2019" ] @@ -78,6 +80,24 @@ "SCRIPT_PARAMETERS": "--platform 3rdParty --type 3rdParty_all" } }, + "test_impact_seed": { + "TAGS": [ + ], + "COMMAND": "python_windows.cmd", + "PARAMETERS": { + "SCRIPT_PATH": "scripts/build/TestImpactAnalysis/tiaf_driver.py", + "SCRIPT_PARAMETERS": "--sequenceType seed --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.debug.json\"" + } + }, + "test_impact_analysis": { + "TAGS": [ + ], + "COMMAND": "python_windows.cmd", + "PARAMETERS": { + "SCRIPT_PATH": "scripts/build/TestImpactAnalysis/tiaf_driver.py", + "SCRIPT_PARAMETERS": "--sequenceType tia --destCommit !CHANGE_ID! --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.profile.json\"" + } + }, "debug_vs2019": { "TAGS": [ "weekly-build-metrics" @@ -86,7 +106,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE -DLY_TEST_IMPACT_ACTIVE=1 -DLY_TEST_IMPACT_INSTRUMENTATION_BIN=\"c:\\ly\\3rdParty\\ackages\\OpenCppCoverage\\Binary\\windows-x64\\OpenCppCoverage.exe\"", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -117,7 +137,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_TEST_IMPACT_ACTIVE=1 -DLY_TEST_IMPACT_INSTRUMENTATION_BIN=\"c:\\ly\\3rdParty\\ackages\\OpenCppCoverage\\Binary\\windows-x64\\OpenCppCoverage.exe\"", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" diff --git a/scripts/build/TestImpactAnalysis/tiaf.py b/scripts/build/TestImpactAnalysis/tiaf.py index 4686c08f93..01e2cde8ba 100644 --- a/scripts/build/TestImpactAnalysis/tiaf.py +++ b/scripts/build/TestImpactAnalysis/tiaf.py @@ -25,8 +25,12 @@ def is_child_path(parent_path, child_path): # Enumerations for test sequence types class SequenceType(Enum): + # Regular sequence as-per the tiaf regular sequence REGULAR = 1 + # TIA sequence as-per the tiaf read-only impact analysis sequence TEST_IMPACT_ANALYSIS = 2 + # Seed sequence as-per the tiaf seed sequence + SEED = 3 class TestImpact: def __init__(self, config_file, dst_commit): @@ -43,7 +47,6 @@ class TestImpact: self.__tiaf_bin = config["repo"]["tiaf_bin"] if not os.path.isfile(self.__tiaf_bin): raise FileNotFoundError("Could not find tiaf binary") - self.__source_of_truth = config["repo"]["source_of_truth"] self.__active_workspace = config["workspace"]["active"]["root"] self.__historic_workspace = config["workspace"]["historic"]["root"] self.__temp_workspace = config["workspace"]["temp"]["root"] @@ -58,16 +61,7 @@ class TestImpact: self.__dst_commit = dst_commit self.__src_commit = None self.__has_src_commit = False - if self.__repo.current_branch == self.__source_of_truth: - self.__is_source_of_truth = True - else: - self.__is_source_of_truth = False print(f"The repository is located at '{self.__repo_dir}' and the current branch is '{self.__branch}'.") - print(f"The source of truth branch is '{self.__source_of_truth}'.") - if self.__is_source_of_truth: - print("I am the source of truth.") - else: - print("I am *not* the source of truth.") # Restricts change lists from checking in test impact analysis files def __check_for_restricted_files(self, file_path): @@ -77,7 +71,7 @@ class TestImpact: def __read_last_run_hash(self): self.__has_src_commit = False if os.path.isfile(self.__last_commit_hash_path): - print(f"Previous commit hash found at 'self.__last_commit_hash_path'") + print(f"Previous commit hash found at '{self.__last_commit_hash_path}'") with open(self.__last_commit_hash_path) as file: self.__src_commit = file.read() self.__has_src_commit = True @@ -104,7 +98,7 @@ class TestImpact: print(e) return # A diff was generated, attempt to parse the diff and construct the change list - print(f"Generated diff between commits {self.__src_commit} and {self.__dst_commit}.") + print(f"Generated diff between commits '{self.__src_commit}' and '{self.__dst_commit}': '{diff_path}'.") change_list = {} change_list["createdFiles"] = [] change_list["updatedFiles"] = [] @@ -152,35 +146,27 @@ class TestImpact: # Runs the specified test sequence def run(self, sequence_type, safe_mode, test_timeout, global_timeout): args = [] - if self.__has_change_list: - args.append(f"-changelist={self.__change_list_path}") - if sequence_type == SequenceType.REGULAR: - print("Sequence type: regular.") - args.append("--sequence=regular") - elif sequence_type == SequenceType.TEST_IMPACT_ANALYSIS: - print("Sequence type: test impact analysis.") - if self.__is_source_of_truth: - # Source of truth branch will allways attempt a seed if no test impact analysis data is available - print("This branch is the source of truth, a seed sequence will be run if there is no test impact analysis data.") - args.append("--sequence=tiaorseed") - args.append("--fpolicy=continue") - else: - # Non source of truth branches will fall back to a regular test run if no test impact analysis data is available - print("This branch is not the source of truth, a regular sequence will be run if there is no test impact analysis data.") - args.append("--sequence=tia") - args.append("--fpolicy=abort") - - if safe_mode == True: - print("Safe mode is on, the discarded test targets will be run after the selected test targets.") - args.append("--safemode=on") - else: - print("Safe mode is off, the discarded test targets will not be run.") + print("Please note: test impact analysis sequences will be run in read-only mode (seed sequences are unaffected).") + if sequence_type == SequenceType.REGULAR: + print("Sequence type: regular.") + args.append("--sequence=regular") + args.append("--fpolicy=abort") + elif sequence_type == SequenceType.SEED: + print("Sequence type: seed.") + args.append("--sequence=seed") + args.append("--fpolicy=continue") + elif sequence_type == SequenceType.TEST_IMPACT_ANALYSIS: + print("Sequence type: test impact analysis (no write).") + args.append("--fpolicy=abort") + if self.__has_change_list: + args.append(f"-changelist={self.__change_list_path}") + args.append("--sequence=tianowrite") else: - raise ValueError(sequence_type) + print(f"No change list was generated, falling back to a regular sequence.") + print("Sequence type: Regular.") + args.append("--sequence=regular") else: - print(f"No change list was generated, this will cause test impact analysis sequences on branches other than the source of truth to fall back to a regular sequence.") - print("Sequence type: Regular.") - args.append("--sequence=regular") + raise ValueError(sequence_type) if test_timeout != None: args.append(f"--ttimeout={test_timeout}") @@ -193,8 +179,10 @@ class TestImpact: print(*args) result = subprocess.run([self.__tiaf_bin] + args) if result.returncode == 0: - print("Test impact analysis runtime returned successfully. Updating historical artifacts...") - self.__write_last_run_hash(self.__dst_commit) + print("Test impact analysis runtime returned successfully.") + if sequence_type == SequenceType.SEED: + print("Writing historical meta-data...") + self.__write_last_run_hash(self.__dst_commit) print("Complete!") else: print(f"The test impact analysis runtime returned with error: '{result.returncode}'.") diff --git a/scripts/build/TestImpactAnalysis/tiaf_driver.py b/scripts/build/TestImpactAnalysis/tiaf_driver.py index 4f20fa1070..1cc7ecb630 100644 --- a/scripts/build/TestImpactAnalysis/tiaf_driver.py +++ b/scripts/build/TestImpactAnalysis/tiaf_driver.py @@ -31,6 +31,8 @@ def parse_args(): return SequenceType.REGULAR elif value == "tia": return SequenceType.TEST_IMPACT_ANALYSIS + elif value == "seed": + return SequenceType.SEED else: raise ValueError(value) @@ -42,23 +44,24 @@ def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('--config', dest="config", type=file_path, help="Path to the test impact analysis framework configuration file", required=True) - parser.add_argument('--destCommit', dest="dst_commit", help="Commit to run test impact analysis on (if empty, HEAD^ will be used)") - parser.add_argument('--sequenceType', dest="sequence_type", type=sequence_type, help="Test sequence type to run ('regular' or 'tia')", required=True) + parser.add_argument('--destCommit', dest="dst_commit", help="Commit to run test impact analysis on (not required for seed)") + parser.add_argument('--sequenceType', dest="sequence_type", type=sequence_type, help="Test sequence type to run ('regular', 'seed' or 'tia')", required=True) parser.add_argument('--suites', dest="suites", nargs='*', help="Suites to include for regular tes sequences (use '*' for all suites)") - parser.add_argument('--safeMode', dest='safe_mode', help="If set, will run any test impact analysis runs in safe mode (unselected tests will still be run)") parser.add_argument('--testTimeout', dest="test_timeout", type=timout_type, help="Maximum flight time (in seconds) of any test target before being terminated", required=False) parser.add_argument('--globalTimeout', dest="global_timeout", type=timout_type, help="Maximum tun time of the sequence before being terminated", required=False) - parser.set_defaults(dst_commit="HEAD^") parser.set_defaults(suites="*") parser.set_defaults(safe_mode=False) parser.set_defaults(test_timeout=None) parser.set_defaults(global_timeout=None) args = parser.parse_args() + + if args.sequence_type == SequenceType.TEST_IMPACT_ANALYSIS and args.dst_commit == None: + raise ValueError("Test impact analysis sequence must have a change list") return args if __name__ == "__main__": args = parse_args() tiaf = TestImpact(args.config, args.dst_commit) - return_code = tiaf.run(args.sequence_type, args.safe_mode, args.test_timeout, args.global_timeout) + return_code = tiaf.run(args.sequence_type, args.test_timeout, args.global_timeout) sys.exit(return_code) \ No newline at end of file From eb67b6b452a768c10e2a905711e2ab1710bcbc4a Mon Sep 17 00:00:00 2001 From: igarri Date: Thu, 3 Jun 2021 13:36:47 +0100 Subject: [PATCH 089/233] Adding namespace aliases and API comments --- .../Views/AssetBrowserTableView.h | 2 +- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 71 +++++++++---------- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h index 65f97d1a6d..15bc95f13c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -32,7 +32,7 @@ namespace AzToolsFramework class AssetBrowserFilterModel; class EntryDelegate; - class AssetBrowserTableView + class AssetBrowserTableView //! Table view that displays the asset browser entries in a list. : public QTableView , public AssetBrowserViewRequestBus::Handler , public AssetBrowserComponentNotificationBus::Handler diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 90b597e273..481cc35635 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -74,8 +74,9 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_ui->setupUi(this); m_ui->m_searchWidget->Setup(true, true); - using namespace AzToolsFramework::AssetBrowser; - AssetBrowserComponentRequestBus::BroadcastResult(m_assetBrowserModel, &AssetBrowserComponentRequests::GetAssetBrowserModel); + namespace AB = AzToolsFramework::AssetBrowser; + + AB::AssetBrowserComponentRequestBus::BroadcastResult(m_assetBrowserModel, &AB::AssetBrowserComponentRequests::GetAssetBrowserModel); AZ_Assert(m_assetBrowserModel, "Failed to get filebrowser model"); m_filterModel->setSourceModel(m_assetBrowserModel); m_filterModel->SetFilter(m_ui->m_searchWidget->GetFilter()); @@ -89,34 +90,34 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_tableModel->setSourceModel(m_filterModel.data()); m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data()); connect( - m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, m_tableModel.data(), - &AssetBrowserTableModel::UpdateTableModelMaps); + m_filterModel.data(), &AB::AssetBrowserFilterModel::filterChanged, m_tableModel.data(), + &AB::AssetBrowserTableModel::UpdateTableModelMaps); connect( - m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::selectionChangedSignal, this, + m_ui->m_assetBrowserTableViewWidget, &AB::AssetBrowserTableView::selectionChangedSignal, this, &AzAssetBrowserWindow::SelectionChangedSlot); connect( m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItemTableModel); connect( - m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget, - &SearchWidget::ClearStringFilter); + m_ui->m_assetBrowserTableViewWidget, &AB::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget, + &AB::SearchWidget::ClearStringFilter); connect( - m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget, - &SearchWidget::ClearTypeFilter); + m_ui->m_assetBrowserTableViewWidget, &AB::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget, + &AB::SearchWidget::ClearTypeFilter); m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main"); - connect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); + connect(m_filterModel.data(), &AB::AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); connect(m_ui->m_viewSwitcherCheckBox, &QCheckBox::stateChanged, this, &AzAssetBrowserWindow::LockToDefaultView); } m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); connect( - m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), - &AssetBrowserFilterModel::filterUpdatedSlot); + m_ui->m_searchWidget->GetFilter().data(), &AB::AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), + &AB::AssetBrowserFilterModel::filterUpdatedSlot); connect( - m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, this, + m_filterModel.data(), &AB::AssetBrowserFilterModel::filterChanged, this, [this]() { const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty(); @@ -125,16 +126,17 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) }); connect( - m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, this, + m_ui->m_assetBrowserTreeViewWidget, &AB::AssetBrowserTreeView::selectionChangedSignal, this, &AzAssetBrowserWindow::SelectionChangedSlot); connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); connect( - m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, - &SearchWidget::ClearStringFilter); + m_ui->m_assetBrowserTreeViewWidget, &AB::AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, + &AB::SearchWidget::ClearStringFilter); connect( - m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter); + m_ui->m_assetBrowserTreeViewWidget, &AB::AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, + &AB::SearchWidget::ClearTypeFilter); m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); } @@ -192,8 +194,6 @@ static void ExpandTreeToIndex(QTreeView* treeView, const QModelIndex& index) void AzAssetBrowserWindow::SelectAsset(const QString& assetPath) { - using namespace AzToolsFramework::AssetBrowser; - QModelIndex index = m_assetBrowserModel->findIndex(assetPath); if (index.isValid()) { @@ -232,21 +232,21 @@ void AzAssetBrowserWindow::SelectionChangedSlot(const QItemSelection& /*selected // just becuase on some OS clicking once is activation. void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& element) { - using namespace AzToolsFramework; - using namespace AzToolsFramework::AssetBrowser; + namespace AB = AzToolsFramework::AssetBrowser; + // assumption: Double clicking an item selects it before telling us we double clicked it. const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); - for (const AssetBrowserEntry* entry : selectedAssets) + for (const AB::AssetBrowserEntry* entry : selectedAssets) { AZ::Data::AssetId assetIdToOpen; AZStd::string fullFilePath; - if (const ProductAssetBrowserEntry* productEntry = azrtti_cast(entry)) + if (const AB::ProductAssetBrowserEntry* productEntry = azrtti_cast(entry)) { assetIdToOpen = productEntry->GetAssetId(); fullFilePath = entry->GetFullPath(); } - else if (const SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry)) + else if (const AB::SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry)) { // manufacture an empty AssetID with the source's UUID assetIdToOpen = AZ::Data::AssetId(sourceEntry->GetSourceUuid(), 0); @@ -256,8 +256,8 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& bool handledBySomeone = false; if (assetIdToOpen.IsValid()) { - AssetBrowserInteractionNotificationBus::Broadcast( - &AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); + AB::AssetBrowserInteractionNotificationBus::Broadcast( + &AB::AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); } if (!handledBySomeone && !fullFilePath.empty()) @@ -269,21 +269,20 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& void AzAssetBrowserWindow::DoubleClickedItemTableModel([[maybe_unused]] const QModelIndex& element) { - using namespace AzToolsFramework; - using namespace AzToolsFramework::AssetBrowser; + namespace AB = AzToolsFramework::AssetBrowser; // assumption: Double clicking an item selects it before telling us we double clicked it. const auto& selectedAssets = m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets(); - for (const AssetBrowserEntry* entry : selectedAssets) + for (const AB::AssetBrowserEntry* entry : selectedAssets) { AZ::Data::AssetId assetIdToOpen; AZStd::string fullFilePath; - if (const ProductAssetBrowserEntry* productEntry = azrtti_cast(entry)) + if (const AB::ProductAssetBrowserEntry* productEntry = azrtti_cast(entry)) { assetIdToOpen = productEntry->GetAssetId(); fullFilePath = entry->GetFullPath(); } - else if (const SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry)) + else if (const AB::SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry)) { // manufacture an empty AssetID with the source's UUID assetIdToOpen = AZ::Data::AssetId(sourceEntry->GetSourceUuid(), 0); @@ -293,8 +292,8 @@ void AzAssetBrowserWindow::DoubleClickedItemTableModel([[maybe_unused]] const QM bool handledBySomeone = false; if (assetIdToOpen.IsValid()) { - AssetBrowserInteractionNotificationBus::Broadcast( - &AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); + AB::AssetBrowserInteractionNotificationBus::Broadcast( + &AB::AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); } if (!handledBySomeone && !fullFilePath.empty()) @@ -312,12 +311,12 @@ void AzAssetBrowserWindow::SwitchDisplayView(bool state) void AzAssetBrowserWindow::LockToDefaultView(bool state) { - using namespace AzToolsFramework; - using namespace AzToolsFramework::AssetBrowser; + using AzToolsFramework::AssetBrowser::AssetBrowserFilterModel; SwitchDisplayView(!state); if (state == true) { - disconnect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); + disconnect( + m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); } else { From 0e4a632417625d1dceb28a2d62d14a0aa8d277e9 Mon Sep 17 00:00:00 2001 From: moudgils Date: Thu, 3 Jun 2021 22:45:50 -0700 Subject: [PATCH 090/233] Add support for re-binding SRG entries if the drawItem has a new pso which changes how a srg is used in the shader. Also optimized api usage to use single call for UseResource and for setting stream buffers. --- .../Shaders/MorphTargets/MorphTargetSRG.azsli | 2 +- .../Metal/Code/Source/RHI/ArgumentBuffer.cpp | 85 ++++++++++--------- .../Metal/Code/Source/RHI/ArgumentBuffer.h | 15 +++- .../RHI/Metal/Code/Source/RHI/CommandList.cpp | 56 ++++++++---- .../RHI/Metal/Code/Source/RHI/CommandList.h | 1 + .../Metal/Code/Source/RHI/PipelineLayout.cpp | 7 ++ .../Metal/Code/Source/RHI/PipelineLayout.h | 6 ++ 7 files changed, 110 insertions(+), 62 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli index 171e803c2c..83193c8559 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli @@ -16,7 +16,7 @@ ShaderResourceGroup MorphTargetPassSrg : SRG_PerPass { - RWStruturedBuffer m_accumulatedDeltas; + RWStructuredBuffer m_accumulatedDeltas; } // This class represents the data that is passed to the morph target compute shader of an individual delta diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp index 6ffd15e0bc..1e2edc6520 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp @@ -386,6 +386,11 @@ namespace AZ void ArgumentBuffer::AddUntrackedResourcesToEncoder(id commandEncoder, const ShaderResourceGroupVisibility& srgResourcesVisInfo) const { + + ComputeResourcesToMakeResidentMap resourcesToMakeResidentCompute; + GraphicsResourcesToMakeResidentMap resourcesToMakeResidentGraphics; + + //Cache the constant buffer associated with a srg if (m_constantBufferSize) { uint8_t numBitsSet = RHI::CountBitsSet(static_cast(srgResourcesVisInfo.m_constantDataStageMask)); @@ -393,28 +398,19 @@ namespace AZ { if(RHI::CheckBitsAny(srgResourcesVisInfo.m_constantDataStageMask, RHI::ShaderStageMask::Compute)) { - [static_cast>(commandEncoder) useResource:m_constantBuffer.GetGpuAddress>() usage:MTLResourceUsageRead]; + resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArray[resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArrayLen++] = m_constantBuffer.GetGpuAddress>(); } else { MTLRenderStages mtlRenderStages = GetRenderStages(srgResourcesVisInfo.m_constantDataStageMask); - [static_cast>(commandEncoder) useResource:m_constantBuffer.GetGpuAddress>() - usage:MTLResourceUsageRead - stages:mtlRenderStages]; + AZStd::pair key = AZStd::make_pair(MTLResourceUsageRead, mtlRenderStages); + resourcesToMakeResidentGraphics[key].m_resourceArray[resourcesToMakeResidentGraphics[key].m_resourceArrayLen++] = m_constantBuffer.GetGpuAddress>(); } - } } - ApplyUseResource(commandEncoder, m_resourceBindings, srgResourcesVisInfo); - } - - void ArgumentBuffer::ApplyUseResource(id encoder, - const ResourceBindingsMap& resourceMap, - const ShaderResourceGroupVisibility& srgResourcesVisInfo) const - { - - CommandEncoderType encodeType = CommandEncoderType::Invalid; - for (const auto& it : resourceMap) + + //Cach all the resources within a srg that are used by the shader based on the visibility information + for (const auto& it : m_resourceBindings) { //Extract the visibility mask for the give resource auto visMaskIt = srgResourcesVisInfo.m_resourcesStageMask.find(it.first); @@ -426,40 +422,50 @@ namespace AZ { if(RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Compute)) { - //Call UseResource on all resources for Compute stage - ApplyUseResourceToCompute(encoder, it.second); - encodeType = CommandEncoderType::Compute; + ApplyUseResourceToCompute(commandEncoder, it.second, resourcesToMakeResidentCompute); } else { - //Call UseResource on all resources for Vertex and Fragment stages AZ_Assert(RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Vertex) || RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Fragment), "The visibility mask %i is not set for Vertex or fragment stage", visMaskIt->second); - ApplyUseResourceToGraphic(encoder, visMaskIt->second, it.second); - encodeType = CommandEncoderType::Render; + ApplyUseResourceToGraphic(commandEncoder, visMaskIt->second, it.second, resourcesToMakeResidentGraphics); } } } + + //Call UseResource on all resources for Compute stage + for (const auto& key : resourcesToMakeResidentCompute) + { + [static_cast>(commandEncoder) useResources: key.second.m_resourceArray.data() + count: key.second.m_resourceArrayLen + usage: key.first]; + } + + //Call UseResource on all resources for Vertex and Fragment stages + for (const auto& key : resourcesToMakeResidentGraphics) + { + [static_cast>(commandEncoder) useResources: key.second.m_resourceArray.data() + count: key.second.m_resourceArrayLen + usage: key.first.first + stages: key.first.second]; + } } - - void ArgumentBuffer::ApplyUseResourceToCompute(id encoder, const ResourceBindingsSet& resourceBindingDataSet) const + + void ArgumentBuffer::ApplyUseResourceToCompute(id encoder, const ResourceBindingsSet& resourceBindingDataSet, ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const { for (const auto& resourceBindingData : resourceBindingDataSet) { ResourceType rescType = resourceBindingData.m_resourcPtr->GetResourceType(); + MTLResourceUsage resourceUsage = MTLResourceUsageRead; switch(rescType) { case ResourceType::MtlTextureType: { - MTLResourceUsage resourceUsage = GetImageResourceUsage(resourceBindingData.m_imageAccess); - [static_cast>(encoder) useResource:resourceBindingData.m_resourcPtr->GetGpuAddress>() usage:resourceUsage]; - + resourceUsage |= GetImageResourceUsage(resourceBindingData.m_imageAccess); break; } case ResourceType::MtlBufferType: { - MTLResourceUsage resourceUsage = GetBufferResourceUsage(resourceBindingData.m_bufferAccess); - [static_cast>(encoder) useResource:resourceBindingData.m_resourcPtr->GetGpuAddress>() usage:resourceUsage]; - + resourceUsage |= GetBufferResourceUsage(resourceBindingData.m_bufferAccess); break; } default: @@ -467,13 +473,15 @@ namespace AZ AZ_Assert(false, "Undefined Resource type"); } } + resourcesToMakeResidentMap[resourceUsage].m_resourceArray[resourcesToMakeResidentMap[resourceUsage].m_resourceArrayLen++] = resourceBindingData.m_resourcPtr->GetGpuAddress>(); } } - void ArgumentBuffer::ApplyUseResourceToGraphic(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet) const + void ArgumentBuffer::ApplyUseResourceToGraphic(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet, GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const { - + MTLRenderStages mtlRenderStages = GetRenderStages(visShaderMask); + MTLResourceUsage resourceUsage = MTLResourceUsageRead; for (const auto& resourceBindingData : resourceBindingDataSet) { ResourceType rescType = resourceBindingData.m_resourcPtr->GetResourceType(); @@ -481,20 +489,12 @@ namespace AZ { case ResourceType::MtlTextureType: { - MTLResourceUsage resourceUsage = GetImageResourceUsage(resourceBindingData.m_imageAccess); - [static_cast>(encoder) useResource:resourceBindingData.m_resourcPtr->GetGpuAddress>() - usage:resourceUsage - stages:mtlRenderStages]; - + resourceUsage |= GetImageResourceUsage(resourceBindingData.m_imageAccess); break; } case ResourceType::MtlBufferType: { - MTLResourceUsage resourceUsage = GetBufferResourceUsage(resourceBindingData.m_bufferAccess); - [static_cast>(encoder) useResource:resourceBindingData.m_resourcPtr->GetGpuAddress>() - usage:resourceUsage - stages:mtlRenderStages]; - + resourceUsage |= GetBufferResourceUsage(resourceBindingData.m_bufferAccess); break; } default: @@ -502,8 +502,9 @@ namespace AZ AZ_Assert(false, "Undefined Resource type"); } } + AZStd::pair key = AZStd::make_pair(resourceUsage, mtlRenderStages); + resourcesToMakeResidentMap[key].m_resourceArray[resourcesToMakeResidentMap[key].m_resourceArrayLen++] = resourceBindingData.m_resourcPtr->GetGpuAddress>(); } } - } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h index a5a8e00e69..2434b8312d 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h @@ -119,8 +119,17 @@ namespace AZ using ResourceBindingsMap = AZStd::unordered_map; ResourceBindingsMap m_resourceBindings; - void ApplyUseResourceToCompute(id encoder, const ResourceBindingsSet& resourceBindingData) const; - void ApplyUseResourceToGraphic(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet) const; + static const int MaxEntriesInArgTable = 31; + struct MetalResourceArray + { + AZStd::array, MaxEntriesInArgTable> m_resourceArray; + int m_resourceArrayLen = 0; + }; + using ComputeResourcesToMakeResidentMap = AZStd::unordered_map; + using GraphicsResourcesToMakeResidentMap = AZStd::unordered_map, MetalResourceArray>; + + void ApplyUseResourceToCompute(id encoder, const ResourceBindingsSet& resourceBindingData, ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; + void ApplyUseResourceToGraphic(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet, GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; //! Use visibility information to call UseResource on all resources for this Argument Buffer void ApplyUseResource(id encoder, const ResourceBindingsMap& resourceMap, @@ -144,8 +153,6 @@ namespace AZ #endif ShaderResourceGroupPool* m_srgPool = nullptr; - - static const int MaxEntriesInArgTable = 31; NSCache* m_samplerCache; }; } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp index 3eca8dabd8..10fbe372b1 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp @@ -258,24 +258,19 @@ namespace AZ continue; } + uint32_t srgVisIndex = pipelineLayout.GetSlotByIndex(shaderResourceGroup->GetBindingSlot()); + const RHI::ShaderStageMask& srgVisInfo = pipelineLayout.GetSrgVisibility(srgVisIndex); + if (bindings.m_srgsByIndex[srgIndex] != shaderResourceGroup) { bindings.m_srgsByIndex[srgIndex] = shaderResourceGroup; auto& compiledArgBuffer = shaderResourceGroup->GetCompiledArgumentBuffer(); - id argBuffer = compiledArgBuffer.GetArgEncoderBuffer(); size_t argBufferOffset = compiledArgBuffer.GetOffset(); - - uint32_t srgVisIndex = pipelineLayout.GetSlotByIndex(shaderResourceGroup->GetBindingSlot()); - const RHI::ShaderStageMask& srgVisInfo = pipelineLayout.GetSrgVisibility(srgVisIndex); - + if(srgVisInfo != RHI::ShaderStageMask::None) { - const ShaderResourceGroupVisibility& srgResourcesVisInfo = pipelineLayout.GetSrgResourcesVisibility(srgVisIndex); - - //For graphics and compute encoder bind the argument buffer and - //make the resource resident for the duration of the work associated with the current scope - //and ensure that it's in a format compatible with the appropriate metal function. + //For graphics and compute encoder bind the argument buffer if(m_commandEncoderType == CommandEncoderType::Render) { id renderEncoder = GetEncoder>(); @@ -293,7 +288,6 @@ namespace AZ offset:argBufferOffset atIndex:slotIndex]; } - shaderResourceGroup->AddUntrackedResourcesToEncoder(m_encoder, srgResourcesVisInfo); } else if(m_commandEncoderType == CommandEncoderType::Compute) { @@ -301,6 +295,28 @@ namespace AZ [computeEncoder setBuffer:argBuffer offset:argBufferOffset atIndex:pipelineLayout.GetSlotByIndex(srgIndex)]; + } + } + } + + //Check againgst the srg resources visibility hash as it is possible for draw items to have different PSO in the same pass. + const AZ::HashValue64 srgResourcesVisHash = pipelineLayout.GetSrgResourcesVisibilityHash(srgVisIndex); + if(bindings.m_srgVisHashByIndex[srgIndex] != srgResourcesVisHash) + { + bindings.m_srgVisHashByIndex[srgIndex] = srgResourcesVisHash; + if(srgVisInfo != RHI::ShaderStageMask::None) + { + const ShaderResourceGroupVisibility& srgResourcesVisInfo = pipelineLayout.GetSrgResourcesVisibility(srgVisIndex); + + //For graphics and compute encoder bind the argument buffer and + //make the resource resident for the duration of the work associated with the current scope + //and ensure that it's in a format compatible with the appropriate metal function. + if(m_commandEncoderType == CommandEncoderType::Render) + { + shaderResourceGroup->AddUntrackedResourcesToEncoder(m_encoder, srgResourcesVisInfo); + } + else if(m_commandEncoderType == CommandEncoderType::Compute) + { shaderResourceGroup->AddUntrackedResourcesToEncoder(m_encoder, srgResourcesVisInfo); } } @@ -447,6 +463,7 @@ namespace AZ for (size_t i = 0; i < bindings.m_srgsByIndex.size(); ++i) { bindings.m_srgsByIndex[i] = nullptr; + bindings.m_srgVisHashByIndex[i] = AZ::HashValue64{0}; } const PipelineLayout& pipelineLayout = pipelineState->GetPipelineLayout(); @@ -469,6 +486,10 @@ namespace AZ void CommandList::SetStreamBuffers(const RHI::StreamBufferView* streams, uint32_t count) { + int bufferArrayLen = 0; + AZStd::array, METAL_MAX_ENTRIES_BUFFER_ARG_TABLE> mtlStreamBuffers; + AZStd::array mtlStreamBufferOffsets; + AZ::HashValue64 streamsHash = AZ::HashValue64{0}; for (uint32_t i = 0; i < count; ++i) { @@ -479,18 +500,23 @@ namespace AZ { m_state.m_streamsHash = streamsHash; AZ_Assert(count <= METAL_MAX_ENTRIES_BUFFER_ARG_TABLE , "Slots needed cannot exceed METAL_MAX_ENTRIES_BUFFER_ARG_TABLE"); - for (uint32_t i = 0; i < count; ++i) + + NSRange range = {METAL_MAX_ENTRIES_BUFFER_ARG_TABLE - count, count}; + //For metal the stream buffers are populated from bottom to top as the top slots are taken by argument buffers + for (int i = count-1; i >= 0; --i) { if (streams[i].GetBuffer()) { const Buffer * buff = static_cast(streams[i].GetBuffer()); id mtlBuff = buff->GetMemoryView().GetGpuAddress>(); - uint32_t VBIndex = (METAL_MAX_ENTRIES_BUFFER_ARG_TABLE - 1) - i; uint32_t offset = streams[i].GetByteOffset() + buff->GetMemoryView().GetOffset(); - id renderEncoder = GetEncoder>(); - [renderEncoder setVertexBuffer: mtlBuff offset: offset atIndex: VBIndex]; + mtlStreamBuffers[bufferArrayLen] = mtlBuff; + mtlStreamBufferOffsets[bufferArrayLen] = offset; + bufferArrayLen++; } } + id renderEncoder = GetEncoder>(); + [renderEncoder setVertexBuffers: mtlStreamBuffers.data() offsets: mtlStreamBufferOffsets.data() withRange: range]; } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h index 6660beb2c4..dd4e382471 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h @@ -99,6 +99,7 @@ namespace AZ { AZStd::array m_srgsByIndex; AZStd::array m_srgsBySlot; + AZStd::array m_srgVisHashByIndex; }; ShaderResourceBindings& GetShaderResourceBindingsByPipelineType(RHI::PipelineStateType pipelineType); diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp index f237884aab..70d36f8d6d 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp @@ -70,6 +70,7 @@ namespace AZ m_srgVisibilities.resize(RHI::Limits::Pipeline::ShaderResourceGroupCountMax); m_srgResourcesVisibility.resize(RHI::Limits::Pipeline::ShaderResourceGroupCountMax); + m_srgResourcesVisibilityHash.resize(RHI::Limits::Pipeline::ShaderResourceGroupCountMax); for (uint32_t srgLayoutIdx = 0; srgLayoutIdx < groupLayoutCount; ++srgLayoutIdx) { const RHI::ShaderResourceGroupLayout& srgLayout = *descriptor.GetShaderResourceGroupLayout(srgLayoutIdx); @@ -111,6 +112,7 @@ namespace AZ m_srgVisibilities[srgIndex] = mask; m_srgResourcesVisibility[srgIndex] = srgVis; + m_srgResourcesVisibilityHash[srgIndex] = srgVis.GetHash(); } // Cache the inline constant size and slot index @@ -141,6 +143,11 @@ namespace AZ return m_srgResourcesVisibility[index]; } + const AZ::HashValue64 PipelineLayout::GetSrgResourcesVisibilityHash(uint32_t index) const + { + return m_srgResourcesVisibilityHash[index]; + } + uint32_t PipelineLayout::GetRootConstantsSize() const { return m_rootConstantsSize; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.h index e8cd249393..6fd06ea29b 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.h @@ -57,6 +57,9 @@ namespace AZ /// Returns srgVisibility data const ShaderResourceGroupVisibility& GetSrgResourcesVisibility(uint32_t index) const; + /// Returns srgVisibility hash + const AZ::HashValue64 GetSrgResourcesVisibilityHash(uint32_t index) const; + /// Returns the root constant specific layout information uint32_t GetRootConstantsSize() const; uint32_t GetRootConstantsSlotIndex() const; @@ -84,6 +87,9 @@ namespace AZ /// Cache Visibility across all the resources within the SRG AZStd::fixed_vector m_srgResourcesVisibility; + /// Cache Visibility hash across all the resources within the SRG + AZStd::fixed_vector m_srgResourcesVisibilityHash; + uint32_t m_rootConstantSlotIndex = (uint32_t)-1; uint32_t m_rootConstantsSize = 0; }; From 3d1abdc4e3934888ad253d0563f614b094496418 Mon Sep 17 00:00:00 2001 From: igarri Date: Fri, 4 Jun 2021 12:16:32 +0100 Subject: [PATCH 091/233] Pull request corrections, namespaces, style, etc --- .../Entries/RootAssetBrowserEntry.cpp | 2 +- .../AssetBrowser/Search/Filter.h | 1 + .../AssetBrowser/Views/EntryDelegate.cpp | 8 +-- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 61 +++++++++---------- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.cpp index 2ccc2cd6bf..e626f31b62 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.cpp @@ -287,7 +287,7 @@ namespace AzToolsFramework product->m_assetType.ToString(product->m_assetTypeString); AZ::Data::AssetCatalogRequestBus::BroadcastResult(product->m_relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, assetId); QString displayPath = QString::fromUtf8(product->m_relativePath.c_str()); - displayPath.remove(QString("/" + QString::fromUtf8(product->m_name.c_str()))); + displayPath.remove(QString(AZ_CORRECT_DATABASE_SEPARATOR + QString::fromUtf8(product->m_name.c_str()))); product->m_displayPath = displayPath; EntryCache::GetInstance()->m_productAssetIdMap[assetId] = product; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.h index 135dd00925..b67b699862 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Search/Filter.h @@ -110,6 +110,7 @@ namespace AzToolsFramework ~StringFilter() override = default; void SetFilterString(const QString& filterString); + protected: QString GetNameInternal() const override; bool MatchInternal(const AssetBrowserEntry* entry) const override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index c73c00981b..d0ac05020d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -95,13 +95,13 @@ namespace AzToolsFramework remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing. } - QString displayString = qvariant_cast(index.data(index.column())); + QString displayString = index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name) + ? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name))) + : qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Path))); style->drawItemText( painter, remainingRect, option.displayAlignment, actualPalette, isEnabled, - index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name) - ? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name))) - : qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Path))), + displayString, isSelected ? QPalette::HighlightedText : QPalette::Text); } } diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 481cc35635..60ff6f9549 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -74,9 +74,9 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_ui->setupUi(this); m_ui->m_searchWidget->Setup(true, true); - namespace AB = AzToolsFramework::AssetBrowser; + namespace AzAssetBrowser = AzToolsFramework::AssetBrowser; - AB::AssetBrowserComponentRequestBus::BroadcastResult(m_assetBrowserModel, &AB::AssetBrowserComponentRequests::GetAssetBrowserModel); + AzAssetBrowser::AssetBrowserComponentRequestBus::BroadcastResult(m_assetBrowserModel, &AzAssetBrowser::AssetBrowserComponentRequests::GetAssetBrowserModel); AZ_Assert(m_assetBrowserModel, "Failed to get filebrowser model"); m_filterModel->setSourceModel(m_assetBrowserModel); m_filterModel->SetFilter(m_ui->m_searchWidget->GetFilter()); @@ -90,34 +90,34 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_tableModel->setSourceModel(m_filterModel.data()); m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data()); connect( - m_filterModel.data(), &AB::AssetBrowserFilterModel::filterChanged, m_tableModel.data(), - &AB::AssetBrowserTableModel::UpdateTableModelMaps); + m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, m_tableModel.data(), + &AzAssetBrowser::AssetBrowserTableModel::UpdateTableModelMaps); connect( - m_ui->m_assetBrowserTableViewWidget, &AB::AssetBrowserTableView::selectionChangedSignal, this, + m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this, &AzAssetBrowserWindow::SelectionChangedSlot); connect( m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItemTableModel); connect( - m_ui->m_assetBrowserTableViewWidget, &AB::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget, - &AB::SearchWidget::ClearStringFilter); + m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget, + &AzAssetBrowser::SearchWidget::ClearStringFilter); connect( - m_ui->m_assetBrowserTableViewWidget, &AB::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget, - &AB::SearchWidget::ClearTypeFilter); + m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget, + &AzAssetBrowser::SearchWidget::ClearTypeFilter); m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main"); - connect(m_filterModel.data(), &AB::AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); + connect(m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); connect(m_ui->m_viewSwitcherCheckBox, &QCheckBox::stateChanged, this, &AzAssetBrowserWindow::LockToDefaultView); } m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); connect( - m_ui->m_searchWidget->GetFilter().data(), &AB::AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), - &AB::AssetBrowserFilterModel::filterUpdatedSlot); + m_ui->m_searchWidget->GetFilter().data(), &AzAssetBrowser::AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), + &AzAssetBrowser::AssetBrowserFilterModel::filterUpdatedSlot); connect( - m_filterModel.data(), &AB::AssetBrowserFilterModel::filterChanged, this, + m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this, [this]() { const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty(); @@ -126,17 +126,17 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) }); connect( - m_ui->m_assetBrowserTreeViewWidget, &AB::AssetBrowserTreeView::selectionChangedSignal, this, + m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::selectionChangedSignal, this, &AzAssetBrowserWindow::SelectionChangedSlot); connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); connect( - m_ui->m_assetBrowserTreeViewWidget, &AB::AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, - &AB::SearchWidget::ClearStringFilter); + m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, + &AzAssetBrowser::SearchWidget::ClearStringFilter); connect( - m_ui->m_assetBrowserTreeViewWidget, &AB::AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, - &AB::SearchWidget::ClearTypeFilter); + m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, + &AzAssetBrowser::SearchWidget::ClearTypeFilter); m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); } @@ -232,21 +232,21 @@ void AzAssetBrowserWindow::SelectionChangedSlot(const QItemSelection& /*selected // just becuase on some OS clicking once is activation. void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& element) { - namespace AB = AzToolsFramework::AssetBrowser; + namespace AzAssetBrowser = AzToolsFramework::AssetBrowser; // assumption: Double clicking an item selects it before telling us we double clicked it. const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); - for (const AB::AssetBrowserEntry* entry : selectedAssets) + for (const AzAssetBrowser::AssetBrowserEntry* entry : selectedAssets) { AZ::Data::AssetId assetIdToOpen; AZStd::string fullFilePath; - if (const AB::ProductAssetBrowserEntry* productEntry = azrtti_cast(entry)) + if (const AzAssetBrowser::ProductAssetBrowserEntry* productEntry = azrtti_cast(entry)) { assetIdToOpen = productEntry->GetAssetId(); fullFilePath = entry->GetFullPath(); } - else if (const AB::SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry)) + else if (const AzAssetBrowser::SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry)) { // manufacture an empty AssetID with the source's UUID assetIdToOpen = AZ::Data::AssetId(sourceEntry->GetSourceUuid(), 0); @@ -256,8 +256,8 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& bool handledBySomeone = false; if (assetIdToOpen.IsValid()) { - AB::AssetBrowserInteractionNotificationBus::Broadcast( - &AB::AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); + AzAssetBrowser::AssetBrowserInteractionNotificationBus::Broadcast( + &AzAssetBrowser::AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); } if (!handledBySomeone && !fullFilePath.empty()) @@ -269,20 +269,19 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& void AzAssetBrowserWindow::DoubleClickedItemTableModel([[maybe_unused]] const QModelIndex& element) { - namespace AB = AzToolsFramework::AssetBrowser; - // assumption: Double clicking an item selects it before telling us we double clicked it. + namespace AzAssetBrowser = AzToolsFramework::AssetBrowser; const auto& selectedAssets = m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets(); - for (const AB::AssetBrowserEntry* entry : selectedAssets) + for (const AzAssetBrowser::AssetBrowserEntry* entry : selectedAssets) { AZ::Data::AssetId assetIdToOpen; AZStd::string fullFilePath; - if (const AB::ProductAssetBrowserEntry* productEntry = azrtti_cast(entry)) + if (const AzAssetBrowser::ProductAssetBrowserEntry* productEntry = azrtti_cast(entry)) { assetIdToOpen = productEntry->GetAssetId(); fullFilePath = entry->GetFullPath(); } - else if (const AB::SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry)) + else if (const AzAssetBrowser::SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry)) { // manufacture an empty AssetID with the source's UUID assetIdToOpen = AZ::Data::AssetId(sourceEntry->GetSourceUuid(), 0); @@ -292,8 +291,8 @@ void AzAssetBrowserWindow::DoubleClickedItemTableModel([[maybe_unused]] const QM bool handledBySomeone = false; if (assetIdToOpen.IsValid()) { - AB::AssetBrowserInteractionNotificationBus::Broadcast( - &AB::AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); + AzAssetBrowser::AssetBrowserInteractionNotificationBus::Broadcast( + &AzAssetBrowser::AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); } if (!handledBySomeone && !fullFilePath.empty()) From ac42a9a748fdb889136e5da378273711caa51f4a Mon Sep 17 00:00:00 2001 From: jonawals Date: Fri, 4 Jun 2021 19:33:26 +0100 Subject: [PATCH 092/233] Changes for read-only runs and new test suites --- .../Source/TestImpactCommandLineOptions.cpp | 55 +++----- .../Source/TestImpactCommandLineOptions.h | 9 +- .../Code/Source/TestImpactConsoleMain.cpp | 83 ++++++++---- .../TestImpactRuntimeConfigurationFactory.cpp | 21 ++- .../TestImpactConfiguration.h | 3 +- .../TestImpactFramework/TestImpactRuntime.h | 10 +- .../TestImpactTestSequence.h | 35 +++++ .../TestImpactTestTargetMetaMapFactory.cpp | 49 ++++--- .../TestImpactTestTargetMetaMapFactory.h | 6 +- .../TestImpactDynamicDependencyMap.cpp | 6 +- .../Runtime/Code/Source/TestImpactRuntime.cpp | 125 ++++++++++-------- .../Code/Source/TestImpactRuntimeUtils.cpp | 7 +- .../Code/Source/TestImpactRuntimeUtils.h | 1 + .../testimpactframework_runtime_files.cmake | 92 ++++++++----- ...timpactframework_runtime_tests_files.cmake | 38 ------ cmake/LYTestWrappers.cmake | 50 +++++-- .../ConsoleFrontendConfig.in | 23 +++- .../LYTestImpactFramework.cmake | 115 ++++++++++------ 18 files changed, 448 insertions(+), 280 deletions(-) delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp index a6eb2e886e..5ff070601a 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp @@ -36,21 +36,21 @@ namespace TestImpact MaxConcurrency, TestTargetTimeout, GlobalTimeout, - SuitesFilter, + SuiteFilter, SafeMode, // Values None, Seed, Regular, ImpactAnalysis, + ImpactAnalysisNoWrite, ImpactAnalysisOrSeed, Locality, Abort, Continue, Ignore, StdOut, - File, - AllSuites + File }; constexpr const char* OptionKeys[] = @@ -70,21 +70,21 @@ namespace TestImpact "maxconcurrency", "ttimeout", "gtimeout", - "suites", + "suite", "safemode", // Values "none", "seed", "regular", "tia", + "tianowrite", "tiaorseed", "locality", "abort", "continue", "ignore", "stdout", - "file", - "*" + "file" }; RepoPath ParseConfigurationFile(const AZ::CommandLine& cmd) @@ -110,6 +110,7 @@ namespace TestImpact {OptionKeys[Seed], TestSequenceType::Seed}, {OptionKeys[Regular], TestSequenceType::Regular}, {OptionKeys[ImpactAnalysis], TestSequenceType::ImpactAnalysis}, + {OptionKeys[ImpactAnalysisNoWrite], TestSequenceType::ImpactAnalysisNoWrite}, {OptionKeys[ImpactAnalysisOrSeed], TestSequenceType::ImpactAnalysisOrSeed} }; @@ -250,32 +251,16 @@ namespace TestImpact return ParseOnOffOption(OptionKeys[SafeMode], states, cmd).value_or(false); } - AZStd::unordered_set ParseSuitesFilter(const AZ::CommandLine& cmd) + SuiteType ParseSuiteFilter(const AZ::CommandLine& cmd) { - AZStd::unordered_set suitesFilter; - if (const auto numSwitchValues = cmd.GetNumSwitchValues(OptionKeys[SuitesFilter]); - numSwitchValues) - { - for (auto i = 0; i < numSwitchValues; i++) - { - const auto value = cmd.GetSwitchValue(OptionKeys[SuitesFilter], i); - AZ_TestImpact_Eval(!value.empty(), CommandLineOptionsException, "Suites option value is empty"); - if (value == OptionKeys[AllSuites]) - { - AZ_TestImpact_Eval( - suitesFilter.empty(), CommandLineOptionsException, "The * suite cannot be used with other suites"); - } - - suitesFilter.insert(value); - } - } - - if (suitesFilter.find(OptionKeys[AllSuites]) != suitesFilter.end()) + const AZStd::vector> states = { - return {}; - } + {GetSuiteTypeName(SuiteType::Main), SuiteType::Main}, + {GetSuiteTypeName(SuiteType::Periodic), SuiteType::Periodic}, + {GetSuiteTypeName(SuiteType::Sandbox), SuiteType::Sandbox} + }; - return suitesFilter; + return ParseMultiStateOption(OptionKeys[SuiteFilter], states, cmd).value_or(SuiteType::Main); } } @@ -299,7 +284,7 @@ namespace TestImpact m_testTargetTimeout = ParseTestTargetTimeout(cmd); m_globalTimeout = ParseGlobalTimeout(cmd); m_safeMode = ParseSafeMode(cmd); - m_suitesFilter = ParseSuitesFilter(cmd); + m_suiteFilter = ParseSuiteFilter(cmd); } bool CommandLineOptions::HasChangeListFile() const @@ -382,9 +367,9 @@ namespace TestImpact return m_globalTimeout; } - const AZStd::unordered_set& CommandLineOptions::GetSuitesFilter() const + SuiteType CommandLineOptions::GetSuiteFilter() const { - return m_suitesFilter; + return m_suiteFilter; } AZStd::string CommandLineOptions::GetCommandLineUsageString() @@ -452,11 +437,7 @@ namespace TestImpact " -maxconcurrency= The maximum number of concurrent test targets/shards to be in flight at \n" " any given moment.\n" " -ochangelist= Outputs the change list used for test selection.\n" - " -suites= The test suites to select from for this test sequence (multiple values are \n" - " allowed). The suite all has special significance and will allow tests from \n" - " any suite to be selected, however this particular suite is mutually exclusive\n" - " with other suite. Note: this option is only applicable to the regular sequence\n" - " and, if safe mode is enables, the tia and tiaorseed sequences."; + " -suite= The test suite to select from for this test sequence."; return help; } diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h index 73961b5fcc..99b1b98dba 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h @@ -18,7 +18,6 @@ #include #include #include -#include namespace TestImpact { @@ -29,6 +28,8 @@ namespace TestImpact Seed, //!< Removes any prior coverage data and runs all test targets with instrumentation to reseed the data from scratch. Regular, //!< Runs all of the test targets without any instrumentation to generate coverage data (any prior coverage data is left intact). ImpactAnalysis, //!< Uses any prior coverage data to run the instrumented subset of selected tests (if no prior coverage data a regular run is performed instead). + ImpactAnalysisNoWrite, //!< Uses any prior coverage data to run the uninstrumented subset of selected tests (if no prior coverage data a regular run is performed instead). + //!< The coverage data is not updated with the subset of selected tests. ImpactAnalysisOrSeed //!< Uses any prior coverage data to run the instrumented subset of selected tests (if no prior coverage data a seed run is performed instead). }; @@ -87,8 +88,8 @@ namespace TestImpact //! Returns the global test sequence timeout to use (if any). const AZStd::optional& GetGlobalTimeout() const; - //! Returns the filter for test suites that will be allowed to be run. - const AZStd::unordered_set& GetSuitesFilter() const; + //! Returns the filter for test suite that will be allowed to be run. + SuiteType GetSuiteFilter() const; private: RepoPath m_configurationFile; @@ -105,7 +106,7 @@ namespace TestImpact AZStd::optional m_maxConcurrency; AZStd::optional m_testTargetTimeout; AZStd::optional m_globalTimeout; - AZStd::unordered_set m_suitesFilter; + SuiteType m_suiteFilter; bool m_safeMode = false; }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp index 84e16c2332..fab6509244 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp @@ -86,6 +86,8 @@ namespace TestImpact Runtime& runtime, const AZStd::optional& changeList) { + // Even though it is possible for a regular run to be selected (see below) which does not actually require a change list, + // consider any impact analysis sequence type without a change list to be an error AZ_TestImpact_Eval( changeList.has_value(), CommandLineOptionsException, @@ -94,39 +96,72 @@ namespace TestImpact TestSequenceResult result = TestSequenceResult::Failure; if (options.HasSafeMode()) { - auto [selectedResult, discardedResult] = runtime.SafeImpactAnalysisTestSequence( - changeList.value(), - options.GetSuitesFilter(), - options.GetTestPrioritizationPolicy(), - options.GetTestTargetTimeout(), - options.GetGlobalTimeout(), - AZStd::ref(sequenceEventHandler), - AZStd::ref(sequenceEventHandler), - AZStd::ref(sequenceEventHandler)); - - // Handling the possible timeout and failure permutations of the selected and discarded test results is splitting hairs - // so apply the following, admittedly arbitrary, rules to determine what the composite test sequence result should be - if (selectedResult == TestSequenceResult::Success && discardedResult == TestSequenceResult::Success) + if (options.GetTestSequenceType() == TestSequenceType::ImpactAnalysis) { - // Trivial case: both sequences succeeded - result = TestSequenceResult::Success; + auto [selectedResult, discardedResult] = runtime.SafeImpactAnalysisTestSequence( + changeList.value(), + options.GetTestPrioritizationPolicy(), + options.GetTestTargetTimeout(), + options.GetGlobalTimeout(), + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler)); + + // Handling the possible timeout and failure permutations of the selected and discarded test results is splitting hairs + // so apply the following, admittedly arbitrary, rules to determine what the composite test sequence result should be + if (selectedResult == TestSequenceResult::Success && discardedResult == TestSequenceResult::Success) + { + // Trivial case: both sequences succeeded + result = TestSequenceResult::Success; + } + else if (selectedResult == TestSequenceResult::Failure || discardedResult == TestSequenceResult::Failure) + { + // One sequence failed whilst the other sequence either succeeded or timed out + result = TestSequenceResult::Failure; + } + else + { + // One sequence timed out whilst the other sequence succeeded or both sequences timed out + result = TestSequenceResult::Timeout; + } } - else if (selectedResult == TestSequenceResult::Failure || discardedResult == TestSequenceResult::Failure) + else if (options.GetTestSequenceType() == TestSequenceType::ImpactAnalysisNoWrite) { - // One sequence failed whilst the other sequence either succeeded or timed out - result = TestSequenceResult::Failure; + // A no-write impact analysis sequence with safe mode enabled is functionally identical to a regular sequence type + // due to a) the selected tests being run without instrumentation and b) the discarded tests also being run without + // instrumentation + result = runtime.RegularTestSequence( + options.GetTestTargetTimeout(), + options.GetGlobalTimeout(), + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler), + AZStd::ref(sequenceEventHandler)); } else { - // One sequence timed out whilst the other sequence succeeded or both sequences timed out - result = TestSequenceResult::Timeout; + throw(Exception("Unexpected sequence type")); } } else { + Policy::DynamicDependencyMap dynamicDependencyMapPolicy; + if (options.GetTestSequenceType() == TestSequenceType::ImpactAnalysis) + { + dynamicDependencyMapPolicy = Policy::DynamicDependencyMap::Update; + } + else if (options.GetTestSequenceType() == TestSequenceType::ImpactAnalysisNoWrite) + { + dynamicDependencyMapPolicy = Policy::DynamicDependencyMap::Discard; + } + else + { + throw(Exception("Unexpected sequence type")); + } + result = runtime.ImpactAnalysisTestSequence( changeList.value(), options.GetTestPrioritizationPolicy(), + dynamicDependencyMapPolicy, options.GetTestTargetTimeout(), options.GetGlobalTimeout(), AZStd::ref(sequenceEventHandler), @@ -164,9 +199,11 @@ namespace TestImpact // As of now, there are no other non-test operations other than printing a change list so getting this far is considered an error AZ_TestImpact_Eval(options.GetTestSequenceType() != TestSequenceType::None, CommandLineOptionsException, "No action specified"); - std::cout << "Constructing in-memory model of source tree and test coverage, this may take a moment...\n"; + std::cout << "Constructing in-memory model of source tree and test coverage for test suite "; + std::cout << GetSuiteTypeName(options.GetSuiteFilter()).c_str() << ", this may take a moment...\n"; Runtime runtime( RuntimeConfigurationFactory(ReadFileContents(options.GetConfigurationFile())), + options.GetSuiteFilter(), options.GetExecutionFailurePolicy(), options.GetExecutionFailureDraftingPolicy(), options.GetTestFailurePolicy(), @@ -184,14 +221,13 @@ namespace TestImpact std::cout << "Test impact analysis data for this repository was not found, seed or regular sequence fallbacks will be used.\n"; } - TestSequenceEventHandler sequenceEventHandler(&options.GetSuitesFilter()); + TestSequenceEventHandler sequenceEventHandler(options.GetSuiteFilter()); switch (const auto type = options.GetTestSequenceType()) { case TestSequenceType::Regular: { const auto result = runtime.RegularTestSequence( - options.GetSuitesFilter(), options.GetTestTargetTimeout(), options.GetGlobalTimeout(), AZStd::ref(sequenceEventHandler), @@ -211,6 +247,7 @@ namespace TestImpact return GetReturnCodeForTestSequenceResult(result); } + case TestSequenceType::ImpactAnalysisNoWrite: case TestSequenceType::ImpactAnalysis: { return WrappedImpactAnalysisTestSequence(sequenceEventHandler, options, runtime, changeList); diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp index 9db99d3c2e..6ef1ce4172 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp @@ -30,7 +30,7 @@ namespace TestImpact "relative_paths", "artifact_dir", "enumeration_cache_dir", - "test_impact_data_file", + "test_impact_data_files", "temp", "active", "target_sources", @@ -75,7 +75,7 @@ namespace TestImpact RelativePaths, ArtifactDir, EnumerationCacheDir, - TestImpactDataFile, + TestImpactDataFiles, TempWorkspace, ActiveWorkspace, TargetSources, @@ -144,6 +144,19 @@ namespace TestImpact return tempWorkspaceConfig; } + AZStd::array ParseTestImpactAnalysisDataFiles(const RepoPath& root, const rapidjson::Value& sparTIAFile) + { + AZStd::array sparTIAFiles; + sparTIAFiles[static_cast(SuiteType::Main)] = + GetAbsPathFromRelPath(root, sparTIAFile[GetSuiteTypeName(SuiteType::Main).c_str()].GetString()); + sparTIAFiles[static_cast(SuiteType::Periodic)] = + GetAbsPathFromRelPath(root, sparTIAFile[GetSuiteTypeName(SuiteType::Periodic).c_str()].GetString()); + sparTIAFiles[static_cast(SuiteType::Sandbox)] = + GetAbsPathFromRelPath(root, sparTIAFile[GetSuiteTypeName(SuiteType::Sandbox).c_str()].GetString()); + + return sparTIAFiles; + } + WorkspaceConfig::Active ParseActiveWorkspaceConfig(const rapidjson::Value& activeWorkspace) { WorkspaceConfig::Active activeWorkspaceConfig; @@ -151,8 +164,8 @@ namespace TestImpact activeWorkspaceConfig.m_root = activeWorkspace[Config::Keys[Config::Root]].GetString(); activeWorkspaceConfig.m_enumerationCacheDirectory = GetAbsPathFromRelPath(activeWorkspaceConfig.m_root, relativePaths[Config::Keys[Config::EnumerationCacheDir]].GetString()); - activeWorkspaceConfig.m_sparTIAFile - = GetAbsPathFromRelPath(activeWorkspaceConfig.m_root, relativePaths[Config::Keys[Config::TestImpactDataFile]].GetString()); + activeWorkspaceConfig.m_sparTIAFiles = + ParseTestImpactAnalysisDataFiles(activeWorkspaceConfig.m_root, relativePaths[Config::Keys[Config::TestImpactDataFiles]]); return activeWorkspaceConfig; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h index 91d748edfe..ee73ac572d 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactConfiguration.h @@ -16,6 +16,7 @@ #include #include +#include #include namespace TestImpact @@ -46,8 +47,8 @@ namespace TestImpact struct Active { RepoPath m_root; //!< Path to the persistent workspace tracked by the repository. - RepoPath m_sparTIAFile; //!< Path to the test impact analysis data. RepoPath m_enumerationCacheDirectory; //!< Path to the test enumerations cache. + AZStd::array m_sparTIAFiles; //!< Paths to the test impact analysis data files for each test suite. }; Temp m_temp; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h index 266bd5ef7a..ae1f78c5c7 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h @@ -92,6 +92,7 @@ namespace TestImpact public: //! Constructs a runtime with the specified configuration and policies. //! @param config The configuration used for this runtime instance. + //! @param suiteFilter The test suite for which the coverage data and test selection will draw from. //! @param executionFailurePolicy Determines how to handle test targets that fail to execute. //! @param executionFailureDraftingPolicy Determines how test targets that previously failed to execute are drafted into subsequent test sequences. //! @param testFailurePolicy Determines how to handle test targets that report test failures. @@ -99,6 +100,7 @@ namespace TestImpact //! @param testShardingPolicy Determines how to handle test targets that have opted in to test sharding. Runtime( RuntimeConfig&& config, + SuiteType suiteFilter, Policy::ExecutionFailure executionFailurePolicy, Policy::ExecutionFailureDrafting executionFailureDraftingPolicy, Policy::TestFailure testFailurePolicy, @@ -110,7 +112,6 @@ namespace TestImpact ~Runtime(); //! Runs a test sequence where all tests with a matching suite in the suite filter and also not on the excluded list are selected. - //! @param suitesFilter The test suites that will be included in the test selection. //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). //! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty). //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. @@ -118,7 +119,6 @@ namespace TestImpact //! @param testRunCompleteCallback The client function to be called after an individual test run has completed. //! @returns TestSequenceResult RegularTestSequence( - const AZStd::unordered_set suitesFilter, AZStd::optional testTargetTimeout, AZStd::optional globalTimeout, AZStd::optional testSequenceStartCallback, @@ -128,6 +128,7 @@ namespace TestImpact //! Runs a test sequence where tests are selected according to test impact analysis so long as they are not on the excluded list. //! @param changeList The change list used to determine the tests to select. //! @param testPrioritizationPolicy Determines how selected tests will be prioritized. + //! @param dynamicDependencyMapPolicy The policy to determine how the coverage data of produced by test sequences is used to update the dynamic dependency map. //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). //! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty). //! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests. @@ -137,6 +138,7 @@ namespace TestImpact TestSequenceResult ImpactAnalysisTestSequence( const ChangeList& changeList, Policy::TestPrioritization testPrioritizationPolicy, + Policy::DynamicDependencyMap dynamicDependencyMapPolicy, AZStd::optional testTargetTimeout, AZStd::optional globalTimeout, AZStd::optional testSequenceStartCallback, @@ -145,7 +147,6 @@ namespace TestImpact //! Runs a test sequence as per the ImpactAnalysisTestSequence where the tests not selected are also run (albeit without instrumentation). //! @param changeList The change list used to determine the tests to select. - //! @param suitesFilter The test suites that will be included in the test selection. //! @param testPrioritizationPolicy Determines how selected tests will be prioritized. //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). //! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty). @@ -155,7 +156,6 @@ namespace TestImpact //! @returns AZStd::pair SafeImpactAnalysisTestSequence( const ChangeList& changeList, - const AZStd::unordered_set suitesFilter, Policy::TestPrioritization testPrioritizationPolicy, AZStd::optional testTargetTimeout, AZStd::optional globalTimeout, @@ -207,6 +207,8 @@ namespace TestImpact void UpdateAndSerializeDynamicDependencyMap(const SourceCoveringTestsList& sourceCoverageTestsList); RuntimeConfig m_config; + SuiteType m_suiteFilter; + RepoPath m_sparTIAFile; Policy::ExecutionFailure m_executionFailurePolicy; Policy::ExecutionFailureDrafting m_executionFailureDraftingPolicy; Policy::TestFailure m_testFailurePolicy; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h index 594c997c45..4e8908cf3f 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h @@ -12,6 +12,10 @@ #pragma once +#include + +#include + namespace TestImpact { namespace Policy @@ -55,6 +59,13 @@ namespace TestImpact Continue //!< Continue the test sequence and report the test failures after the run. }; + //! Policy for updating the dynamic dependency map with the coverage data of produced by test sequences. + enum class DynamicDependencyMap + { + Discard, //!< Discard the coverage data produced by test sequences. + Update //!< Update the dynamic dependency map with the coverage data produced by test sequences. + }; + //! Policy for sharding test targets that have been marked for test sharding. enum class TestSharding { @@ -82,6 +93,30 @@ namespace TestImpact TestInterleaved //!< Tests are interlaced across shards agnostic of fixtures (fastest but prone to inter-test dependency problems). }; + //! Test suite types to select from. + enum class SuiteType : AZ::u8 + { + Main = 0, + Periodic, + Sandbox + }; + + //! User-friendly names for the test suite types. + inline AZStd::string GetSuiteTypeName(SuiteType suiteType) + { + switch (suiteType) + { + case SuiteType::Main: + return "main"; + case SuiteType::Periodic: + return "periodic"; + case SuiteType::Sandbox: + return "sandbox"; + default: + throw(RuntimeException("Unexpected suite type")); + } + } + //! Result of a test sequence that was run. enum class TestSequenceResult { diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp index 8bbb5200bb..4b247086ed 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp @@ -19,7 +19,7 @@ namespace TestImpact { - TestTargetMetaMap TestTargetMetaMapFactory(const AZStd::string& masterTestListData) + TestTargetMetaMap TestTargetMetaMapFactory(const AZStd::string& masterTestListData, SuiteType suiteType) { // Keys for pertinent JSON node and attribute names constexpr const char* Keys[] = @@ -27,6 +27,7 @@ namespace TestImpact "google", "test", "tests", + "suites", "suite", "launch_method", "test_runner", @@ -41,6 +42,7 @@ namespace TestImpact GoogleKey, TestKey, TestsKey, + TestSuitesKey, SuiteKey, LaunchMethodKey, TestRunnerKey, @@ -64,26 +66,35 @@ namespace TestImpact for (const auto& test : tests) { TestTargetMeta testMeta; - testMeta.m_suite = test[Keys[SuiteKey]].GetString(); - testMeta.m_customArgs = test[Keys[CommandKey]].GetString(); - testMeta.m_timeout = AZStd::chrono::seconds{ test[Keys[TimeoutKey]].GetUint() }; - - if (const auto buildTypeString = test[Keys[LaunchMethodKey]].GetString(); strcmp(buildTypeString, Keys[TestRunnerKey]) == 0) - { - testMeta.m_launchMethod = LaunchMethod::TestRunner; - } - else if (strcmp(buildTypeString, Keys[StandAloneKey]) == 0) + const auto testSuites = test[Keys[TestSuitesKey]].GetArray(); + for (const auto& suite : testSuites) { - testMeta.m_launchMethod = LaunchMethod::StandAlone; - } - else - { - throw(ArtifactException("Unexpected test build type")); - } + // Check to see if this test target has the suite we're looking for + if (const auto suiteName = suite[Keys[SuiteKey]].GetString(); + strcmp(GetSuiteTypeName(suiteType).c_str(), suiteName) == 0) + { + testMeta.m_suite = suiteName; + testMeta.m_customArgs = suite[Keys[CommandKey]].GetString(); + testMeta.m_timeout = AZStd::chrono::seconds{ suite[Keys[TimeoutKey]].GetUint() }; + if (const auto buildTypeString = test[Keys[LaunchMethodKey]].GetString(); strcmp(buildTypeString, Keys[TestRunnerKey]) == 0) + { + testMeta.m_launchMethod = LaunchMethod::TestRunner; + } + else if (strcmp(buildTypeString, Keys[StandAloneKey]) == 0) + { + testMeta.m_launchMethod = LaunchMethod::StandAlone; + } + else + { + throw(ArtifactException("Unexpected test build type")); + } - AZStd::string name = test[Keys[NameKey]].GetString(); - AZ_TestImpact_Eval(!name.empty(), ArtifactException, "Test name field cannot be empty"); - testMetas.emplace(AZStd::move(name), AZStd::move(testMeta)); + AZStd::string name = test[Keys[NameKey]].GetString(); + AZ_TestImpact_Eval(!name.empty(), ArtifactException, "Test name field cannot be empty"); + testMetas.emplace(AZStd::move(name), AZStd::move(testMeta)); + break; + } + } } // If there's no tests in the repo then something is seriously wrong diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.h index 039a9e89e2..b08babf528 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.h @@ -12,14 +12,16 @@ #pragma once +#include #include #include namespace TestImpact { - //! Constructs a list of test target meta-data artifacts from the specified master test list data. + //! Constructs a list of test target meta-data artifacts of the specified suite type from the specified master test list data. //! @param masterTestListData The raw master test list data in JSON format. + //! @param suiteType The suite type to select the target meta-data artifacts from. //! @return The constructed list of test target meta-data artifacts. - TestTargetMetaMap TestTargetMetaMapFactory(const AZStd::string& masterTestListData); + TestTargetMetaMap TestTargetMetaMapFactory(const AZStd::string& masterTestListData, SuiteType suiteType); } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp index 826fa0a87d..ece5a7c13b 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp @@ -371,11 +371,11 @@ namespace TestImpact { if (sourceDependency->GetNumCoveringTestTargets()) { - AZ_Warning( - "File Update", false, AZStd::string::format("Source file %s is potentially an orphan (used by build targets " + AZ_Printf( + "File Update", AZStd::string::format("Source file '%s' is potentially an orphan (used by build targets " "without explicitly being added to the build system, e.g. an include directive pulling in a header from the " "repository). Running the covering tests for this file with instrumentation will confirm whether or nor this " - "is the case", updatedFile.c_str()).c_str()); + "is the case.\n", updatedFile.c_str()).c_str()); updateDependencies.emplace_back(AZStd::move(*sourceDependency)); coverageToDelete.push_back(updatedFile); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp index 83808f4ed2..2331c76880 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp @@ -72,6 +72,7 @@ namespace TestImpact Runtime::Runtime( RuntimeConfig&& config, + SuiteType suiteFilter, Policy::ExecutionFailure executionFailurePolicy, Policy::ExecutionFailureDrafting executionFailureDraftingPolicy, Policy::TestFailure testFailurePolicy, @@ -80,6 +81,7 @@ namespace TestImpact Policy::TargetOutputCapture targetOutputCapture, AZStd::optional maxConcurrency) : m_config(AZStd::move(config)) + , m_suiteFilter(suiteFilter) , m_executionFailurePolicy(executionFailurePolicy) , m_executionFailureDraftingPolicy(executionFailureDraftingPolicy) , m_testFailurePolicy(testFailurePolicy) @@ -89,7 +91,7 @@ namespace TestImpact , m_maxConcurrency(maxConcurrency.value_or(AZStd::thread::hardware_concurrency())) { // Construct the dynamic dependency map from the build target descriptors - m_dynamicDependencyMap = ConstructDynamicDependencyMap(m_config.m_buildTargetDescriptor, m_config.m_testTargetMeta); + m_dynamicDependencyMap = ConstructDynamicDependencyMap(suiteFilter, m_config.m_buildTargetDescriptor, m_config.m_testTargetMeta); // Construct the test selector and prioritizer from the dependency graph data (NOTE: currently not implemented) m_testSelectorAndPrioritizer = AZStd::make_unique(m_dynamicDependencyMap.get(), DependencyGraphDataMap{}); @@ -110,7 +112,8 @@ namespace TestImpact try { // Populate the dynamic dependency map with the existing source coverage data (if any) - const auto tiaDataRaw = ReadFileContents(m_config.m_workspace.m_active.m_sparTIAFile); + m_sparTIAFile = m_config.m_workspace.m_active.m_sparTIAFiles[static_cast(m_suiteFilter)].String(); + const auto tiaDataRaw = ReadFileContents(m_sparTIAFile); const auto tiaData = DeserializeSourceCoveringTestsList(tiaDataRaw); if (tiaData.GetNumSources()) { @@ -118,13 +121,17 @@ namespace TestImpact m_hasImpactAnalysisData = true; // Enumerate new test targets - m_testEngine->UpdateEnumerationCache( - m_dynamicDependencyMap->GetNotCoveringTests(), - Policy::ExecutionFailure::Ignore, - Policy::TestFailure::Continue, - AZStd::nullopt, - AZStd::nullopt, - AZStd::nullopt); + const auto testTargetsWithNoEnumeration = m_dynamicDependencyMap->GetNotCoveringTests(); + if (!testTargetsWithNoEnumeration.empty()) + { + m_testEngine->UpdateEnumerationCache( + testTargetsWithNoEnumeration, + Policy::ExecutionFailure::Ignore, + Policy::TestFailure::Continue, + AZStd::nullopt, + AZStd::nullopt, + AZStd::nullopt); + } } } catch (const DependencyException& e) @@ -136,8 +143,10 @@ namespace TestImpact } catch ([[maybe_unused]]const Exception& e) { - AZ_Printf("No test impact analysis data found at %s", m_config.m_workspace.m_active.m_sparTIAFile.c_str()); - } + AZ_Printf("TestImpactRuntime", + AZStd::string::format( + "No test impact analysis data found for suite '%s' at %s", GetSuiteTypeName(m_suiteFilter).c_str(), m_sparTIAFile.c_str()).c_str()); + } } Runtime::~Runtime() = default; @@ -168,13 +177,16 @@ namespace TestImpact addMutatedTestTargetsToEnumerationList(changeDependencyList.GetDeleteSourceDependencies()); // Enumerate the mutated test targets to ensure their enumeration caches are up to date - m_testEngine->UpdateEnumerationCache( - testTargets, - Policy::ExecutionFailure::Ignore, - Policy::TestFailure::Continue, - AZStd::nullopt, - AZStd::nullopt, - AZStd::nullopt); + if (!testTargets.empty()) + { + m_testEngine->UpdateEnumerationCache( + testTargets, + Policy::ExecutionFailure::Ignore, + Policy::TestFailure::Continue, + AZStd::nullopt, + AZStd::nullopt, + AZStd::nullopt); + } } AZStd::pair, AZStd::vector> Runtime::SelectCoveringTestTargetsAndUpdateEnumerationCache( @@ -233,7 +245,7 @@ namespace TestImpact void Runtime::ClearDynamicDependencyMapAndRemoveExistingFile() { - DeleteFile(m_config.m_workspace.m_active.m_sparTIAFile); + DeleteFile(m_sparTIAFile); m_dynamicDependencyMap->ClearAllSourceCoverage(); } @@ -247,12 +259,11 @@ namespace TestImpact m_dynamicDependencyMap->ReplaceSourceCoverage(sourceCoverageTestsList); const auto sparTIA = m_dynamicDependencyMap->ExportSourceCoverage(); const auto sparTIAData = SerializeSourceCoveringTestsList(sparTIA); - WriteFileContents(sparTIAData, m_config.m_workspace.m_active.m_sparTIAFile); + WriteFileContents(sparTIAData, m_sparTIAFile); m_hasImpactAnalysisData = true; } TestSequenceResult Runtime::RegularTestSequence( - const AZStd::unordered_set suitesFilter, AZStd::optional testTargetTimeout, AZStd::optional globalTimeout, AZStd::optional testSequenceStartCallback, @@ -268,21 +279,7 @@ namespace TestImpact { if (!m_testTargetExcludeList.contains(&testTarget)) { - if (suitesFilter.empty()) - { - // Suite filter is empty, all tests that are not on the excluded list are included - includedTestTargets.push_back(&testTarget); - } - else if(suitesFilter.contains(testTarget.GetSuite())) - { - // Test target belonging to a suite in the suite filter are included, provided that are not on the exclude list - includedTestTargets.push_back(&testTarget); - } - else - { - // Test target not belonging to a suite in the suite filter are excluded - excludedTestTargets.push_back(&testTarget); - } + includedTestTargets.push_back(&testTarget); } else { @@ -318,6 +315,7 @@ namespace TestImpact TestSequenceResult Runtime::ImpactAnalysisTestSequence( const ChangeList& changeList, Policy::TestPrioritization testPrioritizationPolicy, + Policy::DynamicDependencyMap dynamicDependencyMapPolicy, AZStd::optional testTargetTimeout, AZStd::optional globalTimeout, AZStd::optional testSequenceStartCallback, @@ -338,30 +336,51 @@ namespace TestImpact ExtractTestTargetNames(draftedTestTargets)); } - const auto [result, testJobs] = m_testEngine->InstrumentedRun( - includedSelectedTestTargets, - m_testShardingPolicy, - m_executionFailurePolicy, - Policy::IntegrityFailure::Continue, - m_testFailurePolicy, - m_targetOutputCapture, - testTargetTimeout, - globalTimeout, - TestRunCompleteCallbackHandler(testCompleteCallback)); - - UpdateAndSerializeDynamicDependencyMap(CreateSourceCoveringTestFromTestCoverages(testJobs, m_config.m_repo.m_root)); - - if (testSequenceEndCallback.has_value()) + if (dynamicDependencyMapPolicy == Policy::DynamicDependencyMap::Update) { - (*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed()); + const auto [result, testJobs] = m_testEngine->InstrumentedRun( + includedSelectedTestTargets, + m_testShardingPolicy, + m_executionFailurePolicy, + Policy::IntegrityFailure::Continue, + m_testFailurePolicy, + m_targetOutputCapture, + testTargetTimeout, + globalTimeout, + TestRunCompleteCallbackHandler(testCompleteCallback)); + + UpdateAndSerializeDynamicDependencyMap(CreateSourceCoveringTestFromTestCoverages(testJobs, m_config.m_repo.m_root)); + + if (testSequenceEndCallback.has_value()) + { + (*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed()); + } + + return result; } + else + { + const auto [result, testJobs] = m_testEngine->RegularRun( + includedSelectedTestTargets, + m_testShardingPolicy, + m_executionFailurePolicy, + m_testFailurePolicy, + m_targetOutputCapture, + testTargetTimeout, + globalTimeout, + TestRunCompleteCallbackHandler(testCompleteCallback)); + + if (testSequenceEndCallback.has_value()) + { + (*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed()); + } - return result; + return result; + } } AZStd::pair Runtime::SafeImpactAnalysisTestSequence( const ChangeList& changeList, - const AZStd::unordered_set suitesFilter, Policy::TestPrioritization testPrioritizationPolicy, AZStd::optional testTargetTimeout, AZStd::optional globalTimeout, diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp index 87a402bd6a..47c0e83233 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp @@ -22,10 +22,10 @@ namespace TestImpact { - TestTargetMetaMap ReadTestTargetMetaMapFile(const RepoPath& testTargetMetaConfigFile) + TestTargetMetaMap ReadTestTargetMetaMapFile(SuiteType suiteFilter, const RepoPath& testTargetMetaConfigFile) { const auto masterTestListData = ReadFileContents(testTargetMetaConfigFile); - return TestTargetMetaMapFactory(masterTestListData); + return TestTargetMetaMapFactory(masterTestListData, suiteFilter); } AZStd::vector ReadBuildTargetDescriptorFiles(const BuildTargetDescriptorConfig& buildTargetDescriptorConfig) @@ -46,10 +46,11 @@ namespace TestImpact } AZStd::unique_ptr ConstructDynamicDependencyMap( + SuiteType suiteFilter, const BuildTargetDescriptorConfig& buildTargetDescriptorConfig, const TestTargetMetaConfig& testTargetMetaConfig) { - auto testTargetmetaMap = ReadTestTargetMetaMapFile(testTargetMetaConfig.m_metaFile); + auto testTargetmetaMap = ReadTestTargetMetaMapFile(suiteFilter, testTargetMetaConfig.m_metaFile); auto buildTargetDescriptors = ReadBuildTargetDescriptorFiles(buildTargetDescriptorConfig); auto buildTargets = CompileTargetDescriptors(AZStd::move(buildTargetDescriptors), AZStd::move(testTargetmetaMap)); auto&& [productionTargets, testTargets] = buildTargets; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h index 56775b1c58..943b0cbec4 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h @@ -30,6 +30,7 @@ namespace TestImpact { //! Construct a dynamic dependency map from the build target descriptors and test target metas. AZStd::unique_ptr ConstructDynamicDependencyMap( + SuiteType suiteFilter, const BuildTargetDescriptorConfig& buildTargetDescriptorConfig, const TestTargetMetaConfig& testTargetMetaConfig); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake index da3693433e..2053a1ea5e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake +++ b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake @@ -10,15 +10,23 @@ # set(FILES - Include/TestImpactFramework/TestImpactBitwise.h - Include/TestImpactFramework/TestImpactCallback.h Include/TestImpactFramework/TestImpactException.h - Include/TestImpactFramework/TestImpactFrameworkPath.h + Include/TestImpactFramework/TestImpactRepoPath.h + Include/TestImpactFramework/TestImpactRuntime.h + Include/TestImpactFramework/TestImpactRuntimeException.h + Include/TestImpactFramework/TestImpactConfiguration.h + Include/TestImpactFramework/TestImpactConfigurationException.h + Include/TestImpactFramework/TestImpactChangelist.h + Include/TestImpactFramework/TestImpactChangelistSerializer.h + Include/TestImpactFramework/TestImpactChangelistException.h + Include/TestImpactFramework/TestImpactTestSequence.h + Include/TestImpactFramework/TestImpactClientTestSelection.h + Include/TestImpactFramework/TestImpactClientTestRun.h + Include/TestImpactFramework/TestImpactClientFailureReport.h + Include/TestImpactFramework/TestImpactFileUtils.h Source/Artifact/TestImpactArtifactException.h Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h - Source/Artifact/Factory/TestImpactChangeListFactory.cpp - Source/Artifact/Factory/TestImpactChangeListFactory.h Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.cpp Source/Artifact/Factory/TestImpactTestEnumerationSuiteFactory.h Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp @@ -27,6 +35,8 @@ set(FILES Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.h Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp Source/Artifact/Factory/TestImpactModuleCoverageFactory.h + Source/Artifact/Factory/TestImpactDependencyGraphDataFactory.cpp + Source/Artifact/Factory/TestImpactDependencyGraphDataFactory.h Source/Artifact/Static/TestImpactBuildTargetDescriptor.cpp Source/Artifact/Static/TestImpactBuildTargetDescriptor.h Source/Artifact/Static/TestImpactTargetDescriptorCompiler.cpp @@ -37,7 +47,6 @@ set(FILES Source/Artifact/Static/TestImpactTestTargetDescriptor.cpp Source/Artifact/Static/TestImpactTestTargetDescriptor.h Source/Artifact/Static/TestImpactDependencyGraphData.h - Source/Artifact/Dynamic/TestImpactChangelist.h Source/Artifact/Dynamic/TestImpactTestEnumerationSuite.h Source/Artifact/Dynamic/TestImpactTestRunSuite.h Source/Artifact/Dynamic/TestImpactTestSuite.h @@ -50,6 +59,8 @@ set(FILES Source/Process/TestImpactProcessLauncher.h Source/Process/JobRunner/TestImpactProcessJob.h Source/Process/JobRunner/TestImpactProcessJobInfo.h + Source/Process/JobRunner/TestImpactProcessJobMeta.cpp + Source/Process/JobRunner/TestImpactProcessJobMeta.h Source/Process/JobRunner/TestImpactProcessJobRunner.h Source/Process/Scheduler/TestImpactProcessScheduler.cpp Source/Process/Scheduler/TestImpactProcessScheduler.h @@ -64,6 +75,8 @@ set(FILES Source/Dependency/TestImpactTestSelectorAndPrioritizer.cpp Source/Dependency/TestImpactSourceCoveringTestsList.h Source/Dependency/TestImpactSourceCoveringTestsList.cpp + Source/Dependency/TestImpactSourceCoveringTestsSerializer.cpp + Source/Dependency/TestImpactSourceCoveringTestsSerializer.h Source/Target/TestImpactBuildTarget.cpp Source/Target/TestImpactBuildTarget.h Source/Target/TestImpactBuildTargetList.h @@ -74,29 +87,48 @@ set(FILES Source/Target/TestImpactTestTarget.cpp Source/Target/TestImpactTestTarget.h Source/Target/TestImpactTestTargetList.h - Source/Test/Enumeration/TestImpactTestEnumeration.h - Source/Test/Enumeration/TestImpactTestEnumerationException.h - Source/Test/Enumeration/TestImpactTestEnumerationSerializer.cpp - Source/Test/Enumeration/TestImpactTestEnumerationSerializer.h - Source/Test/Enumeration/TestImpactTestEnumerator.cpp - Source/Test/Enumeration/TestImpactTestEnumerator.h - Source/Test/Run/TestImpactTestRunSerializer.cpp - Source/Test/Run/TestImpactTestRunSerializer.h - Source/Test/Run/TestImpactTestRunner.cpp - Source/Test/Run/TestImpactTestRunner.h - Source/Test/Run/TestImpactInstrumentedTestRunner.cpp - Source/Test/Run/TestImpactInstrumentedTestRunner.h - Source/Test/Run/TestImpactTestRun.cpp - Source/Test/Run/TestImpactTestRun.h - Source/Test/Run/TestImpactTestRunJobData.cpp - Source/Test/Run/TestImpactTestRunJobData.h - Source/Test/Run/TestImpactTestCoverage.cpp - Source/Test/Run/TestImpactTestCoverage.h - Source/Test/Run/TestImpactTestRunException.h - Source/Test/Job/TestImpactTestJobRunner.h - Source/Test/Job/TestImpactTestJobException.h - Source/Test/Job/TestImpactTestJobCommon.h - Source/Test/TestImpactTestSuiteContainer.h + Source/TestEngine/Enumeration/TestImpactTestEnumeration.h + Source/TestEngine/Enumeration/TestImpactTestEnumerationSerializer.cpp + Source/TestEngine/Enumeration/TestImpactTestEnumerationSerializer.h + Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp + Source/TestEngine/Enumeration/TestImpactTestEnumerator.h + Source/TestEngine/Run/TestImpactTestRunSerializer.cpp + Source/TestEngine/Run/TestImpactTestRunSerializer.h + Source/TestEngine/Run/TestImpactTestRunner.cpp + Source/TestEngine/Run/TestImpactTestRunner.h + Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp + Source/TestEngine/Run/TestImpactInstrumentedTestRunner.h + Source/TestEngine/Run/TestImpactTestRun.cpp + Source/TestEngine/Run/TestImpactTestRun.h + Source/TestEngine/Run/TestImpactTestRunJobData.cpp + Source/TestEngine/Run/TestImpactTestRunJobData.h + Source/TestEngine/Run/TestImpactTestCoverage.cpp + Source/TestEngine/Run/TestImpactTestCoverage.h + Source/TestEngine/JobRunner/TestImpactTestJobRunner.h + Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.cpp + Source/TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h + Source/TestEngine/JobRunner/TestImpactTestTargetExtension.h + Source/TestEngine/TestImpactTestEngineJobFailure.cpp + Source/TestEngine/TestImpactTestEngineJobFailure.h + Source/TestEngine/TestImpactTestSuiteContainer.h + Source/TestEngine/TestImpactTestEngine.cpp + Source/TestEngine/TestImpactTestEngine.h + Source/TestEngine/TestImpactTestEngineJob.cpp + Source/TestEngine/TestImpactTestEngineJob.h + Source/TestEngine/TestImpactTestEngineEnumeration.cpp + Source/TestEngine/TestImpactTestEngineEnumeration.h + Source/TestEngine/TestImpactTestEngineRegularRun.cpp + Source/TestEngine/TestImpactTestEngineRegularRun.h + Source/TestEngine/TestImpactTestEngineInstrumentedRun.cpp + Source/TestEngine/TestImpactTestEngineInstrumentedRun.h + Source/TestEngine/TestImpactTestEngineException.h Source/TestImpactException.cpp - Source/TestImpactFrameworkPath.cpp + Source/TestImpactRuntime.cpp + Source/TestImpactRuntimeUtils.cpp + Source/TestImpactRuntimeUtils.h + Source/TestImpactClientTestSelection.cpp + Source/TestImpactClientTestRun.cpp + Source/TestImpactClientFailureReport.cpp + Source/TestImpactChangeListSerializer.cpp + Source/TestImpactRepoPath.cpp ) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake deleted file mode 100644 index 13f788c37b..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake +++ /dev/null @@ -1,38 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -set(FILES - Tests/Artifact/TestImpactTargetDescriptorCompilerTest.cpp - Tests/Artifact/TestImpactBuildTargetDescriptorFactoryTest.cpp - Tests/Artifact/TestImpactModuleCoverageFactoryTest.cpp - Tests/Artifact/TestImpactChangeListFactoryTest.cpp - Tests/Artifact/TestImpactTestEnumerationSuiteFactoryTest.cpp - Tests/Artifact/TestImpactTestRunSuiteFactoryTest.cpp - Tests/Artifact/TestImpactTestTargetMetaMapFactoryTest.cpp - - Tests/Process/TestImpactProcessSchedulerTest.cpp - - Tests/Process/TestImpactProcessTest.cpp - Tests/Target/TestImpactBuildTargetTest.cpp - Tests/TestImpactExceptionTest.cpp - Tests/TestImpactFrameworkPathTest.cpp - Tests/Test/TestImpactTestEnumeratorTest.cpp - Tests/Test/TestImpactTestEumerationSerializerTest.cpp - Tests/Test/TestImpactTestRunSerializerTest.cpp - Tests/Test/TestImpactTestRunnerTest.cpp - Tests/Test/TestImpactInstrumentedTestRunnerTest.cpp - Tests/Test/TestImpactTestCoverageTest.cpp - Tests/TestImpactTestJobRunnerCommon.h - Tests/TestImpactTestMain.cpp - Tests/TestImpactTestUtils.cpp - Tests/TestImpactTestUtils.h - -) diff --git a/cmake/LYTestWrappers.cmake b/cmake/LYTestWrappers.cmake index 5b7e1c766b..3308c4411a 100644 --- a/cmake/LYTestWrappers.cmake +++ b/cmake/LYTestWrappers.cmake @@ -241,12 +241,18 @@ function(ly_add_test) endif() - # Store the test so we can walk through all of them in LYTestImpactFramework.cmake - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS ${ly_add_test_NAME}) - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_test_NAME}_TEST_SUITE ${ly_add_test_TEST_SUITE}) - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_test_NAME}_TEST_LIBRARY ${ly_add_test_TEST_LIBRARY}) - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_test_NAME}_TEST_TIMEOUT ${ly_add_test_TIMEOUT}) - + # Check to see whether or not this test target has been stored in the global list for walking by the test impact analysis framework + get_property(all_tests GLOBAL PROPERTY LY_ALL_TESTS) + if(NOT "${LY_ALL_TESTS_TARGET_NAME}" IN_LIST all_tests) + # This is the first reference to this test target so add it to the global list + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS ${LY_ALL_TESTS_TARGET_NAME}) + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${LY_ALL_TESTS_TARGET_NAME}_TEST_LIBRARY ${ly_add_test_TEST_LIBRARY}) + endif() + # Add the test suite and timeout value to the test target params + set(LY_TEST_PARAMS "${LY_TEST_PARAMS}#${ly_add_test_TEST_SUITE}") + set(LY_TEST_PARAMS "${LY_TEST_PARAMS}#${ly_add_test_TIMEOUT}") + # Store the params for this test target + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${LY_ALL_TESTS_TARGET_NAME}_PARAMS ${LY_TEST_PARAMS}) endfunction() #! ly_add_pytest: registers target PyTest-based test with CTest @@ -288,6 +294,11 @@ function(ly_add_pytest) string(REPLACE "::" "_" pytest_report_directory "${PYTEST_XML_OUTPUT_DIR}/${ly_add_pytest_NAME}.xml") + # Set the name of the current test target for storage in the global list + set(LY_ALL_TESTS_TARGET_NAME ${ly_add_pytest_NAME}) + # Add the script path to the test target params + set(LY_TEST_PARAMS "${ly_add_pytest_PATH}") + ly_add_test( NAME ${ly_add_pytest_NAME} TEST_SUITE ${ly_add_pytest_TEST_SUITE} @@ -298,7 +309,6 @@ function(ly_add_pytest) ${ly_add_pytest_UNPARSED_ARGUMENTS} ) - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_pytest_NAME}_SCRIPT_PATH ${ly_add_pytest_PATH}) set_tests_properties(${LY_ADDED_TEST_NAME} PROPERTIES RUN_SERIAL "${ly_add_pytest_TEST_SERIAL}") endfunction() @@ -341,6 +351,10 @@ function(ly_add_editor_python_test) file(REAL_PATH ${ly_add_editor_python_test_TEST_PROJECT} project_real_path BASE_DIRECTORY ${LY_ROOT_FOLDER}) + # Set the name of the current test target for storage in the global list + set(LY_ALL_TESTS_TARGET_NAME ${ly_add_editor_python_test_NAME}) + # Add the script path to the test target params + set(LY_TEST_PARAMS "${ly_add_editor_python_test_PATH}") # Run test via the run_epbtest.cmake script. # Parameters used are explained in run_epbtest.cmake. ly_add_test( @@ -362,8 +376,6 @@ function(ly_add_editor_python_test) TIMEOUT ${ly_add_editor_python_test_TIMEOUT} COMPONENT ${ly_add_editor_python_test_COMPONENT} ) - - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_editor_python_test_NAME}_SCRIPT_PATH ${ly_add_editor_python_test_PATH}) set_tests_properties(${LY_ADDED_TEST_NAME} PROPERTIES RUN_SERIAL "${ly_add_editor_python_test_TEST_SERIAL}") endfunction() @@ -431,18 +443,23 @@ function(ly_add_googletest) set(full_test_command $ $ AzRunUnitTests) # Add AzTestRunner as a build dependency ly_add_dependencies(${build_target} AZ::AzTestRunner) + # Start the test target params and dd the command runner command # Ideally, we would populate the full command procedurally but the generator expressions won't be expanded by the time we need this data - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_googletest_NAME}_TEST_COMMAND "AzRunUnitTests") + set(LY_TEST_PARAMS "AzRunUnitTests") else() set(full_test_command ${ly_add_googletest_TEST_COMMAND}) # Remove the generator expressions so we are left with the argument(s) required to run unit tests for executable targets string(REPLACE ";" "" stripped_test_command ${full_test_command}) string(GENEX_STRIP ${stripped_test_command} stripped_test_command) - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_googletest_NAME}_TEST_COMMAND ${stripped_test_command}) + # Start the test target params and dd the command runner command + set(LY_TEST_PARAMS "${stripped_test_command}") endif() string(REPLACE "::" "_" report_directory "${GTEST_XML_OUTPUT_DIR}/${ly_add_googletest_NAME}.xml") + # Set the name of the current test target for storage in the global list + set(LY_ALL_TESTS_TARGET_NAME ${target_name}) + # Invoke the lower level ly_add_test command to add the actual ctest and setup the test labels to add_dependencies on the target ly_add_test( NAME ${ly_add_googletest_NAME} @@ -520,16 +537,21 @@ function(ly_add_googlebenchmark) # If command is not supplied attempts, uses the AzTestRunner to run googlebenchmarks on the supplied TARGET set(full_test_command $ $ AzRunBenchmarks ${output_format_args}) + # Start the test target params and dd the command runner command # Ideally, we would populate the full command procedurally but the generator expressions won't be expanded by the time we need this data - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_googlebenchmark_NAME}_TEST_COMMAND "AzRunUnitTests") + set(LY_TEST_PARAMS "AzRunUnitTests") else() set(full_test_command ${ly_add_googlebenchmark_TEST_COMMAND}) # Remove the generator expressions so we are left with the argument(s) required to run unit tests for executable targets string(REPLACE ";" "" stripped_test_command ${full_test_command}) string(GENEX_STRIP ${stripped_test_command} stripped_test_command) - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_googletest_NAME}_TEST_COMMAND ${stripped_test_command}) + # Start the test target params and dd the command runner command + set(LY_TEST_PARAMS "${stripped_test_command}") endif() - + + # Set the name of the current test target for storage in the global list + set(LY_ALL_TESTS_TARGET_NAME ${ly_add_googlebenchmark_NAME}) + # Set the name of the current test target for storage in the global list ly_add_test( NAME ${ly_add_googlebenchmark_NAME} TEST_REQUIRES ${ly_add_googlebenchmark_TEST_REQUIRES} diff --git a/cmake/TestImpactFramework/ConsoleFrontendConfig.in b/cmake/TestImpactFramework/ConsoleFrontendConfig.in index f6af3d801d..357499771c 100644 --- a/cmake/TestImpactFramework/ConsoleFrontendConfig.in +++ b/cmake/TestImpactFramework/ConsoleFrontendConfig.in @@ -4,7 +4,8 @@ "timestamp": "${timestamp}" }, "repo": { - "root": "${repo_dir}" + "root": "${repo_dir}", + "tiaf_bin": "${tiaf_bin}" }, "workspace": { "temp": { @@ -13,11 +14,23 @@ "artifact_dir": "RuntimeArtifact" } }, - "persistent": { - "root": "${persistent_dir}", + "active": { + "root": "${active_dir}", "relative_paths": { - "test_impact_data_file": "TestImpactData.spartia", - "enumeration_cache_dir": "EnumerationCache" + "test_impact_data_files": { + "main": "TestImpactData.main.spartia", + "periodic": "TestImpactData.periodic.spartia", + "sandbox": "TestImpactData.sandbox.spartia" + }, + "enumeration_cache_dir": "EnumerationCache", + "last_build_target_list_file": "LastRunBuildTargets.json" + } + }, + "historic": { + "root": "${historic_dir}", + "relative_paths": { + "last_run_hash_file": "last_run.hash", + "last_build_target_list_file": "LastRunBuildTargets.json" } } }, diff --git a/cmake/TestImpactFramework/LYTestImpactFramework.cmake b/cmake/TestImpactFramework/LYTestImpactFramework.cmake index 3081a4c89c..54dadbd8ad 100644 --- a/cmake/TestImpactFramework/LYTestImpactFramework.cmake +++ b/cmake/TestImpactFramework/LYTestImpactFramework.cmake @@ -89,7 +89,7 @@ function(ly_test_impact_get_test_launch_method TARGET_NAME LAUNCH_METHOD) elseif("${target_type}" STREQUAL "EXECUTABLE") set(${LAUNCH_METHOD} "stand_alone" PARENT_SCOPE) else() - message(FATAL_ERROR "Cannot deduce test target launch method for the target type ${target_type}") + message(FATAL_ERROR "Cannot deduce test target launch method for the target ${TARGET_NAME} with type ${target_type}") endif() endfunction() @@ -131,33 +131,50 @@ function(ly_test_impact_extract_python_test COMPOSITE_TEST TEST_NAME) set(${TEST_NAME} ${test_name} PARENT_SCOPE) endfunction() -#! ly_test_impact_extract_google_test_params: extracts the google test name and command parameters. +#! ly_test_impact_extract_google_test_params: extracts the suites for the given google test. # # \arg:COMPOSITE_TEST test in the form 'namespace::test' +# \arg:COMPOSITE_SUITES composite list of suites for this target # \arg:TEST_NAME name of test -# \arg:TEST_COMMAND optional command arguments to run the test -function(ly_test_impact_extract_google_test_params COMPOSITE_TEST TEST_NAME TEST_COMMAND) - get_property(test_command GLOBAL PROPERTY LY_ALL_TESTS_${COMPOSITE_TEST}_TEST_COMMAND) +# \arg:TEST_SUITES extracted list of suites for this target in JSON format +function(ly_test_impact_extract_google_test_params COMPOSITE_TEST COMPOSITE_SUITES TEST_NAME TEST_SUITES) # Namespace and test are mandatory string(REPLACE "::" ";" test_components ${COMPOSITE_TEST}) list(LENGTH test_components num_test_components) if(num_test_components LESS 2) message(FATAL_ERROR "The test ${test_components} appears to have been specified without a namespace, i.e.:\ly_add_googletest/benchmark(NAME ${test_components})\nInstead of (perhaps):\ly_add_googletest/benchmark(NAME Gem::${test_components})\nPlease add the missing namespace before proceeding.") endif() - + list(GET test_components 0 test_namespace) list(GET test_components 1 test_name) set(${TEST_NAMESPACE} ${test_namespace} PARENT_SCOPE) set(${TEST_NAME} ${test_name} PARENT_SCOPE) - set(${TEST_COMMAND} ${test_command} PARENT_SCOPE) + + set(test_suites "") + foreach(composite_suite ${COMPOSITE_SUITES}) + # Command, suite, timeout + string(REPLACE "#" ";" suite_components ${composite_suite}) + list(LENGTH suite_components num_suite_components) + if(num_suite_components LESS 3) + message(FATAL_ERROR "The suite components ${composite_suite} are required to be in the following format: command#suite#string.") + endif() + list(GET suite_components 0 test_command) + list(GET suite_components 1 test_suite) + list(GET suite_components 2 test_timeout) + set(suite_params "{ \"suite\": \"${test_suite}\", \"command\": \"${test_command}\", \"timeout\": ${test_timeout} }") + list(APPEND test_suites "${suite_params}") + endforeach() + string(REPLACE ";" ", " test_suites "${test_suites}") + set(${TEST_SUITES} ${test_suites} PARENT_SCOPE) endfunction() #! ly_test_impact_extract_python_test_params: extracts the python test name and relative script path parameters. # # \arg:COMPOSITE_TEST test in form 'namespace::test' or 'test' +# \arg:COMPOSITE_SUITES composite list of suites for this target # \arg:TEST_NAME name of test -# \arg:SCRIPT_PATH name of test -function(ly_test_impact_extract_python_test_params COMPOSITE_TEST TEST_NAME SCRIPT_PATH) +# \arg:TEST_SUITES extracted list of suites for this target in JSON format +function(ly_test_impact_extract_python_test_params COMPOSITE_TEST COMPOSITE_SUITES TEST_NAME TEST_SUITES) get_property(script_path GLOBAL PROPERTY LY_ALL_TESTS_${COMPOSITE_TEST}_SCRIPT_PATH) # namespace is optional, in which case this component will be simply the test name @@ -169,15 +186,30 @@ function(ly_test_impact_extract_python_test_params COMPOSITE_TEST TEST_NAME SCRI set(test_name ${test_components}) endif() - # Get python script path relative to repo root - ly_test_impact_rebase_file_to_repo_root( - ${script_path} - script_path - ${LY_ROOT_FOLDER} - ) - set(${TEST_NAME} ${test_name} PARENT_SCOPE) - set(${SCRIPT_PATH} ${script_path} PARENT_SCOPE) + + set(test_suites "") + foreach(composite_suite ${COMPOSITE_SUITES}) + # Script path, suite, timeout + string(REPLACE "#" ";" suite_components ${composite_suite}) + list(LENGTH suite_components num_suite_components) + if(num_suite_components LESS 3) + message(FATAL_ERROR "The suite components ${composite_suite} are required to be in the following format: script_path#suite#string.") + endif() + list(GET suite_components 0 script_path) + list(GET suite_components 1 test_suite) + list(GET suite_components 2 test_timeout) + # Get python script path relative to repo root + ly_test_impact_rebase_file_to_repo_root( + ${script_path} + script_path + ${LY_ROOT_FOLDER} + ) + set(suite_params "{ \"suite\": \"${test_suite}\", \"script\": \"${script_path}\", \"timeout\": ${test_timeout} }") + list(APPEND test_suites "${suite_params}") + endforeach() + string(REPLACE ";" ", " test_suites "${test_suites}") + set(${TEST_SUITES} ${test_suites} PARENT_SCOPE) endfunction() #! ly_test_impact_write_test_enumeration_file: exports the master test lists to file. @@ -185,7 +217,6 @@ endfunction() # \arg:TEST_ENUMERATION_TEMPLATE_FILE path to test enumeration template file function(ly_test_impact_write_test_enumeration_file TEST_ENUMERATION_TEMPLATE_FILE) get_property(LY_ALL_TESTS GLOBAL PROPERTY LY_ALL_TESTS) - # Enumerated tests for each type set(google_tests "") set(google_benchmarks "") @@ -196,29 +227,28 @@ function(ly_test_impact_write_test_enumeration_file TEST_ENUMERATION_TEMPLATE_FI # Walk the test list foreach(test ${LY_ALL_TESTS}) message(TRACE "Parsing ${test}") + get_property(test_params GLOBAL PROPERTY LY_ALL_TESTS_${test}_PARAMS) get_property(test_type GLOBAL PROPERTY LY_ALL_TESTS_${test}_TEST_LIBRARY) - get_property(test_suite GLOBAL PROPERTY LY_ALL_TESTS_${test}_TEST_SUITE) - get_property(test_timeout GLOBAL PROPERTY LY_ALL_TESTS_${test}_TEST_TIMEOUT) if("${test_type}" STREQUAL "pytest") # Python tests - ly_test_impact_extract_python_test_params(${test} test_name script_path) - list(APPEND python_tests " { \"name\": \"${test_name}\", \"suite\": \"${test_suite}\", \"script\": \"${script_path}\", \"timeout\":${test_timeout} }") + ly_test_impact_extract_python_test_params(${test} "${test_params}" test_name test_suites) + list(APPEND python_tests " { \"name\": \"${test_name}\", \"suites\": [${test_suites}] }") elseif("${test_type}" STREQUAL "pytest_editor") - # Python editor tests - ly_test_impact_extract_python_test_params(${test} test_name script_path) - list(APPEND python_editor_tests " { \"name\": \"${test_name}\", \"suite\": \"${test_suite}\", \"script\": \"${script_path}\", \"timeout\":${test_timeout} }") + # Python editor tests + ly_test_impact_extract_python_test_params(${test} "${test_params}" test_name test_suites) + list(APPEND python_editor_tests " { \"name\": \"${test_name}\", \"suites\": [${test_suites}] }") elseif("${test_type}" STREQUAL "googletest") # Google tests - ly_test_impact_extract_google_test_params(${test} test_name test_command) - ly_test_impact_get_test_launch_method(${test_name} launch_method) - list(APPEND google_tests " { \"name\": \"${test_name}\", \"suite\": \"${test_suite}\", \"command\": \"${test_command}\", \"timeout\":${test_timeout}, \"launch_method\": \"${launch_method}\" }") + ly_test_impact_extract_google_test_params(${test} "${test_params}" test_name test_suites) + ly_test_impact_get_test_launch_method(${test} launch_method) + list(APPEND google_tests " { \"name\": \"${test_name}\", \"launch_method\": \"${launch_method}\", \"suites\": [${test_suites}] }") elseif("${test_type}" STREQUAL "googlebenchmark") # Google benchmarks - ly_test_impact_extract_google_test_params(${test} test_name test_command) - list(APPEND google_benchmarks " { \"name\": \"${test_name}\", \"suite\": \"${test_suite}\", \"command\": \"${test_command}\", \"timeout\":${test_timeout} }") + ly_test_impact_extract_google_test_params(${test} "${test_params}" test_name test_suites) + list(APPEND google_benchmarks " { \"name\": \"${test_name}\", \"launch_method\": \"${launch_method}\", \"suites\": [${test_suites}] }") else() message("${test_name} is of unknown type (TEST_LIBRARY property is empty)") - list(APPEND unknown_tests " { \"name\": \"${test}\" }") + list(APPEND unknown_tests " { \"name\": \"${test}\", \"type\": \"${test_type}\" }") endif() endforeach() @@ -330,8 +360,11 @@ function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE PERSISTENT_DATA_D # Temp dir set(temp_dir "${LY_TEST_IMPACT_TEMP_DIR}") - # Persistent dir - set(persistent_dir "${PERSISTENT_DATA_DIR}") + # Active persistent data dir + set(active_dir "${PERSISTENT_DATA_DIR}/active") + + # Historic persistent data dir + set(historic_dir "${PERSISTENT_DATA_DIR}/historic") # Source to target mappings dir set(source_target_mapping_dir "${LY_TEST_IMPACT_SOURCE_TARGET_MAPPING_DIR}") @@ -341,6 +374,9 @@ function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE PERSISTENT_DATA_D # Build dependency artifact dir set(target_dependency_dir "${LY_TEST_IMPACT_TARGET_DEPENDENCY_DIR}") + + # Test impact analysis framework binary + set(tiaf_bin "$") # Substitute config file template with above vars file(READ "${CONFIG_TEMPLATE_FILE}" config_file) @@ -348,9 +384,13 @@ function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE PERSISTENT_DATA_D # Write out entire config contents to a file in the build directory of the test impact framework console target file(GENERATE - OUTPUT "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/$.$.json" + OUTPUT "${PERSISTENT_DATA_DIR}/$.$.json" CONTENT ${config_file} ) + + # Set the above config file as the default config file to use for the test impact framework console target + target_compile_definitions(${LY_TEST_IMPACT_CONSOLE_STATIC_TARGET} PUBLIC "LY_TEST_IMPACT_DEFAULT_CONFIG_FILE=\"${PERSISTENT_DATA_DIR}/$.$.json\"") + message(DEBUG "Test impact framework post steps complete") endfunction() #! ly_test_impact_post_step: runs the post steps to be executed after all other cmake scripts have been executed. @@ -360,8 +400,7 @@ function(ly_test_impact_post_step) endif() # Directory per build config for persistent test impact data (to be checked in) - set(persistent_data_dir "${LY_ROOT_FOLDER}/Tests/test_impact_framework/${CMAKE_SYSTEM_NAME}/$") - + set(persistent_data_dir "${LY_TEST_IMPACT_WORKING_DIR}/persistent") # Directory for binaries built for this profile set(bin_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$") @@ -388,8 +427,4 @@ function(ly_test_impact_post_step) # Copy over the graphviz options file for the build dependency graphs message(DEBUG "Test impact framework config file written") file(COPY "cmake/TestImpactFramework/CMakeGraphVizOptions.cmake" DESTINATION ${CMAKE_BINARY_DIR}) - - # Set the above config file as the default config file to use for the test impact framework console target - target_compile_definitions(${LY_TEST_IMPACT_CONSOLE_STATIC_TARGET} PUBLIC "LY_TEST_IMPACT_DEFAULT_CONFIG_FILE=\"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/$.$.json\"") - message(DEBUG "Test impact framework post steps complete") endfunction() From c6d0887210c367f58b086d90f5f20759887d5e72 Mon Sep 17 00:00:00 2001 From: moudgils Date: Sun, 6 Jun 2021 10:24:42 -0700 Subject: [PATCH 093/233] Minor method name changes --- Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp | 8 ++++---- Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp index 1e2edc6520..5d6c275184 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp @@ -422,12 +422,12 @@ namespace AZ { if(RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Compute)) { - ApplyUseResourceToCompute(commandEncoder, it.second, resourcesToMakeResidentCompute); + CollectResourcesForCompute(commandEncoder, it.second, resourcesToMakeResidentCompute); } else { AZ_Assert(RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Vertex) || RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Fragment), "The visibility mask %i is not set for Vertex or fragment stage", visMaskIt->second); - ApplyUseResourceToGraphic(commandEncoder, visMaskIt->second, it.second, resourcesToMakeResidentGraphics); + CollectResourcesForGraphics(commandEncoder, visMaskIt->second, it.second, resourcesToMakeResidentGraphics); } } } @@ -450,7 +450,7 @@ namespace AZ } } - void ArgumentBuffer::ApplyUseResourceToCompute(id encoder, const ResourceBindingsSet& resourceBindingDataSet, ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const + void ArgumentBuffer::CollectResourcesForCompute(id encoder, const ResourceBindingsSet& resourceBindingDataSet, ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const { for (const auto& resourceBindingData : resourceBindingDataSet) { @@ -477,7 +477,7 @@ namespace AZ } } - void ArgumentBuffer::ApplyUseResourceToGraphic(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet, GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const + void ArgumentBuffer::CollectResourcesForGraphics(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet, GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const { MTLRenderStages mtlRenderStages = GetRenderStages(visShaderMask); diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h index 2434b8312d..06380162b6 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h @@ -128,8 +128,8 @@ namespace AZ using ComputeResourcesToMakeResidentMap = AZStd::unordered_map; using GraphicsResourcesToMakeResidentMap = AZStd::unordered_map, MetalResourceArray>; - void ApplyUseResourceToCompute(id encoder, const ResourceBindingsSet& resourceBindingData, ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; - void ApplyUseResourceToGraphic(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet, GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; + void CollectResourcesForCompute(id encoder, const ResourceBindingsSet& resourceBindingData, ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; + void CollectResourcesForGraphics(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet, GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; //! Use visibility information to call UseResource on all resources for this Argument Buffer void ApplyUseResource(id encoder, const ResourceBindingsMap& resourceMap, From b00f56909027667fe99fc6e713175f91f401c128 Mon Sep 17 00:00:00 2001 From: jonawals Date: Sun, 6 Jun 2021 19:09:24 +0100 Subject: [PATCH 094/233] Address PR comments --- .../Code/Source/TestImpactConsoleMain.cpp | 2 +- cmake/LYTestWrappers.cmake | 32 ++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp index fab6509244..4853c6da15 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp @@ -121,7 +121,7 @@ namespace TestImpact } else { - // One sequence timed out whilst the other sequence succeeded or both sequences timed out + // One or both sequences timed out or failed result = TestSequenceResult::Timeout; } } diff --git a/cmake/LYTestWrappers.cmake b/cmake/LYTestWrappers.cmake index 3308c4411a..eebc281d06 100644 --- a/cmake/LYTestWrappers.cmake +++ b/cmake/LYTestWrappers.cmake @@ -71,6 +71,7 @@ endfunction() #! ly_add_test: Adds a new RUN_TEST using for the specified target using the supplied command # # \arg:NAME - Name to for the test run target +# \arg:PARENT_NAME(optional) - Name of the parent test run target (if this is a subsequent call to specify a suite) # \arg:TEST_REQUIRES(optional) - List of system resources that are required to run this test. # Only available option is "gpu" # \arg:TEST_SUITE(optional) - "smoke" or "periodic" or "sandbox" - prevents the test from running normally @@ -94,7 +95,7 @@ endfunction() # sets LY_ADDED_TEST_NAME to the fully qualified name of the test, in parent scope function(ly_add_test) set(options EXCLUDE_TEST_RUN_TARGET_FROM_IDE) - set(one_value_args NAME TEST_LIBRARY TEST_SUITE TIMEOUT) + set(one_value_args NAME PARENT_NAME TEST_LIBRARY TEST_SUITE TIMEOUT) set(multi_value_args TEST_REQUIRES TEST_COMMAND NON_IDE_PARAMS RUNTIME_DEPENDENCIES COMPONENT LABELS) # note that we dont use TEST_LIBRARY here, but PAL files might so do not remove! @@ -241,18 +242,24 @@ function(ly_add_test) endif() + if(NOT ly_add_test_PARENT_NAME) + set(test_target ${ly_add_test_NAME}) + else() + set(test_target ${ly_add_test_PARENT_NAME}) + endif() + # Check to see whether or not this test target has been stored in the global list for walking by the test impact analysis framework get_property(all_tests GLOBAL PROPERTY LY_ALL_TESTS) - if(NOT "${LY_ALL_TESTS_TARGET_NAME}" IN_LIST all_tests) + if(NOT "${test_target}" IN_LIST all_tests) # This is the first reference to this test target so add it to the global list - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS ${LY_ALL_TESTS_TARGET_NAME}) - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${LY_ALL_TESTS_TARGET_NAME}_TEST_LIBRARY ${ly_add_test_TEST_LIBRARY}) + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS ${test_target}) + set_property(GLOBAL PROPERTY LY_ALL_TESTS_${test_target}_TEST_LIBRARY ${ly_add_test_TEST_LIBRARY}) endif() # Add the test suite and timeout value to the test target params set(LY_TEST_PARAMS "${LY_TEST_PARAMS}#${ly_add_test_TEST_SUITE}") set(LY_TEST_PARAMS "${LY_TEST_PARAMS}#${ly_add_test_TIMEOUT}") # Store the params for this test target - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${LY_ALL_TESTS_TARGET_NAME}_PARAMS ${LY_TEST_PARAMS}) + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${test_target}_PARAMS ${LY_TEST_PARAMS}) endfunction() #! ly_add_pytest: registers target PyTest-based test with CTest @@ -294,13 +301,12 @@ function(ly_add_pytest) string(REPLACE "::" "_" pytest_report_directory "${PYTEST_XML_OUTPUT_DIR}/${ly_add_pytest_NAME}.xml") - # Set the name of the current test target for storage in the global list - set(LY_ALL_TESTS_TARGET_NAME ${ly_add_pytest_NAME}) # Add the script path to the test target params set(LY_TEST_PARAMS "${ly_add_pytest_PATH}") ly_add_test( NAME ${ly_add_pytest_NAME} + PARENT_NAME ${ly_add_pytest_NAME} TEST_SUITE ${ly_add_pytest_TEST_SUITE} LABELS FRAMEWORK_pytest TEST_COMMAND ${LY_PYTEST_EXECUTABLE} ${ly_add_pytest_PATH} ${ly_add_pytest_EXTRA_ARGS} --junitxml=${pytest_report_directory} ${custom_marks_args} @@ -351,14 +357,13 @@ function(ly_add_editor_python_test) file(REAL_PATH ${ly_add_editor_python_test_TEST_PROJECT} project_real_path BASE_DIRECTORY ${LY_ROOT_FOLDER}) - # Set the name of the current test target for storage in the global list - set(LY_ALL_TESTS_TARGET_NAME ${ly_add_editor_python_test_NAME}) # Add the script path to the test target params set(LY_TEST_PARAMS "${ly_add_editor_python_test_PATH}") # Run test via the run_epbtest.cmake script. # Parameters used are explained in run_epbtest.cmake. ly_add_test( NAME ${ly_add_editor_python_test_NAME} + PARENT_NAME ${ly_add_editor_python_test_NAME} TEST_REQUIRES ${ly_add_editor_python_test_TEST_REQUIRES} TEST_COMMAND ${CMAKE_COMMAND} -DCMD_ARG_TEST_PROJECT=${project_real_path} @@ -457,12 +462,10 @@ function(ly_add_googletest) string(REPLACE "::" "_" report_directory "${GTEST_XML_OUTPUT_DIR}/${ly_add_googletest_NAME}.xml") - # Set the name of the current test target for storage in the global list - set(LY_ALL_TESTS_TARGET_NAME ${target_name}) - # Invoke the lower level ly_add_test command to add the actual ctest and setup the test labels to add_dependencies on the target ly_add_test( NAME ${ly_add_googletest_NAME} + PARENT_NAME ${target_name} TEST_SUITE ${ly_add_googletest_TEST_SUITE} LABELS FRAMEWORK_googletest TEST_COMMAND ${full_test_command} --gtest_output=xml:${report_directory} ${LY_GOOGLETEST_EXTRA_PARAMS} @@ -548,12 +551,11 @@ function(ly_add_googlebenchmark) # Start the test target params and dd the command runner command set(LY_TEST_PARAMS "${stripped_test_command}") endif() - - # Set the name of the current test target for storage in the global list - set(LY_ALL_TESTS_TARGET_NAME ${ly_add_googlebenchmark_NAME}) + # Set the name of the current test target for storage in the global list ly_add_test( NAME ${ly_add_googlebenchmark_NAME} + PARENT_NAME ${ly_add_googlebenchmark_NAME} TEST_REQUIRES ${ly_add_googlebenchmark_TEST_REQUIRES} TEST_COMMAND ${full_test_command} ${LY_GOOGLETEST_EXTRA_PARAMS} TEST_SUITE "benchmark" From 4ed0c7b1d8ccaf70bec71674763dc0dd65ad5f9f Mon Sep 17 00:00:00 2001 From: jonawals Date: Mon, 7 Jun 2021 08:52:20 +0100 Subject: [PATCH 095/233] Add drafting of failed tests --- .../Source/TestImpactCommandLineOptions.cpp | 50 ++++++++++-------- .../Source/TestImpactCommandLineOptions.h | 6 +-- .../Code/Source/TestImpactConsoleMain.cpp | 2 +- .../TestImpactFramework/TestImpactRuntime.h | 7 +-- .../TestImpactTestSequence.h | 8 +-- .../TestImpactDynamicDependencyMap.cpp | 48 +++++++++++------ .../TestImpactDynamicDependencyMap.h | 9 ++-- .../Runtime/Code/Source/TestImpactRuntime.cpp | 51 ++++++++++++++----- 8 files changed, 119 insertions(+), 62 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp index 5ff070601a..aad9319cf9 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp @@ -28,7 +28,7 @@ namespace TestImpact Sequence, TestPrioritizationPolicy, ExecutionFailurePolicy, - ExecutionFailureDraftingPolicy, + FailedTestCoveragePolicy, TestFailurePolicy, IntegrityFailurePolicy, TestShardingPolicy, @@ -50,7 +50,9 @@ namespace TestImpact Continue, Ignore, StdOut, - File + File, + Remove, + Keep }; constexpr const char* OptionKeys[] = @@ -62,7 +64,7 @@ namespace TestImpact "sequence", "ppolicy", "epolicy", - "rexecfailures", + "cpolicy", "fpolicy", "ipolicy", "shard", @@ -84,7 +86,9 @@ namespace TestImpact "continue", "ignore", "stdout", - "file" + "file", + "remove", + "keep" }; RepoPath ParseConfigurationFile(const AZ::CommandLine& cmd) @@ -139,15 +143,15 @@ namespace TestImpact return ParseMultiStateOption(OptionKeys[ExecutionFailurePolicy], states, cmd).value_or(Policy::ExecutionFailure::Continue); } - Policy::ExecutionFailureDrafting ParseExecutionFailureDraftingPolicy(const AZ::CommandLine& cmd) + Policy::FailedTestCoverage ParseFailedTestCoveragePolicy(const AZ::CommandLine& cmd) { - const BinaryStateValue states = + const AZStd::vector> states = { - Policy::ExecutionFailureDrafting::Never, - Policy::ExecutionFailureDrafting::Always + {OptionKeys[Remove], Policy::FailedTestCoverage::Remove}, + {OptionKeys[Keep], Policy::FailedTestCoverage::Keep} }; - return ParseOnOffOption(OptionKeys[ExecutionFailureDraftingPolicy], states, cmd).value_or(Policy::ExecutionFailureDrafting::Always); + return ParseMultiStateOption(OptionKeys[FailedTestCoveragePolicy], states, cmd).value_or(Policy::FailedTestCoverage::Keep); } Policy::TestFailure ParseTestFailurePolicy(const AZ::CommandLine& cmd) @@ -275,7 +279,7 @@ namespace TestImpact m_testSequenceType = ParseTestSequenceType(cmd); m_testPrioritizationPolicy = ParseTestPrioritizationPolicy(cmd); m_executionFailurePolicy = ParseExecutionFailurePolicy(cmd); - m_executionFailureDraftingPolicy = ParseExecutionFailureDraftingPolicy(cmd); + m_failedTestCoveragePolicy = ParseFailedTestCoveragePolicy(cmd); m_testFailurePolicy = ParseTestFailurePolicy(cmd); m_integrityFailurePolicy = ParseIntegrityFailurePolicy(cmd); m_testShardingPolicy = ParseTestShardingPolicy(cmd); @@ -327,9 +331,9 @@ namespace TestImpact return m_executionFailurePolicy; } - Policy::ExecutionFailureDrafting CommandLineOptions::GetExecutionFailureDraftingPolicy() const + Policy::FailedTestCoverage CommandLineOptions::GetFailedTestCoveragePolicy() const { - return m_executionFailureDraftingPolicy; + return m_failedTestCoveragePolicy; } Policy::TestFailure CommandLineOptions::GetTestFailurePolicy() const @@ -401,7 +405,11 @@ namespace TestImpact " tests are run regardless).\n" " -shard= Break any test targets with a sharding policy into the number of \n" " shards according to the maximum concurrency value.\n" - " -rexecfailures= Attempt to execute test targets that previously failed to execute.\n" + " -cpolicy= Policy for handling the coverage data of failed tests (both test that \n" + " failed to execute and tests that ran but failed), where remove will \n" + " remove the failed tests from the all coverage data(causing them to be \n" + " drafted into future test runs) and keep will keep any existing coverage \n" + " data and update the coverage data for failed tests that produce coverage.\n" " -targetout= Capture of individual test run stdout, where stdout will capture \n" " each individual test target's stdout and output each one to stdout \n" " and file will capture each individual test target's stdout and output \n" @@ -409,25 +417,25 @@ namespace TestImpact " -epolicy= Policy for handling test execution failure (test targets could not be \n" " launched due to the binary not being built, incorrect paths, etc.), \n" " where abort will abort the entire test sequence upon the first test\n" - " target execution failureand report a failure(along with the return \n" + " target execution failure and report a failure(along with the return \n" " code of the test target that failed to launch), continue will continue \n" " with the test sequence in the event of test target execution failures\n" " and treat the test targets that failed to launch as as test failures\n" " (along with the return codes of the test targets that failed to \n" " launch), ignore will continue with the test sequence in the event of \n" - " test target execution failuresand treat the test targets that failed\n" - " to launch as as test passes(along with the return codes of the test \n" + " test target execution failures and treat the test targets that failed\n" + " to launch as test passes(along with the return codes of the test \n" " targets that failed to launch).\n" " -fpolicy Policy for handling test failures (test targets report failing tests), \n" - " where abort will abort the entire test sequenceupon the first test \n" - " failureand report a failure and continue will continue with the test\n" - " sequence in the event of test failuresand report the test failures.\n" + " where abort will abort the entire test sequence upon the first test \n" + " failure and report a failure and continue will continue with the test\n" + " sequence in the event of test failures and report the test failures.\n" " -ipolicy= Policy for handling coverage data integrity failures, where abort will \n" " abort the test sequenceand report a failure, seed will attempt another \n" " sequence using the seed sequence type, otherwise will abort and report \n" - " a failure (this option has no effect for regularand seed sequence \n" + " a failure (this option has no effect for regular and seed sequence \n" " types) and rerun will attempt another sequence using the regular \n" - " sequence type, otherwise will abortand report a failure(this option has \n" + " sequence type, otherwise will abort and report a failure(this option has \n" " no effect for regular sequence type).\n" " -ppolicy= Policy for prioritizing selected test targets, where none will not \n" " attempt any test target prioritization and locality will attempt to \n" diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h index 99b1b98dba..b215261a87 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h @@ -64,8 +64,8 @@ namespace TestImpact //! Returns the test execution failure policy to use. Policy::ExecutionFailure GetExecutionFailurePolicy() const; - //! Returns the test historic test execution failure drafting policy to use. - Policy::ExecutionFailureDrafting GetExecutionFailureDraftingPolicy() const; + //! Returns failed test coverage drafting policy to use. + Policy::FailedTestCoverage GetFailedTestCoveragePolicy() const; //! Returns the test failure policy to use. Policy::TestFailure GetTestFailurePolicy() const; @@ -98,7 +98,7 @@ namespace TestImpact TestSequenceType m_testSequenceType; Policy::TestPrioritization m_testPrioritizationPolicy = Policy::TestPrioritization::None; Policy::ExecutionFailure m_executionFailurePolicy = Policy::ExecutionFailure::Continue; - Policy::ExecutionFailureDrafting m_executionFailureDraftingPolicy = Policy::ExecutionFailureDrafting::Always; + Policy::FailedTestCoverage m_failedTestCoveragePolicy = Policy::FailedTestCoverage::Keep; Policy::TestFailure m_testFailurePolicy = Policy::TestFailure::Abort; Policy::IntegrityFailure m_integrityFailurePolicy = Policy::IntegrityFailure::Abort; Policy::TestSharding m_testShardingPolicy = Policy::TestSharding::Never; diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp index 4853c6da15..77b1d98b3c 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp @@ -205,7 +205,7 @@ namespace TestImpact RuntimeConfigurationFactory(ReadFileContents(options.GetConfigurationFile())), options.GetSuiteFilter(), options.GetExecutionFailurePolicy(), - options.GetExecutionFailureDraftingPolicy(), + options.GetFailedTestCoveragePolicy(), options.GetTestFailurePolicy(), options.GetIntegrityFailurePolicy(), options.GetTestShardingPolicy(), diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h index ae1f78c5c7..86f2907ab7 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h @@ -35,6 +35,7 @@ namespace TestImpact class TestEngine; class TestTarget; class SourceCoveringTestsList; + class TestEngineInstrumentedRun; //! Callback for a test sequence that isn't using test impact analysis to determine selected tests. //! @param tests The tests that will be run for this sequence. @@ -102,7 +103,7 @@ namespace TestImpact RuntimeConfig&& config, SuiteType suiteFilter, Policy::ExecutionFailure executionFailurePolicy, - Policy::ExecutionFailureDrafting executionFailureDraftingPolicy, + Policy::FailedTestCoverage failedTestCoveragePolicy, Policy::TestFailure testFailurePolicy, Policy::IntegrityFailure integrationFailurePolicy, Policy::TestSharding testShardingPolicy, @@ -204,13 +205,13 @@ namespace TestImpact void ClearDynamicDependencyMapAndRemoveExistingFile(); //! Updates the dynamic dependency map and serializes the entire map to disk. - void UpdateAndSerializeDynamicDependencyMap(const SourceCoveringTestsList& sourceCoverageTestsList); + void UpdateAndSerializeDynamicDependencyMap(const AZStd::vector& jobs); RuntimeConfig m_config; SuiteType m_suiteFilter; RepoPath m_sparTIAFile; Policy::ExecutionFailure m_executionFailurePolicy; - Policy::ExecutionFailureDrafting m_executionFailureDraftingPolicy; + Policy::FailedTestCoverage m_failedTestCoveragePolicy; Policy::TestFailure m_testFailurePolicy; Policy::IntegrityFailure m_integrationFailurePolicy; Policy::TestSharding m_testShardingPolicy; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h index 4e8908cf3f..5f9bff952f 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h @@ -31,11 +31,11 @@ namespace TestImpact Ignore //!< Continue the test sequence and ignore the execution failures. }; - //! Policy for reattempting the execution of test targets that failed to execute in previous runs. - enum class ExecutionFailureDrafting + //! Policy for handling the coverage data of failed tests targets (both test that failed to execute and tests that ran but failed). + enum class FailedTestCoverage { - Never, //!< Do not attempt to execute historic execution failures. - Always //!< Reattempt the exectution of historic execution failures. + Remove, //!< Remove the failed test targets from the all coverage data (causing them to be drafted into future test runs). + Keep //!< Keep any existing coverage data and update the coverage data for failed test targetss that produce coverage. }; //! Policy for prioritizing selected tests. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp index ece5a7c13b..22bcc97c2a 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp @@ -56,7 +56,7 @@ namespace TestImpact for (const auto& target : m_testTargets.GetTargets()) { mapBuildTargetSources(&target); - m_testTargetSourceCoverageCount[&target] = 0; + m_testTargetSourceCoverage[&target] = {}; } } @@ -144,18 +144,15 @@ namespace TestImpact sourceCoverage.GetPath().c_str()).c_str()); auto [sourceDependencyIt, inserted] = m_sourceDependencyMap.insert(sourceCoverage.GetPath().String()); - auto& [key, sourceDependency] = *sourceDependencyIt; + auto& [source, sourceDependency] = *sourceDependencyIt; - // Knock down the source coverage count for the test targets and clear any existing coverage for the delta + // Remove the source from the test target covering sources map and clear any existing coverage for the delta for (const auto& testTarget : sourceDependency.m_coveringTestTargets) { - if (auto coveringTestTargetIt = m_testTargetSourceCoverageCount.find(testTarget); - coveringTestTargetIt != m_testTargetSourceCoverageCount.end()) + if (auto coveringTestTargetIt = m_testTargetSourceCoverage.find(testTarget); + coveringTestTargetIt != m_testTargetSourceCoverage.end()) { - if (coveringTestTargetIt->second > 0) - { - coveringTestTargetIt->second--; - } + coveringTestTargetIt->second.erase(source); } } sourceDependency.m_coveringTestTargets.clear(); @@ -169,8 +166,8 @@ namespace TestImpact // Source to covering test target mapping sourceDependency.m_coveringTestTargets.insert(testTarget); - // Test target covering sources count - m_testTargetSourceCoverageCount[testTarget]++; + // Add the source to the test target covering sources map + m_testTargetSourceCoverage[testTarget].insert(source); // Build target to covering test target mapping for (const auto& parentTarget : sourceDependency.m_parentTargets) @@ -429,12 +426,33 @@ namespace TestImpact return ChangeDependencyList(AZStd::move(createDependencies), AZStd::move(updateDependencies), AZStd::move(deleteDependencies)); } + void DynamicDependencyMap::RemoveTestTargetFromSourceCoverage(const TestTarget* testTarget) + { + if (const auto& it = m_testTargetSourceCoverage.find(testTarget); + it != m_testTargetSourceCoverage.end()) + { + for (const auto& source : it->second) + { + const auto sourceDependency = m_sourceDependencyMap.find(source); + AZ_TestImpact_Eval( + sourceDependency != m_sourceDependencyMap.end(), + DependencyException, + AZStd::string::format("Test target '%s' has covering source '%s' yet cannot be found in the dependency map", + testTarget->GetName().c_str(), source.c_str())); + + sourceDependency->second.m_coveringTestTargets.erase(testTarget); + } + + m_testTargetSourceCoverage.erase(testTarget); + } + } + AZStd::vector DynamicDependencyMap::GetCoveringTests() const { AZStd::vector covering; - for (const auto& [testTarget, coveringSources] : m_testTargetSourceCoverageCount) + for (const auto& [testTarget, coveringSources] : m_testTargetSourceCoverage) { - if (coveringSources > 0) + if (!coveringSources.empty()) { covering.push_back(testTarget); } @@ -446,9 +464,9 @@ namespace TestImpact AZStd::vector DynamicDependencyMap::GetNotCoveringTests() const { AZStd::vector notCovering; - for(const auto& [testTarget, coveringSources] : m_testTargetSourceCoverageCount) + for(const auto& [testTarget, coveringSources] : m_testTargetSourceCoverage) { - if(coveringSources == 0) + if (coveringSources.empty()) { notCovering.push_back(testTarget); } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h index f209e22823..cd01a1a728 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h @@ -100,6 +100,9 @@ namespace TestImpact //! @returns The change list as resolved to the appropriate source dependencies. [[nodiscard]] ChangeDependencyList ApplyAndResoveChangeList(const ChangeList& changeList); + //! Removes the specified test target from all source coverage. + void RemoveTestTargetFromSourceCoverage(const TestTarget* testTarget); + //! Returns the test targets that cover one or more sources in the repository. AZStd::vector GetCoveringTests() const; @@ -120,13 +123,13 @@ namespace TestImpact //! The dependency map of sources to their parent build targets and covering test targets. AZStd::unordered_map m_sourceDependencyMap; + //! Map of all test targets and the sources they cover. + AZStd::unordered_map> m_testTargetSourceCoverage; + //! The map of build targets and their covering test targets. AZStd::unordered_map> m_buildTargetCoverage; //! Mapping of autogen input sources to their generated output sources. AZStd::unordered_map> m_autogenInputToOutputMap; - - //! Number of sources that each test target in the repository covers. - AZStd::unordered_map m_testTargetSourceCoverageCount; }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp index 2331c76880..44657dad21 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp @@ -70,11 +70,20 @@ namespace TestImpact }; } + template + AZStd::vector ConcatenateVectors(const AZStd::vector& v1, const AZStd::vector& v2) + { + AZStd::vector result; + result.reserve(v1.size() + v2.size()); + result.insert(result.end(), v1.begin(), v1.end()); + result.insert(result.end(), v2.begin(), v2.end()); + return result; + } Runtime::Runtime( RuntimeConfig&& config, SuiteType suiteFilter, Policy::ExecutionFailure executionFailurePolicy, - Policy::ExecutionFailureDrafting executionFailureDraftingPolicy, + Policy::FailedTestCoverage failedTestCoveragePolicy, Policy::TestFailure testFailurePolicy, Policy::IntegrityFailure integrationFailurePolicy, Policy::TestSharding testShardingPolicy, @@ -83,7 +92,7 @@ namespace TestImpact : m_config(AZStd::move(config)) , m_suiteFilter(suiteFilter) , m_executionFailurePolicy(executionFailurePolicy) - , m_executionFailureDraftingPolicy(executionFailureDraftingPolicy) + , m_failedTestCoveragePolicy(failedTestCoveragePolicy) , m_testFailurePolicy(testFailurePolicy) , m_integrationFailurePolicy(integrationFailurePolicy) , m_testShardingPolicy(testShardingPolicy) @@ -145,7 +154,7 @@ namespace TestImpact { AZ_Printf("TestImpactRuntime", AZStd::string::format( - "No test impact analysis data found for suite '%s' at %s", GetSuiteTypeName(m_suiteFilter).c_str(), m_sparTIAFile.c_str()).c_str()); + "No test impact analysis data found for suite '%s' at %s\n", GetSuiteTypeName(m_suiteFilter).c_str(), m_sparTIAFile.c_str()).c_str()); } } @@ -249,14 +258,28 @@ namespace TestImpact m_dynamicDependencyMap->ClearAllSourceCoverage(); } - void Runtime::UpdateAndSerializeDynamicDependencyMap(const SourceCoveringTestsList& sourceCoverageTestsList) + void Runtime::UpdateAndSerializeDynamicDependencyMap(const AZStd::vector& jobs) { + const auto sourceCoverageTestsList = CreateSourceCoveringTestFromTestCoverages(jobs, m_config.m_repo.m_root); if (!sourceCoverageTestsList.GetNumSources()) { return; } m_dynamicDependencyMap->ReplaceSourceCoverage(sourceCoverageTestsList); + + if (m_failedTestCoveragePolicy == Policy::FailedTestCoverage::Remove) + { + for (const auto& job : jobs) + { + if (job.GetTestResult() != Client::TestRunResult::AllTestsPass || + !job.GetTestCoverge().has_value()) + { + m_dynamicDependencyMap->RemoveTestTargetFromSourceCoverage(job.GetTestTarget()); + } + } + } + const auto sparTIA = m_dynamicDependencyMap->ExportSourceCoverage(); const auto sparTIAData = SerializeSourceCoveringTestsList(sparTIA); WriteFileContents(sparTIAData, m_sparTIAFile); @@ -323,11 +346,12 @@ namespace TestImpact AZStd::optional testCompleteCallback) { Timer timer; - AZStd::vector draftedTestTargets; + AZStd::vector draftedTestTargets = m_dynamicDependencyMap->GetNotCoveringTests(); auto [selectedTestTargets, discardedTestTargets] = SelectCoveringTestTargetsAndUpdateEnumerationCache(changeList, testPrioritizationPolicy); auto [includedSelectedTestTargets, excludedSelectedTestTargets] = SelectTestTargetsByExcludeList(selectedTestTargets); + AZStd::vector testTargetsToRun = ConcatenateVectors(includedSelectedTestTargets, draftedTestTargets); if (testSequenceStartCallback.has_value()) { (*testSequenceStartCallback)( @@ -336,10 +360,11 @@ namespace TestImpact ExtractTestTargetNames(draftedTestTargets)); } + if (dynamicDependencyMapPolicy == Policy::DynamicDependencyMap::Update) { const auto [result, testJobs] = m_testEngine->InstrumentedRun( - includedSelectedTestTargets, + testTargetsToRun, m_testShardingPolicy, m_executionFailurePolicy, Policy::IntegrityFailure::Continue, @@ -349,7 +374,7 @@ namespace TestImpact globalTimeout, TestRunCompleteCallbackHandler(testCompleteCallback)); - UpdateAndSerializeDynamicDependencyMap(CreateSourceCoveringTestFromTestCoverages(testJobs, m_config.m_repo.m_root)); + UpdateAndSerializeDynamicDependencyMap(testJobs); if (testSequenceEndCallback.has_value()) { @@ -361,7 +386,7 @@ namespace TestImpact else { const auto [result, testJobs] = m_testEngine->RegularRun( - includedSelectedTestTargets, + testTargetsToRun, m_testShardingPolicy, m_executionFailurePolicy, m_testFailurePolicy, @@ -389,12 +414,13 @@ namespace TestImpact AZStd::optional testCompleteCallback) { Timer timer; - AZStd::vector draftedTestTargets; + AZStd::vector draftedTestTargets = m_dynamicDependencyMap->GetNotCoveringTests(); auto [selectedTestTargets, discardedTestTargets] = SelectCoveringTestTargetsAndUpdateEnumerationCache(changeList, testPrioritizationPolicy); auto [includedSelectedTestTargets, excludedSelectedTestTargets] = SelectTestTargetsByExcludeList(selectedTestTargets); auto [includedDiscardedTestTargets, excludedDiscardedTestTargets] = SelectTestTargetsByExcludeList(discardedTestTargets); + AZStd::vector testTargetsToRun = ConcatenateVectors(includedSelectedTestTargets, draftedTestTargets); if (testSequenceStartCallback.has_value()) { (*testSequenceStartCallback)( @@ -403,9 +429,10 @@ namespace TestImpact ExtractTestTargetNames(draftedTestTargets)); } + // Impact analysis run of the selected test targets const auto [selectedResult, selectedTestJobs] = m_testEngine->InstrumentedRun( - includedSelectedTestTargets, + testTargetsToRun, m_testShardingPolicy, m_executionFailurePolicy, Policy::IntegrityFailure::Continue, @@ -433,7 +460,7 @@ namespace TestImpact globalTimeout, TestRunCompleteCallbackHandler(testCompleteCallback)); - UpdateAndSerializeDynamicDependencyMap(CreateSourceCoveringTestFromTestCoverages(selectedTestJobs, m_config.m_repo.m_root)); + UpdateAndSerializeDynamicDependencyMap(selectedTestJobs); if (testSequenceEndCallback.has_value()) { @@ -486,7 +513,7 @@ namespace TestImpact TestRunCompleteCallbackHandler(testCompleteCallback)); ClearDynamicDependencyMapAndRemoveExistingFile(); - UpdateAndSerializeDynamicDependencyMap(CreateSourceCoveringTestFromTestCoverages(testJobs, m_config.m_repo.m_root)); + UpdateAndSerializeDynamicDependencyMap(testJobs); if (testSequenceEndCallback.has_value()) { From 0da739ef18c41ec1ecb29cb563b69d676ef5d7cd Mon Sep 17 00:00:00 2001 From: jonawals Date: Mon, 7 Jun 2021 09:05:29 +0100 Subject: [PATCH 096/233] Add curiously missing comments --- .../Runtime/Code/Source/TestImpactRuntime.cpp | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp index 44657dad21..d83976b488 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp @@ -70,6 +70,7 @@ namespace TestImpact }; } + //! Utility for concatenating two vectors. template AZStd::vector ConcatenateVectors(const AZStd::vector& v1, const AZStd::vector& v2) { @@ -79,6 +80,7 @@ namespace TestImpact result.insert(result.end(), v2.begin(), v2.end()); return result; } + Runtime::Runtime( RuntimeConfig&& config, SuiteType suiteFilter, @@ -346,12 +348,20 @@ namespace TestImpact AZStd::optional testCompleteCallback) { Timer timer; + + // Draft in the test targets that have no coverage entries in the dynamic dependency map AZStd::vector draftedTestTargets = m_dynamicDependencyMap->GetNotCoveringTests(); + // The test targets that were selected for the change list by the dynamic dependency map and the test targets that were not auto [selectedTestTargets, discardedTestTargets] = SelectCoveringTestTargetsAndUpdateEnumerationCache(changeList, testPrioritizationPolicy); + + // The subset of selected test targets that are not on the configuration's exclude list and those that are auto [includedSelectedTestTargets, excludedSelectedTestTargets] = SelectTestTargetsByExcludeList(selectedTestTargets); + // We present to the client the included selected test targets and the drafted test targets as distinct sets but internally + // we consider the concatenated set of the two the actual set of tests to run AZStd::vector testTargetsToRun = ConcatenateVectors(includedSelectedTestTargets, draftedTestTargets); + if (testSequenceStartCallback.has_value()) { (*testSequenceStartCallback)( @@ -360,7 +370,6 @@ namespace TestImpact ExtractTestTargetNames(draftedTestTargets)); } - if (dynamicDependencyMapPolicy == Policy::DynamicDependencyMap::Update) { const auto [result, testJobs] = m_testEngine->InstrumentedRun( @@ -413,14 +422,24 @@ namespace TestImpact AZStd::optional testSequenceEndCallback, AZStd::optional testCompleteCallback) { - Timer timer; + Timer timer; + + // Draft in the test targets that have no coverage entries in the dynamic dependency map AZStd::vector draftedTestTargets = m_dynamicDependencyMap->GetNotCoveringTests(); + // The test targets that were selected for the change list by the dynamic dependency map and the test targets that were not auto [selectedTestTargets, discardedTestTargets] = SelectCoveringTestTargetsAndUpdateEnumerationCache(changeList, testPrioritizationPolicy); + + // The subset of selected test targets that are not on the configuration's exclude list and those that are auto [includedSelectedTestTargets, excludedSelectedTestTargets] = SelectTestTargetsByExcludeList(selectedTestTargets); + + // The subset of discarded test targets that are not on the configuration's exclude list and those that are auto [includedDiscardedTestTargets, excludedDiscardedTestTargets] = SelectTestTargetsByExcludeList(discardedTestTargets); + // We present to the client the included selected test targets and the drafted test targets as distinct sets but internally + // we consider the concatenated set of the two the actual set of tests to run AZStd::vector testTargetsToRun = ConcatenateVectors(includedSelectedTestTargets, draftedTestTargets); + if (testSequenceStartCallback.has_value()) { (*testSequenceStartCallback)( @@ -429,7 +448,6 @@ namespace TestImpact ExtractTestTargetNames(draftedTestTargets)); } - // Impact analysis run of the selected test targets const auto [selectedResult, selectedTestJobs] = m_testEngine->InstrumentedRun( testTargetsToRun, From d341dab7f54d866849a2c1f96190808e7e0f622b Mon Sep 17 00:00:00 2001 From: jonawals Date: Mon, 7 Jun 2021 10:39:58 +0100 Subject: [PATCH 097/233] Address PR comments --- .../Static/Code/Source/TestImpactCommandLineOptions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp index aad9319cf9..01005718bc 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp @@ -405,9 +405,9 @@ namespace TestImpact " tests are run regardless).\n" " -shard= Break any test targets with a sharding policy into the number of \n" " shards according to the maximum concurrency value.\n" - " -cpolicy= Policy for handling the coverage data of failed tests (both test that \n" + " -cpolicy= Policy for handling the coverage data of failed tests (both tests that \n" " failed to execute and tests that ran but failed), where remove will \n" - " remove the failed tests from the all coverage data(causing them to be \n" + " remove the failed tests from the all coverage data (causing them to be \n" " drafted into future test runs) and keep will keep any existing coverage \n" " data and update the coverage data for failed tests that produce coverage.\n" " -targetout= Capture of individual test run stdout, where stdout will capture \n" From a0feeec608c694c24367b129a61a74749525a32f Mon Sep 17 00:00:00 2001 From: jonawals Date: Mon, 7 Jun 2021 10:56:07 +0100 Subject: [PATCH 098/233] Add console output to runtime --- ...tImpactConsoleTestSequenceEventHandler.cpp | 234 ++++++++++++++++++ ...estImpactConsoleTestSequenceEventHandler.h | 72 ++++++ .../Code/Source/TestImpactConsoleUtils.cpp | 34 +++ .../Code/Source/TestImpactConsoleUtils.h | 56 +++++ 4 files changed, 396 insertions(+) create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.h create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.cpp create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.h diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp new file mode 100644 index 0000000000..8595fc0683 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp @@ -0,0 +1,234 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include + +#include + +namespace TestImpact +{ + namespace Console + { + namespace Output + { + void TestSuiteFilter(SuiteType filter) + { + std::cout << "Test suite filter: " << GetSuiteTypeName(filter).c_str() << "\n"; + } + + void ImpactAnalysisTestSelection(size_t numSelectedTests, size_t numDiscardedTests, size_t numExcludedTests, size_t numDraftedTests) + { + const float totalTests = numSelectedTests + numDiscardedTests; + const float saving = (1.0 - (numSelectedTests / totalTests)) * 100.0f; + + std::cout << numSelectedTests << " tests selected, " << numDiscardedTests << " tests discarded (" << saving << "% test saving)\n"; + std::cout << "Of which " << numExcludedTests << " tests have been excluded and " << numDraftedTests << " tests have been drafted.\n"; + } + + void FailureReport(const Client::SequenceFailure& failureReport, AZStd::chrono::milliseconds duration) + { + std::cout << "Sequence completed in " << (duration.count() / 1000.f) << "s with"; + + if (failureReport.GetExecutionFailures().size() || + failureReport.GetTestRunFailures().size() || + failureReport.GetTimedOutTests().size() || + failureReport.GetUnexecutedTests().size()) + { + std::cout << ":\n"; + std::cout << SetColor(Foreground::White, Background::Red).c_str() + << failureReport.GetTestRunFailures().size() + << ResetColor().c_str() << " test failures\n"; + + std::cout << SetColor(Foreground::White, Background::Red).c_str() + << failureReport.GetExecutionFailures().size() + << ResetColor().c_str() << " execution failures\n"; + + std::cout << SetColor(Foreground::White, Background::Red).c_str() + << failureReport.GetTimedOutTests().size() + << ResetColor().c_str() << " test timeouts\n"; + + std::cout << SetColor(Foreground::White, Background::Red).c_str() + << failureReport.GetUnexecutedTests().size() + << ResetColor().c_str() << " unexecuted tests\n"; + + if (!failureReport.GetTestRunFailures().empty()) + { + std::cout << "\nTest failures:\n"; + for (const auto& testRunFailure : failureReport.GetTestRunFailures()) + { + std::cout << " " << testRunFailure.GetTargetName().c_str(); + for (const auto& testCaseFailure : testRunFailure.GetTestCaseFailures()) + { + std::cout << "." << testCaseFailure.GetName().c_str(); + for (const auto& testFailure : testCaseFailure.GetTestFailures()) + { + std::cout << "." << testFailure.GetName().c_str() << "\n"; + } + } + } + } + + if (!failureReport.GetExecutionFailures().empty()) + { + std::cout << "\nExecution failures:\n"; + for (const auto& executionFailure : failureReport.GetExecutionFailures()) + { + std::cout << " " << executionFailure.GetTargetName().c_str() << "\n"; + std::cout << executionFailure.GetCommandString().c_str() << "\n"; + } + } + + if (!failureReport.GetTimedOutTests().empty()) + { + std::cout << "\nTimed out tests:\n"; + for (const auto& testTimeout : failureReport.GetTimedOutTests()) + { + std::cout << " " << testTimeout.GetTargetName().c_str() << "\n"; + } + } + + if (!failureReport.GetUnexecutedTests().empty()) + { + std::cout << "\nUnexecuted tests:\n"; + for (const auto& unexecutedTest : failureReport.GetUnexecutedTests()) + { + std::cout << " " << unexecutedTest.GetTargetName().c_str() << "\n"; + } + } + } + else + { + std::cout << SetColor(Foreground::White, Background::Green).c_str() << " \100% passes!\n" << ResetColor().c_str(); + } + } + } + + TestSequenceEventHandler::TestSequenceEventHandler(SuiteType suiteFilter) + : m_suiteFilter(suiteFilter) + { + } + + // TestSequenceStartCallback + void TestSequenceEventHandler::operator()(Client::TestRunSelection&& selectedTests) + { + ClearState(); + m_numTests = selectedTests.GetNumIncludedTestRuns(); + + Output::TestSuiteFilter(m_suiteFilter); + std::cout << selectedTests.GetNumIncludedTestRuns() << " tests selected, " << selectedTests.GetNumExcludedTestRuns() << " excluded.\n"; + } + + // ImpactAnalysisTestSequenceStartCallback + void TestSequenceEventHandler::operator()( + Client::TestRunSelection&& selectedTests, + AZStd::vector&& discardedTests, + AZStd::vector&& draftedTests) + { + ClearState(); + m_numTests = selectedTests.GetNumIncludedTestRuns() + draftedTests.size(); + + Output::TestSuiteFilter(m_suiteFilter); + Output::ImpactAnalysisTestSelection( + selectedTests.GetTotalNumTests(), discardedTests.size(), selectedTests.GetNumExcludedTestRuns(), draftedTests.size()); + } + + // SafeImpactAnalysisTestSequenceStartCallback + void TestSequenceEventHandler::operator()( + Client::TestRunSelection&& selectedTests, + Client::TestRunSelection&& discardedTests, + AZStd::vector&& draftedTests) + { + ClearState(); + m_numTests = selectedTests.GetNumIncludedTestRuns() + draftedTests.size(); + + Output::TestSuiteFilter(m_suiteFilter); + Output::ImpactAnalysisTestSelection( + selectedTests.GetTotalNumTests(), + discardedTests.GetTotalNumTests(), + selectedTests.GetNumExcludedTestRuns() + discardedTests.GetNumExcludedTestRuns(), + draftedTests.size()); + } + + // TestSequenceCompleteCallback + void TestSequenceEventHandler::operator()( + Client::SequenceFailure&& failureReport, + AZStd::chrono::milliseconds duration) + { + + Output::FailureReport(failureReport, duration); + std::cout << "Updating and serializing the test impact analysis data, this may take a moment...\n"; + } + + // SafeTestSequenceCompleteCallback + void TestSequenceEventHandler::operator()( + Client::SequenceFailure&& selectedFailureReport, + Client::SequenceFailure&& discardedFailureReport, + AZStd::chrono::milliseconds selectedDuration, + AZStd::chrono::milliseconds discaredDuration) + { + std::cout << "Selected test run:\n"; + Output::FailureReport(selectedFailureReport, selectedDuration); + + std::cout << "Discarded test run:\n"; + Output::FailureReport(discardedFailureReport, discaredDuration); + + std::cout << "Updating and serializing the test impact analysis data, this may take a moment...\n"; + } + + // TestRunCompleteCallback + void TestSequenceEventHandler::operator()([[maybe_unused]] Client::TestRun&& test) + { + m_numTestsComplete++; + const auto progress = AZStd::string::format("(%03u/%03u)", m_numTestsComplete, m_numTests, test.GetTargetName().c_str()); + + AZStd::string result; + switch (test.GetResult()) + { + case Client::TestRunResult::AllTestsPass: + { + result = SetColorForString(Foreground::White, Background::Green, "PASS"); + break; + } + case Client::TestRunResult::FailedToExecute: + { + result = SetColorForString(Foreground::White, Background::Red, "EXEC"); + break; + } + case Client::TestRunResult::NotRun: + { + result = SetColorForString(Foreground::White, Background::Yellow, "SKIP"); + break; + } + case Client::TestRunResult::TestFailures: + { + result = SetColorForString(Foreground::White, Background::Red, "FAIL"); + break; + } + case Client::TestRunResult::Timeout: + { + result = SetColorForString(Foreground::White, Background::Magenta, "TIME"); + break; + } + } + + std::cout << progress.c_str() << " " << result.c_str() << " " << test.GetTargetName().c_str() << " (" << (test.GetDuration().count() / 1000.f) << "s)\n"; + } + + void TestSequenceEventHandler::ClearState() + { + m_numTests = 0; + m_numTestsComplete = 0; + } + } // namespace Console +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.h new file mode 100644 index 0000000000..01f59b2c64 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.h @@ -0,0 +1,72 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#pragma once + +namespace TestImpact +{ + namespace Console + { + //! Event handler for all test sequence types. + class TestSequenceEventHandler + { + public: + TestSequenceEventHandler(SuiteType suiteFilter); + + //! TestSequenceStartCallback. + void operator()(Client::TestRunSelection&& selectedTests); + + //! ImpactAnalysisTestSequenceStartCallback. + void operator()( + Client::TestRunSelection&& selectedTests, + AZStd::vector&& discardedTests, + AZStd::vector&& draftedTests); + + //! SafeImpactAnalysisTestSequenceStartCallback. + void operator()( + Client::TestRunSelection&& selectedTests, + Client::TestRunSelection&& discardedTests, + AZStd::vector&& draftedTests); + + //! TestSequenceCompleteCallback. + void operator()( + Client::SequenceFailure&& failureReport, + AZStd::chrono::milliseconds duration); + + //! SafeTestSequenceCompleteCallback. + void operator()( + Client::SequenceFailure&& selectedFailureReport, + Client::SequenceFailure&& discardedFailureReport, + AZStd::chrono::milliseconds selectedDuration, + AZStd::chrono::milliseconds discaredDuration); + + //! TestRunCompleteCallback. + void operator()(Client::TestRun&& test); + + private: + void ClearState(); + + SuiteType m_suiteFilter; + size_t m_numTests = 0; + size_t m_numTestsComplete = 0; + }; + } // namespace Console +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.cpp new file mode 100644 index 0000000000..e00a766eb5 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.cpp @@ -0,0 +1,34 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +namespace TestImpact +{ + namespace Console + { + AZStd::string SetColor(Foreground fgd, Background bgd) + { + return AZStd::string::format("\033[%u;%um", static_cast(fgd), static_cast(bgd)); + } + + AZStd::string SetColorForString(Foreground fgd, Background bgd, const AZStd::string& str) + { + return AZStd::string::format("%s%s%s", SetColor(fgd, bgd).c_str(), str.c_str(), ResetColor().c_str()); + } + + AZStd::string ResetColor() + { + return "\033[0m"; + } + } // namespace Console +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.h new file mode 100644 index 0000000000..d0fc2495d3 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.h @@ -0,0 +1,56 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + namespace Console + { + //! The set of available foreground colors. + enum class Foreground + { + Black = 30, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + White + }; + + //! The set of available background colors. + enum class Background + { + Black = 40, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + White + }; + + //! Returns a string to be used to set the specified foreground and background color. + AZStd::string SetColor(Foreground fgd, Background bgd); + + //! Returns a string with the specified string set to the specified foreground and background color followed by a color reset. + AZStd::string SetColorForString(Foreground fgd, Background bgd, const AZStd::string& str); + + //! Returns a string to be used to reset the color back to white foreground on black background. + AZStd::string ResetColor(); + } // namespace Console +} // namespace TestImpact From 796b5be286a713d9e42f2363a3cdfcf0bdf5dafd Mon Sep 17 00:00:00 2001 From: jonawals Date: Mon, 7 Jun 2021 15:32:17 +0100 Subject: [PATCH 099/233] Address PR comments --- .../Source/TestImpactConsoleTestSequenceEventHandler.cpp | 8 ++++---- .../Source/TestImpactConsoleTestSequenceEventHandler.h | 2 +- .../Console/Static/Code/Source/TestImpactConsoleUtils.cpp | 8 ++++---- .../Console/Static/Code/Source/TestImpactConsoleUtils.h | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp index 8595fc0683..0a745514a5 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp @@ -40,10 +40,10 @@ namespace TestImpact { std::cout << "Sequence completed in " << (duration.count() / 1000.f) << "s with"; - if (failureReport.GetExecutionFailures().size() || - failureReport.GetTestRunFailures().size() || - failureReport.GetTimedOutTests().size() || - failureReport.GetUnexecutedTests().size()) + if (!failureReport.GetExecutionFailures().empty() || + !failureReport.GetTestRunFailures().empty() || + !failureReport.GetTimedOutTests().empty() || + !failureReport.GetUnexecutedTests().empty()) { std::cout << ":\n"; std::cout << SetColor(Foreground::White, Background::Red).c_str() diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.h index 01f59b2c64..ee5eee0bf8 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.h +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.h @@ -29,7 +29,7 @@ namespace TestImpact class TestSequenceEventHandler { public: - TestSequenceEventHandler(SuiteType suiteFilter); + explicit TestSequenceEventHandler(SuiteType suiteFilter); //! TestSequenceStartCallback. void operator()(Client::TestRunSelection&& selectedTests); diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.cpp index e00a766eb5..76ce830371 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.cpp @@ -16,14 +16,14 @@ namespace TestImpact { namespace Console { - AZStd::string SetColor(Foreground fgd, Background bgd) + AZStd::string SetColor(Foreground foreground, Background background) { - return AZStd::string::format("\033[%u;%um", static_cast(fgd), static_cast(bgd)); + return AZStd::string::format("\033[%u;%um", aznumeric_cast(foreground), aznumeric_cast(background)); } - AZStd::string SetColorForString(Foreground fgd, Background bgd, const AZStd::string& str) + AZStd::string SetColorForString(Foreground foreground, Background background, const AZStd::string& str) { - return AZStd::string::format("%s%s%s", SetColor(fgd, bgd).c_str(), str.c_str(), ResetColor().c_str()); + return AZStd::string::format("%s%s%s", SetColor(foreground, background).c_str(), str.c_str(), ResetColor().c_str()); } AZStd::string ResetColor() diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.h b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.h index d0fc2495d3..a003e2db26 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.h +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.h @@ -45,10 +45,10 @@ namespace TestImpact }; //! Returns a string to be used to set the specified foreground and background color. - AZStd::string SetColor(Foreground fgd, Background bgd); + AZStd::string SetColor(Foreground foreground, Background background); //! Returns a string with the specified string set to the specified foreground and background color followed by a color reset. - AZStd::string SetColorForString(Foreground fgd, Background bgd, const AZStd::string& str); + AZStd::string SetColorForString(Foreground foreground, Background background, const AZStd::string& str); //! Returns a string to be used to reset the color back to white foreground on black background. AZStd::string ResetColor(); From cc9e8a72e447a65dd67f9b252a79dacfcce7b9c6 Mon Sep 17 00:00:00 2001 From: jonawals Date: Mon, 7 Jun 2021 17:45:48 +0100 Subject: [PATCH 100/233] Fix coverage clear bug and other minor refactors --- .../Source/TestImpactCommandLineOptions.cpp | 16 ++-- .../TestImpactFramework/TestImpactRuntime.h | 7 +- .../TestImpactTestSequence.h | 2 +- .../TestImpactDynamicDependencyMap.cpp | 39 ++++++-- .../TestImpactDynamicDependencyMap.h | 9 +- .../Enumeration/TestImpactTestEnumerator.cpp | 2 +- .../Run/TestImpactInstrumentedTestRunner.cpp | 2 +- .../TestEngine/Run/TestImpactTestRunner.cpp | 2 +- .../Runtime/Code/Source/TestImpactRuntime.cpp | 89 ++++++++++++++----- .../Code/Source/TestImpactRuntimeUtils.cpp | 52 +---------- .../Code/Source/TestImpactRuntimeUtils.h | 7 +- 11 files changed, 131 insertions(+), 96 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp index 01005718bc..992b466a54 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp @@ -51,7 +51,7 @@ namespace TestImpact Ignore, StdOut, File, - Remove, + Discard, Keep }; @@ -87,7 +87,7 @@ namespace TestImpact "ignore", "stdout", "file", - "remove", + "discard", "keep" }; @@ -147,7 +147,7 @@ namespace TestImpact { const AZStd::vector> states = { - {OptionKeys[Remove], Policy::FailedTestCoverage::Remove}, + {OptionKeys[Discard], Policy::FailedTestCoverage::Discard}, {OptionKeys[Keep], Policy::FailedTestCoverage::Keep} }; @@ -405,11 +405,11 @@ namespace TestImpact " tests are run regardless).\n" " -shard= Break any test targets with a sharding policy into the number of \n" " shards according to the maximum concurrency value.\n" - " -cpolicy= Policy for handling the coverage data of failed tests (both tests that \n" - " failed to execute and tests that ran but failed), where remove will \n" - " remove the failed tests from the all coverage data (causing them to be \n" - " drafted into future test runs) and keep will keep any existing coverage \n" - " data and update the coverage data for failed tests that produce coverage.\n" + " -cpolicy= Policy for handling the coverage data of failing tests, where discard \n" + " will discard the coverage data produced by the failing tests, causing \n" + " them to be drafted into future test runs and keep will keep any existing \n" + " coverage data and update the coverage data for failed tests that produce \n" + " coverage.\n" " -targetout= Capture of individual test run stdout, where stdout will capture \n" " each individual test target's stdout and output each one to stdout \n" " and file will capture each individual test target's stdout and output \n" diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h index 86f2907ab7..2a7720ee55 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h @@ -81,7 +81,8 @@ namespace TestImpact using SafeTestSequenceCompleteCallback = AZStd::function; + AZStd::chrono::milliseconds selectedDuration, + AZStd::chrono::milliseconds discardedDuration)>; //! Callback for test runs that have completed for any reason. //! @param selectedTests The test that has completed. @@ -201,6 +202,10 @@ namespace TestImpact AZStd::pair, AZStd::vector> SelectTestTargetsByExcludeList( AZStd::vector testTargets) const; + //! Prunes the existing coverage for the specified jobs and creates the consolidates source covering tests list from the + //! test engine instrumented run jobs. + SourceCoveringTestsList CreateSourceCoveringTestFromTestCoverages(const AZStd::vector& jobs); + //! Prepares the dynamic dependency map for a seed update by clearing all existing data and deleting the file that will be serialized. void ClearDynamicDependencyMapAndRemoveExistingFile(); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h index 5f9bff952f..5a5a6196e2 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactTestSequence.h @@ -34,7 +34,7 @@ namespace TestImpact //! Policy for handling the coverage data of failed tests targets (both test that failed to execute and tests that ran but failed). enum class FailedTestCoverage { - Remove, //!< Remove the failed test targets from the all coverage data (causing them to be drafted into future test runs). + Discard, //!< Discard the coverage data produced by the failing tests, causing them to be drafted into future test runs. Keep //!< Keep any existing coverage data and update the coverage data for failed test targetss that produce coverage. }; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp index 22bcc97c2a..28b817e32c 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp @@ -133,8 +133,9 @@ namespace TestImpact return buildTarget; } - void DynamicDependencyMap::ReplaceSourceCoverage(const SourceCoveringTestsList& sourceCoverageDelta) + void DynamicDependencyMap::ReplaceSourceCoverageInternal(const SourceCoveringTestsList& sourceCoverageDelta, bool pruneIfNoParentsOrCoverage) { + AZStd::vector killList; for (const auto& sourceCoverage : sourceCoverageDelta.GetCoverage()) { // Autogen input files are not compiled sources and thus supplying coverage data for them makes no sense @@ -146,7 +147,12 @@ namespace TestImpact auto [sourceDependencyIt, inserted] = m_sourceDependencyMap.insert(sourceCoverage.GetPath().String()); auto& [source, sourceDependency] = *sourceDependencyIt; - // Remove the source from the test target covering sources map and clear any existing coverage for the delta + // Before we can replace the coverage for this source dependency, we must: + // 1. Remove the source from the test target covering sources map + // 2. Prune the covered targets for the parent test target(s) of this source dependency + // 3. Clear any existing coverage for the delta + + // 1. for (const auto& testTarget : sourceDependency.m_coveringTestTargets) { if (auto coveringTestTargetIt = m_testTargetSourceCoverage.find(testTarget); @@ -154,7 +160,20 @@ namespace TestImpact { coveringTestTargetIt->second.erase(source); } + + } + + // 2. + // This step is prohibitively expensive as it requires iterating over all of the sources of the build targets covered by + // the parent test targets of this source dependency to ensure that this is in fact the last source being cleared and thus + // it can be determined that the parent test target is no longer covering the given build target + // + // The implications of this are that multiple calls to the test selector and priritizor's SelectTestTargets method will end + // up pulling in more test targets than needed for newly-created production sources until the next time the dynamic dependency + // map reconstructed, however until this use case materializes the implications described will not be addressed + + // 3. sourceDependency.m_coveringTestTargets.clear(); // Update the dependency with any new coverage data @@ -183,13 +202,18 @@ namespace TestImpact } // If the new coverage data results in a parentless and coverageless entry, consider it a dead entry and remove accordingly - if (sourceDependency.m_coveringTestTargets.empty() && sourceDependency.m_parentTargets.empty()) + if (sourceDependency.m_coveringTestTargets.empty() && sourceDependency.m_parentTargets.empty() && pruneIfNoParentsOrCoverage) { m_sourceDependencyMap.erase(sourceDependencyIt); } } } + void DynamicDependencyMap::ReplaceSourceCoverage(const SourceCoveringTestsList& sourceCoverageDelta) + { + ReplaceSourceCoverageInternal(sourceCoverageDelta, true); + } + void DynamicDependencyMap::ClearSourceCoverage(const AZStd::vector& paths) { for (const auto& path : paths) @@ -212,9 +236,14 @@ namespace TestImpact void DynamicDependencyMap::ClearAllSourceCoverage() { - for (const auto& [path, coverage] : m_sourceDependencyMap) + for (auto it = m_sourceDependencyMap.begin(); it != m_sourceDependencyMap.end(); ++it) { - ReplaceSourceCoverage(SourceCoveringTestsList(AZStd::vector{ SourceCoveringTests(RepoPath(path)) })); + const auto& [path, coverage] = *it; + ReplaceSourceCoverageInternal(SourceCoveringTestsList(AZStd::vector{ SourceCoveringTests(RepoPath(path)) }), false); + if (coverage.m_coveringTestTargets.empty() && coverage.m_parentTargets.empty()) + { + it = m_sourceDependencyMap.erase(it); + } } } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h index cd01a1a728..701f69e68d 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h @@ -81,7 +81,6 @@ namespace TestImpact SourceDependency GetSourceDependencyOrThrow(const RepoPath& path) const; //! Replaces the source coverage of the specified sources with the specified source coverage. - //! @note The covering targets for the parent test target(s) will not be pruned if those covering targets are removed. //! @param sourceCoverageDelta The source coverage delta to replace in the dependency map. void ReplaceSourceCoverage(const SourceCoveringTestsList& sourceCoverageDelta); @@ -110,6 +109,13 @@ namespace TestImpact AZStd::vector GetNotCoveringTests() const; private: + //! Internal handler for ReplaceSourceCoverage where the pruning of parentless and coverageless source depenencies after the + //! source coverage has been replaced must be explicitly stated. + //! @note The covered targets for the source dependency's parent test target(s) will not be pruned if those covering targets are removed. + //! @param sourceCoverageDelta The source coverage delta to replace in the dependency map. + //! @param pruneIfNoParentsOrCoverage Flag to specify whether or not newly parentless and coverageless dependencies will be removed. + void ReplaceSourceCoverageInternal(const SourceCoveringTestsList& sourceCoverageDelta, bool pruneIfNoParentsOrCoverage); + //! Clears the source coverage of the specified sources. //! @note The covering targets for the parent test target(s) will not be pruned if those covering targets are removed. void ClearSourceCoverage(const AZStd::vector& paths); @@ -127,6 +133,7 @@ namespace TestImpact AZStd::unordered_map> m_testTargetSourceCoverage; //! The map of build targets and their covering test targets. + //! @note As per the note for ReplaceSourceCoverageInternal, this map is currently not pruned when source coverage is replaced. AZStd::unordered_map> m_buildTargetCoverage; //! Mapping of autogen input sources to their generated output sources. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp index 58ffb85cb3..17784dd937 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Enumeration/TestImpactTestEnumerator.cpp @@ -73,7 +73,7 @@ namespace TestImpact } catch (const TestEngineException& e) { - AZ_Printf("Enumerate", "Enumeration cache error: %s", e.what()); + AZ_Printf("Enumerate", AZStd::string::format("Enumeration cache error: %s\n", e.what()).c_str()); DeleteFile(jobInfo->GetCache()->m_file); } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp index aa8ff04657..5a0ef74472 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactInstrumentedTestRunner.cpp @@ -72,7 +72,7 @@ namespace TestImpact } catch (const Exception& e) { - AZ_Printf("RunInstrumentedTests", e.what()); + AZ_Printf("RunInstrumentedTests", AZStd::string::format("%s\n", e.what()).c_str()); runs[jobId] = AZStd::nullopt; } } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp index 7ac13f85b2..16717a1fe5 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/Run/TestImpactTestRunner.cpp @@ -46,7 +46,7 @@ namespace TestImpact } catch (const Exception& e) { - AZ_Printf("RunTests", e.what()); + AZ_Printf("RunTests", AZStd::string::format("%s\n", e.what()).c_str()); runs[jobId] = AZStd::nullopt; } } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp index d83976b488..79f5269188 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp @@ -256,32 +256,77 @@ namespace TestImpact void Runtime::ClearDynamicDependencyMapAndRemoveExistingFile() { - DeleteFile(m_sparTIAFile); m_dynamicDependencyMap->ClearAllSourceCoverage(); + DeleteFile(m_sparTIAFile); } - void Runtime::UpdateAndSerializeDynamicDependencyMap(const AZStd::vector& jobs) + SourceCoveringTestsList Runtime::CreateSourceCoveringTestFromTestCoverages(const AZStd::vector& jobs) { - const auto sourceCoverageTestsList = CreateSourceCoveringTestFromTestCoverages(jobs, m_config.m_repo.m_root); - if (!sourceCoverageTestsList.GetNumSources()) + AZStd::unordered_map> coverage; + for (const auto& job : jobs) { - return; - } + // First we must remove any existing coverage for the test target so as to not end up with source remnants from previous + // coverage that is no longer covered by this revision of the test target + m_dynamicDependencyMap->RemoveTestTargetFromSourceCoverage(job.GetTestTarget()); + + // Next we will update the coverage of test targets that completed (with or without failures), unless the failed test coverage + // policy dictates we should instead discard the coverage of test targets with failing tests + if (const auto testResult = job.GetTestResult(); + testResult == Client::TestRunResult::AllTestsPass || + (m_failedTestCoveragePolicy == Policy::FailedTestCoverage::Keep && testResult == Client::TestRunResult::TestFailures)) + { + if (testResult == Client::TestRunResult::AllTestsPass) + { + // Passing tests should have coverage data, otherwise something is very wrong + AZ_TestImpact_Eval( + job.GetTestCoverge().has_value(), + RuntimeException, + AZStd::string::format( + "Test target '%s' completed its test run successfully but produced no coverage data", + job.GetTestTarget()->GetName().c_str())); + } + else if (!job.GetTestCoverge().has_value()) + { + // When a test run completes with failing tests but produces no coverage artifact that's typically a sign of the + // test aborting due to an unhandled exception, in which case ignore it and let it be picked up in the failure report + continue; + } - m_dynamicDependencyMap->ReplaceSourceCoverage(sourceCoverageTestsList); + for (const auto& source : job.GetTestCoverge().value().GetSourcesCovered()) + { + coverage[source.String()].insert(job.GetTestTarget()->GetName()); + } + } + } - if (m_failedTestCoveragePolicy == Policy::FailedTestCoverage::Remove) + AZStd::vector sourceCoveringTests; + sourceCoveringTests.reserve(coverage.size()); + for (auto&& [source, testTargets] : coverage) { - for (const auto& job : jobs) + if (const auto sourcePath = RepoPath(source); + sourcePath.IsRelativeTo(m_config.m_repo.m_root)) { - if (job.GetTestResult() != Client::TestRunResult::AllTestsPass || - !job.GetTestCoverge().has_value()) - { - m_dynamicDependencyMap->RemoveTestTargetFromSourceCoverage(job.GetTestTarget()); - } + sourceCoveringTests.push_back( + SourceCoveringTests(RepoPath(sourcePath.LexicallyRelative(m_config.m_repo.m_root)), AZStd::move(testTargets))); + } + else + { + AZ_Warning("TestImpact", false, "Ignoring source, source it outside of repo: '%s'", sourcePath.c_str()); } } + return SourceCoveringTestsList(AZStd::move(sourceCoveringTests)); + } + + void Runtime::UpdateAndSerializeDynamicDependencyMap(const AZStd::vector& jobs) + { + const auto sourceCoverageTestsList = CreateSourceCoveringTestFromTestCoverages(jobs); + if (!sourceCoverageTestsList.GetNumSources()) + { + return; + } + + m_dynamicDependencyMap->ReplaceSourceCoverage(sourceCoverageTestsList); const auto sparTIA = m_dynamicDependencyMap->ExportSourceCoverage(); const auto sparTIAData = SerializeSourceCoveringTestsList(sparTIA); WriteFileContents(sparTIAData, m_sparTIAFile); @@ -422,7 +467,7 @@ namespace TestImpact AZStd::optional testSequenceEndCallback, AZStd::optional testCompleteCallback) { - Timer timer; + Timer timer; // Draft in the test targets that have no coverage entries in the dynamic dependency map AZStd::vector draftedTestTargets = m_dynamicDependencyMap->GetNotCoveringTests(); @@ -459,6 +504,8 @@ namespace TestImpact testTargetTimeout, globalTimeout, TestRunCompleteCallbackHandler(testCompleteCallback)); + + const auto selectedDuraton = timer.Elapsed(); // Carry the remaining global sequence time over to the discarded test run if (globalTimeout.has_value()) @@ -478,16 +525,18 @@ namespace TestImpact globalTimeout, TestRunCompleteCallbackHandler(testCompleteCallback)); - UpdateAndSerializeDynamicDependencyMap(selectedTestJobs); + const auto discardedDuraton = timer.Elapsed(); if (testSequenceEndCallback.has_value()) { (*testSequenceEndCallback)( GenerateSequenceFailureReport(selectedTestJobs), GenerateSequenceFailureReport(discardedTestJobs), - timer.Elapsed()); + selectedDuraton, + discardedDuraton); } + UpdateAndSerializeDynamicDependencyMap(selectedTestJobs); return { selectedResult, discardedResult }; } @@ -530,14 +579,14 @@ namespace TestImpact globalTimeout, TestRunCompleteCallbackHandler(testCompleteCallback)); - ClearDynamicDependencyMapAndRemoveExistingFile(); - UpdateAndSerializeDynamicDependencyMap(testJobs); - if (testSequenceEndCallback.has_value()) { (*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed()); } + ClearDynamicDependencyMapAndRemoveExistingFile(); + UpdateAndSerializeDynamicDependencyMap(testJobs); + return result; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp index 47c0e83233..68482ab721 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.cpp @@ -82,54 +82,4 @@ namespace TestImpact return testNames; } - - SourceCoveringTestsList CreateSourceCoveringTestFromTestCoverages(const AZStd::vector& jobs, const RepoPath& root) - { - AZStd::unordered_map> coverage; - for (const auto& job : jobs) - { - if (const auto testResult = job.GetTestResult(); - testResult == Client::TestRunResult::AllTestsPass || testResult == Client::TestRunResult::TestFailures) - { - if (testResult == Client::TestRunResult::AllTestsPass) - { - // Passing tests should have coverage data, otherwise something is very wrong - AZ_TestImpact_Eval( - job.GetTestCoverge().has_value(), - RuntimeException, - AZStd::string::format( - "Test target '%s' completed its test run successfully but produced no coverage data", - job.GetTestTarget()->GetName().c_str())); - } - else if (!job.GetTestCoverge().has_value()) - { - // When a test run completes with failing tests but produces no coverage artifact that's typically a sign of the - // test aborting due to an unhandled exception, in which case ignore it and let it be picked up in the failure report - continue; - } - - for (const auto& source : job.GetTestCoverge().value().GetSourcesCovered()) - { - coverage[source.String()].insert(job.GetTestTarget()->GetName()); - } - } - } - - AZStd::vector sourceCoveringTests; - sourceCoveringTests.reserve(coverage.size()); - for (auto&& [source, testTargets] : coverage) - { - if (const auto sourcePath = RepoPath(source); - sourcePath.IsRelativeTo(root)) - { - sourceCoveringTests.push_back(SourceCoveringTests(RepoPath(sourcePath.LexicallyRelative(root)), AZStd::move(testTargets))); - } - else - { - AZ_Warning("TestImpact", false, "Ignoring source, source it outside of repo: %s", sourcePath.c_str()); - } - } - - return SourceCoveringTestsList(AZStd::move(sourceCoveringTests)); - } -} +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h index 943b0cbec4..807911d854 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntimeUtils.h @@ -40,12 +40,7 @@ namespace TestImpact const AZStd::vector& excludedTestTargets); //! Extracts the name information from the specified test targets. - AZStd::vector ExtractTestTargetNames(const AZStd::vector testTargets); - - //! Creates the consolidates source covering tests list from the test engine instrumented run jobs. - SourceCoveringTestsList CreateSourceCoveringTestFromTestCoverages( - const AZStd::vector& jobs, - const RepoPath& root); + AZStd::vector ExtractTestTargetNames(const AZStd::vector testTargets); //! Generates a test run failure report from the specified test engine job information. //! @tparam TestJob The test engine job type. From f603b08d0542983c18c1b76cbed72aae88851f7f Mon Sep 17 00:00:00 2001 From: jonawals Date: Mon, 7 Jun 2021 18:37:20 +0100 Subject: [PATCH 101/233] Address PR comments --- .../Runtime/Code/Source/TestImpactRuntime.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp index 79f5269188..ef4bf5cd03 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp @@ -271,9 +271,15 @@ namespace TestImpact // Next we will update the coverage of test targets that completed (with or without failures), unless the failed test coverage // policy dictates we should instead discard the coverage of test targets with failing tests - if (const auto testResult = job.GetTestResult(); - testResult == Client::TestRunResult::AllTestsPass || - (m_failedTestCoveragePolicy == Policy::FailedTestCoverage::Keep && testResult == Client::TestRunResult::TestFailures)) + const auto testResult = job.GetTestResult(); + + if (m_failedTestCoveragePolicy == Policy::FailedTestCoverage::Discard && testResult == Client::TestRunResult::TestFailures) + { + // Discard the coverage for this job + continue; + } + + if (testResult == Client::TestRunResult::AllTestsPass || testResult == Client::TestRunResult::TestFailures) { if (testResult == Client::TestRunResult::AllTestsPass) { From a56591fa126862e58ec6ab5b384282d2acaea538 Mon Sep 17 00:00:00 2001 From: jonawals Date: Mon, 7 Jun 2021 18:38:54 +0100 Subject: [PATCH 102/233] Address PR comments --- .../Runtime/Code/Source/TestImpactRuntime.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp index ef4bf5cd03..784a46ffa0 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp @@ -291,7 +291,8 @@ namespace TestImpact "Test target '%s' completed its test run successfully but produced no coverage data", job.GetTestTarget()->GetName().c_str())); } - else if (!job.GetTestCoverge().has_value()) + + if (!job.GetTestCoverge().has_value()) { // When a test run completes with failing tests but produces no coverage artifact that's typically a sign of the // test aborting due to an unhandled exception, in which case ignore it and let it be picked up in the failure report From e0f953cf6bf787b979c59bf5607978b91c504c11 Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 11:59:06 +0100 Subject: [PATCH 103/233] Address PR comments --- .../Source/TestImpactCommandLineOptions.cpp | 133 +++++++++--------- .../TestImpactFramework/TestImpactRuntime.h | 2 +- 2 files changed, 69 insertions(+), 66 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp index 992b466a54..0a4c80afa4 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp @@ -381,71 +381,74 @@ namespace TestImpact AZStd::string help = "usage: tiaf [options]\n" " options:\n" - " -config= Path to the configuration file for the TIAF runtime (default: \n" - " ..json).\n" - " -changelist= Path to the JSON of source file changes to perform test impact \n" - " analysis on.\n" - " -gtimeout= Global timeout value to terminate the entire test sequence should it \n" - " be exceeded.\n" - " -ttimeout= Timeout value to terminate individual test targets should it be \n" - " exceeded.\n" - " -sequence= The type of test sequence to perform, where none runs no tests and\n" - " will report a all tests successful, seed removes any prior coverage \n" - " data and runs all test targets with instrumentation to reseed the \n" - " data from scratch, regular runs all of the test targets without any \n" - " instrumentation to generate coverage data(any prior coverage data is \n" - " left intact), tia uses any prior coverage data to run the instrumented \n" - " subset of selected tests(if no prior coverage data a regular run is \n" - " performed instead) and tiaorseed uses any prior coverage data to run \n" - " the instrumented subset of selected tests(if no prior coverage data a \n" - " seed run is performed instead).\n" - " -safemode= Flag to specify a safe mode sequence where the set of unselected \n" - " tests is run without instrumentation after the set of selected \n" - " instrumented tests is run (this has the effect of ensuring all \n" - " tests are run regardless).\n" - " -shard= Break any test targets with a sharding policy into the number of \n" - " shards according to the maximum concurrency value.\n" - " -cpolicy= Policy for handling the coverage data of failing tests, where discard \n" - " will discard the coverage data produced by the failing tests, causing \n" - " them to be drafted into future test runs and keep will keep any existing \n" - " coverage data and update the coverage data for failed tests that produce \n" - " coverage.\n" - " -targetout= Capture of individual test run stdout, where stdout will capture \n" - " each individual test target's stdout and output each one to stdout \n" - " and file will capture each individual test target's stdout and output \n" - " each one individually to a file (multiple values are accepted).\n" - " -epolicy= Policy for handling test execution failure (test targets could not be \n" - " launched due to the binary not being built, incorrect paths, etc.), \n" - " where abort will abort the entire test sequence upon the first test\n" - " target execution failure and report a failure(along with the return \n" - " code of the test target that failed to launch), continue will continue \n" - " with the test sequence in the event of test target execution failures\n" - " and treat the test targets that failed to launch as as test failures\n" - " (along with the return codes of the test targets that failed to \n" - " launch), ignore will continue with the test sequence in the event of \n" - " test target execution failures and treat the test targets that failed\n" - " to launch as test passes(along with the return codes of the test \n" - " targets that failed to launch).\n" - " -fpolicy Policy for handling test failures (test targets report failing tests), \n" - " where abort will abort the entire test sequence upon the first test \n" - " failure and report a failure and continue will continue with the test\n" - " sequence in the event of test failures and report the test failures.\n" - " -ipolicy= Policy for handling coverage data integrity failures, where abort will \n" - " abort the test sequenceand report a failure, seed will attempt another \n" - " sequence using the seed sequence type, otherwise will abort and report \n" - " a failure (this option has no effect for regular and seed sequence \n" - " types) and rerun will attempt another sequence using the regular \n" - " sequence type, otherwise will abort and report a failure(this option has \n" - " no effect for regular sequence type).\n" - " -ppolicy= Policy for prioritizing selected test targets, where none will not \n" - " attempt any test target prioritization and locality will attempt to \n" - " prioritize test targets according to the locality of their covering \n" - " production targets in the dependency graph(if no dependency graph data \n" - " available, no prioritization will occur).\n" - " -maxconcurrency= The maximum number of concurrent test targets/shards to be in flight at \n" - " any given moment.\n" - " -ochangelist= Outputs the change list used for test selection.\n" - " -suite= The test suite to select from for this test sequence."; + " -config= Path to the configuration file for the TIAF runtime (default: \n" + " ..json).\n" + " -changelist= Path to the JSON of source file changes to perform test impact \n" + " analysis on.\n" + " -gtimeout= Global timeout value to terminate the entire test sequence should it \n" + " be exceeded.\n" + " -ttimeout= Timeout value to terminate individual test targets should it be \n" + " exceeded.\n" + " -sequence= The type of test sequence to perform, where 'none' runs no tests and\n" + " will report a all tests successful, 'seed' removes any prior coverage \n" + " data and runs all test targets with instrumentation to reseed the \n" + " data from scratch, 'regular' runs all of the test targets without any \n" + " instrumentation to generate coverage data(any prior coverage data is \n" + " left intact), 'tia' uses any prior coverage data to run the instrumented \n" + " subset of selected tests(if no prior coverage data a regular run is \n" + " performed instead), 'tianowrite' uses any prior coverage data to run the \n" + " uninstrumented subset of selected tests (if no prior coverage data a \n" + " regular run is performed instead). The coverage data is not updated with \n" + " the subset of selected tests and 'tiaorseed' uses any prior coverage data \n" + " to run the instrumented subset of selected tests (if no prior coverage \n" + " data a seed run is performed instead).\n" + " -safemode= Flag to specify a safe mode sequence where the set of unselected \n" + " tests is run without instrumentation after the set of selected \n" + " instrumented tests is run (this has the effect of ensuring all \n" + " tests are run regardless).\n" + " -shard= Break any test targets with a sharding policy into the number of \n" + " shards according to the maximum concurrency value.\n" + " -cpolicy= Policy for handling the coverage data of failing tests, where 'discard' \n" + " will discard the coverage data produced by the failing tests, causing \n" + " them to be drafted into future test runs and 'keep' will keep any existing \n" + " coverage data and update the coverage data for failed tests that produce \n" + " coverage.\n" + " -targetout= Capture of individual test run stdout, where 'stdout' will capture \n" + " each individual test target's stdout and output each one to stdout \n" + " and 'file' will capture each individual test target's stdout and output \n" + " each one individually to a file (multiple values are accepted).\n" + " -epolicy= Policy for handling test execution failure (test targets could not be \n" + " launched due to the binary not being built, incorrect paths, etc.), \n" + " where 'abort' will abort the entire test sequence upon the first test\n" + " target execution failure and report a failure(along with the return \n" + " code of the test target that failed to launch), 'continue' will continue \n" + " with the test sequence in the event of test target execution failures\n" + " and treat the test targets that failed to launch as test failures\n" + " (along with the return codes of the test targets that failed to \n" + " launch), 'ignore' will continue with the test sequence in the event of \n" + " test target execution failures and treat the test targets that failed\n" + " to launch as test passes(along with the return codes of the test \n" + " targets that failed to launch).\n" + " -fpolicy Policy for handling test failures (test targets report failing tests), \n" + " where 'abort' will abort the entire test sequence upon the first test \n" + " failure and report a failure and 'continue' will continue with the test\n" + " sequence in the event of test failures and report the test failures.\n" + " -ipolicy= Policy for handling coverage data integrity failures, where 'abort' will \n" + " abort the test sequence and report a failure, 'seed' will attempt another \n" + " sequence using the seed sequence type, otherwise will abort and report \n" + " a failure (this option has no effect for regular and seed sequence \n" + " types) and 'rerun' will attempt another sequence using the regular \n" + " sequence type, otherwise will abort and report a failure(this option has \n" + " no effect for regular sequence type).\n" + " -ppolicy= Policy for prioritizing selected test targets, where 'none' will not \n" + " attempt any test target prioritization and 'locality' will attempt to \n" + " prioritize test targets according to the locality of their covering \n" + " production targets in the dependency graph(if no dependency graph data \n" + " available, no prioritization will occur).\n" + " -maxconcurrency= The maximum number of concurrent test targets/shards to be in flight at \n" + " any given moment.\n" + " -ochangelist= Outputs the change list used for test selection.\n" + " -suite= The test suite to select from for this test sequence."; return help; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h index 2a7720ee55..485b966a1e 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRuntime.h @@ -202,7 +202,7 @@ namespace TestImpact AZStd::pair, AZStd::vector> SelectTestTargetsByExcludeList( AZStd::vector testTargets) const; - //! Prunes the existing coverage for the specified jobs and creates the consolidates source covering tests list from the + //! Prunes the existing coverage for the specified jobs and creates the consolidated source covering tests list from the //! test engine instrumented run jobs. SourceCoveringTestsList CreateSourceCoveringTestFromTestCoverages(const AZStd::vector& jobs); From c9351f4d45714bbb6b8432ed26fb3e4286d84f58 Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 12:05:38 +0100 Subject: [PATCH 104/233] Make TestEngine clean up artifact dir before runs --- .../Code/Source/TestEngine/TestImpactTestEngine.cpp | 12 ++++++++++++ .../Code/Source/TestEngine/TestImpactTestEngine.h | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp index b563c1846b..f3ce777938 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp @@ -10,6 +10,8 @@ * */ +#include + #include #include #include @@ -247,11 +249,17 @@ namespace TestImpact , m_testEnumerator(AZStd::make_unique(maxConcurrentRuns)) , m_instrumentedTestRunner(AZStd::make_unique(maxConcurrentRuns)) , m_testRunner(AZStd::make_unique(maxConcurrentRuns)) + , m_artifactDir(artifactDir) { } TestEngine::~TestEngine() = default; + void TestEngine::CleanArtifactDir() const + { + DeleteFiles(m_artifactDir, "*xml*"); + } + AZStd::pair> TestEngine::UpdateEnumerationCache( const AZStd::vector& testTargets, Policy::ExecutionFailure executionFailurePolicy, @@ -283,6 +291,8 @@ namespace TestImpact AZStd::optional globalTimeout, AZStd::optional callback) { + CleanArtifactDir(); + TestEngineJobMap engineJobs; const auto jobInfos = m_testJobInfoGenerator->GenerateRegularTestRunJobInfos(testTargets); @@ -308,6 +318,8 @@ namespace TestImpact AZStd::optional globalTimeout, AZStd::optional callback) { + CleanArtifactDir(); + TestEngineJobMap engineJobs; const auto jobInfos = m_testJobInfoGenerator->GenerateInstrumentedTestRunJobInfos(testTargets, CoverageLevel::Source); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h index 83f57f00ef..5192a1d3b7 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h @@ -118,10 +118,14 @@ namespace TestImpact AZStd::optional callback); private: + //! Cleans up the artifacts directory of any artifacts from previous runs. + void CleanArtifactDir() const; + size_t m_maxConcurrentRuns = 0; AZStd::unique_ptr m_testJobInfoGenerator; AZStd::unique_ptr m_testEnumerator; AZStd::unique_ptr m_instrumentedTestRunner; AZStd::unique_ptr m_testRunner; + RepoPath m_artifactDir; }; } // namespace TestImpact From 9e187d67a4f272bd4e9bdb928fc02c48ab172a53 Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 12:08:42 +0100 Subject: [PATCH 105/233] Fix typo with delete files filter --- .../Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp index f3ce777938..4c7d660004 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp @@ -257,7 +257,7 @@ namespace TestImpact void TestEngine::CleanArtifactDir() const { - DeleteFiles(m_artifactDir, "*xml*"); + DeleteFiles(m_artifactDir, "*.xml"); } AZStd::pair> TestEngine::UpdateEnumerationCache( From 7a884c85b37d7bfa810b53d7a9720fcc5e3f5d9c Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 12:19:07 +0100 Subject: [PATCH 106/233] Address PR comments --- .../Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp | 6 +++--- .../Runtime/Code/Source/TestEngine/TestImpactTestEngine.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp index 4c7d660004..a9d0e15781 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp @@ -255,7 +255,7 @@ namespace TestImpact TestEngine::~TestEngine() = default; - void TestEngine::CleanArtifactDir() const + void TestEngine::DeleteArtifactXmls() const { DeleteFiles(m_artifactDir, "*.xml"); } @@ -291,7 +291,7 @@ namespace TestImpact AZStd::optional globalTimeout, AZStd::optional callback) { - CleanArtifactDir(); + DeleteArtifactXmls(); TestEngineJobMap engineJobs; const auto jobInfos = m_testJobInfoGenerator->GenerateRegularTestRunJobInfos(testTargets); @@ -318,7 +318,7 @@ namespace TestImpact AZStd::optional globalTimeout, AZStd::optional callback) { - CleanArtifactDir(); + DeleteArtifactXmls(); TestEngineJobMap engineJobs; const auto jobInfos = m_testJobInfoGenerator->GenerateInstrumentedTestRunJobInfos(testTargets, CoverageLevel::Source); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h index 5192a1d3b7..7d16f352f3 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h @@ -119,7 +119,7 @@ namespace TestImpact private: //! Cleans up the artifacts directory of any artifacts from previous runs. - void CleanArtifactDir() const; + void DeleteArtifactXmls() const; size_t m_maxConcurrentRuns = 0; AZStd::unique_ptr m_testJobInfoGenerator; From 656aa528d86d8a561d12b22d048ced7b1a40273e Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 13:39:37 +0100 Subject: [PATCH 107/233] Implement new handling of tiaf seed data --- .../Frontend/Console/CMakeLists.txt | 13 +++++ .../Console/Executable/CMakeLists.txt | 12 +++++ .../Console/Executable/Code/CMakeLists.txt | 25 ++++++++++ ...pactframework_frontend_console_files.cmake | 14 ++++++ ...amework_frontend_console_tests_files.cmake | 0 .../Frontend/Console/Static/CMakeLists.txt | 12 +++++ .../Console/Static/Code/CMakeLists.txt | 50 +++++++++++++++++++ ...mework_frontend_console_static_files.cmake | 26 ++++++++++ ..._frontend_console_static_tests_files.cmake | 14 ++++++ .../testimpactframework_runtime_files.cmake | 2 - ...timpactframework_runtime_tests_files.cmake | 13 +++++ .../ConsoleFrontendConfig.in | 7 +++ .../LYTestImpactFramework.cmake | 9 +++- scripts/build/Jenkins/Jenkinsfile | 2 +- .../build/Platform/Windows/build_config.json | 22 +++----- 15 files changed, 201 insertions(+), 20 deletions(-) create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Executable/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/testimpactframework_frontend_console_files.cmake create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/testimpactframework_frontend_console_tests_files.cmake create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/CMakeLists.txt create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_files.cmake create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_tests_files.cmake create mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/CMakeLists.txt new file mode 100644 index 0000000000..a25d506156 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/CMakeLists.txt @@ -0,0 +1,13 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +add_subdirectory(Executable) +add_subdirectory(Static) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/CMakeLists.txt new file mode 100644 index 0000000000..8298bb7123 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/CMakeLists.txt new file mode 100644 index 0000000000..88da472e1e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +ly_add_target( + NAME TestImpact.Frontend.Console EXECUTABLE + OUTPUT_NAME tiaf + NAMESPACE AZ + FILES_CMAKE + testimpactframework_frontend_console_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::TestImpact.Frontend.Console.Static +) + diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/testimpactframework_frontend_console_files.cmake b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/testimpactframework_frontend_console_files.cmake new file mode 100644 index 0000000000..19e99649a7 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/testimpactframework_frontend_console_files.cmake @@ -0,0 +1,14 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Source/TestImpactConsole.cpp +) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/testimpactframework_frontend_console_tests_files.cmake b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/testimpactframework_frontend_console_tests_files.cmake new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/Static/CMakeLists.txt new file mode 100644 index 0000000000..8298bb7123 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/CMakeLists.txt new file mode 100644 index 0000000000..2302cb5484 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/CMakeLists.txt @@ -0,0 +1,50 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +ly_add_target( + NAME TestImpact.Frontend.Console.Static STATIC + NAMESPACE AZ + FILES_CMAKE + testimpactframework_frontend_console_static_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PUBLIC + AZ::TestImpact.Runtime.Static +) + +################################################################################ +# Tests +################################################################################ + +ly_add_target( + NAME TestImpact.Frontend.Console.Static.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE AZ + FILES_CMAKE + testimpactframework_frontend_console_static_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Include + Source + Tests + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTestShared + AZ::AzTest + AZ::TestImpact.Frontend.Console.Static +) + +ly_add_googletest( + NAME AZ::TestImpact.Frontend.Console.Static.Tests +) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_files.cmake b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_files.cmake new file mode 100644 index 0000000000..97081dc08c --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_files.cmake @@ -0,0 +1,26 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Include/TestImpactFramework/TestImpactConsoleMain.h + Source/TestImpactCommandLineOptions.h + Source/TestImpactCommandLineOptions.cpp + Source/TestImpactCommandLineOptionsUtils.cpp + Source/TestImpactCommandLineOptionsUtils.h + Source/TestImpactCommandLineOptionsException.h + Source/TestImpactRuntimeConfigurationFactory.h + Source/TestImpactRuntimeConfigurationFactory.cpp + Source/TestImpactConsoleMain.cpp + Source/TestImpactConsoleTestSequenceEventHandler.cpp + Source/TestImpactConsoleTestSequenceEventHandler.h + Source/TestImpactConsoleUtils.cpp + Source/TestImpactConsoleUtils.h +) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_tests_files.cmake b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_tests_files.cmake new file mode 100644 index 0000000000..4e45f540ad --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_tests_files.cmake @@ -0,0 +1,14 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + +) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake index 2053a1ea5e..250894464b 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake +++ b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_files.cmake @@ -35,8 +35,6 @@ set(FILES Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.h Source/Artifact/Factory/TestImpactModuleCoverageFactory.cpp Source/Artifact/Factory/TestImpactModuleCoverageFactory.h - Source/Artifact/Factory/TestImpactDependencyGraphDataFactory.cpp - Source/Artifact/Factory/TestImpactDependencyGraphDataFactory.h Source/Artifact/Static/TestImpactBuildTargetDescriptor.cpp Source/Artifact/Static/TestImpactBuildTargetDescriptor.h Source/Artifact/Static/TestImpactTargetDescriptorCompiler.cpp diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake new file mode 100644 index 0000000000..5714be5dfb --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/testimpactframework_runtime_tests_files.cmake @@ -0,0 +1,13 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES +) diff --git a/cmake/TestImpactFramework/ConsoleFrontendConfig.in b/cmake/TestImpactFramework/ConsoleFrontendConfig.in index 357499771c..9338672fbd 100644 --- a/cmake/TestImpactFramework/ConsoleFrontendConfig.in +++ b/cmake/TestImpactFramework/ConsoleFrontendConfig.in @@ -3,6 +3,13 @@ "platform": "${platform}", "timestamp": "${timestamp}" }, + "jenkins": { + "pipeline_of_truth" : [ + "nightly-incremental", + "nightly-clean" + ], + "use_test_impact_analysis": ${use_tiaf} + }, "repo": { "root": "${repo_dir}", "tiaf_bin": "${tiaf_bin}" diff --git a/cmake/TestImpactFramework/LYTestImpactFramework.cmake b/cmake/TestImpactFramework/LYTestImpactFramework.cmake index 54dadbd8ad..661a066477 100644 --- a/cmake/TestImpactFramework/LYTestImpactFramework.cmake +++ b/cmake/TestImpactFramework/LYTestImpactFramework.cmake @@ -344,9 +344,14 @@ function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE PERSISTENT_DATA_D # Instrumentation binary if(NOT LY_TEST_IMPACT_INSTRUMENTATION_BIN) - message(FATAL_ERROR "No test impact framework instrumentation binary was specified, please provide the path with option LY_TEST_IMPACT_INSTRUMENTATION_BIN") + # No binary specified is not an error, it just means that the test impact analysis part of the framework is disabled + message("No test impact framework instrumentation binary was specified, test impact analysis framework will fall back to regular test sequences instead") + set(use_tiaf false) + set(instrumentation_bin "") + else() + set(use_tiaf true) + file(TO_CMAKE_PATH ${LY_TEST_IMPACT_INSTRUMENTATION_BIN} instrumentation_bin) endif() - file(TO_CMAKE_PATH ${LY_TEST_IMPACT_INSTRUMENTATION_BIN} instrumentation_bin) # Testrunner binary set(test_runner_bin $) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 2f6be2b060..5c11efe5a8 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -447,7 +447,7 @@ try { // repositoryName is the full repository name repositoryName = (repositoryUrl =~ /https:\/\/github.com\/(.*)\.git/)[0][1] (projectName, pipelineName) = GetRunningPipelineName(env.JOB_NAME) // env.JOB_NAME is the name of the job given by Jenkins - + env.PIPELINE_NAME = pipelineName if(env.BRANCH_NAME) { branchName = env.BRANCH_NAME } else { diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 5dede68d92..be729c2b7f 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -22,16 +22,17 @@ ], "steps": [ "debug_vs2019", - "test_impact_seed", "test_debug_vs2019" ] }, "profile_vs2019_pipe": { "TAGS": [ - "default" + "default", + "nightly-incremental", + "nightly-clean" ], "steps": [ - "profile_vs2019", + "profile_vs2019", "test_impact_analysis", "asset_profile_vs2019", "test_cpu_profile_vs2019" @@ -81,22 +82,13 @@ "SCRIPT_PARAMETERS": "--platform 3rdParty --type 3rdParty_all" } }, - "test_impact_seed": { - "TAGS": [ - ], - "COMMAND": "python_windows.cmd", - "PARAMETERS": { - "SCRIPT_PATH": "scripts/build/TestImpactAnalysis/tiaf_driver.py", - "SCRIPT_PARAMETERS": "--sequenceType seed --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.debug.json\"" - } - }, "test_impact_analysis": { "TAGS": [ ], "COMMAND": "python_windows.cmd", "PARAMETERS": { "SCRIPT_PATH": "scripts/build/TestImpactAnalysis/tiaf_driver.py", - "SCRIPT_PARAMETERS": "--sequenceType tia --destCommit !CHANGE_ID! --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.profile.json\"" + "SCRIPT_PARAMETERS": "--safeMode --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.profile.json\"" } }, "debug_vs2019": { @@ -107,7 +99,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE -DLY_TEST_IMPACT_ACTIVE=1 -DLY_TEST_IMPACT_INSTRUMENTATION_BIN=\"c:\\ly\\3rdParty\\ackages\\OpenCppCoverage\\Binary\\windows-x64\\OpenCppCoverage.exe\"", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -139,7 +131,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_TEST_IMPACT_ACTIVE=1 -DLY_TEST_IMPACT_INSTRUMENTATION_BIN=\"c:\\ly\\3rdParty\\ackages\\OpenCppCoverage\\Binary\\windows-x64\\OpenCppCoverage.exe\"", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_TEST_IMPACT_ACTIVE=1 -DLY_TEST_IMPACT_INSTRUMENTATION_BIN=!TEST_IMPACT_WIN_BINARY!", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" From 4b819953d65763f7e7e9fd07276945c455c6981f Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 14:41:18 +0100 Subject: [PATCH 108/233] Fix build script --- scripts/build/Platform/Windows/build_config.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index d45bac0363..d94f34d9e0 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -84,6 +84,9 @@ }, "test_impact_analysis": { "TAGS": [ + "nightly-incremental", + "nightly-clean", + "default" ], "COMMAND": "python_windows.cmd", "PARAMETERS": { From 7a38a93b9c127c41b36fe7ead4d4521b1122ea89 Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 14:47:57 +0100 Subject: [PATCH 109/233] Remove test_impact_analysis --- scripts/build/Platform/Windows/build_config.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index d94f34d9e0..bb4a0decbf 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -32,8 +32,7 @@ "nightly-clean" ], "steps": [ - "profile_vs2019", - "test_impact_analysis", + "profile_vs2019", "asset_profile_vs2019", "test_cpu_profile_vs2019" ] @@ -84,9 +83,6 @@ }, "test_impact_analysis": { "TAGS": [ - "nightly-incremental", - "nightly-clean", - "default" ], "COMMAND": "python_windows.cmd", "PARAMETERS": { From 98670a21bc8de75e6fc5f406f8e40032aa8afd9c Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 15:42:49 +0100 Subject: [PATCH 110/233] Fix missing copyright header --- .../Code/testimpactframework_frontend_console_tests_files.cmake | 0 ...testimpactframework_frontend_console_static_tests_files.cmake | 1 - 2 files changed, 1 deletion(-) delete mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/testimpactframework_frontend_console_tests_files.cmake diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/testimpactframework_frontend_console_tests_files.cmake b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/testimpactframework_frontend_console_tests_files.cmake deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_tests_files.cmake b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_tests_files.cmake index 4e45f540ad..5714be5dfb 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_tests_files.cmake +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_tests_files.cmake @@ -10,5 +10,4 @@ # set(FILES - ) From a9e6eaa57a7cdfc05b096a646b5cf2540cc2981a Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 16:43:02 +0100 Subject: [PATCH 111/233] Fix anon enum clash with uber files --- .../Source/TestImpactCommandLineOptions.cpp | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp index 0a4c80afa4..9a05fc6fc9 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp @@ -22,22 +22,22 @@ namespace TestImpact enum { // Options - Config, - ChangeList, - OutputChangeList, - Sequence, - TestPrioritizationPolicy, - ExecutionFailurePolicy, - FailedTestCoveragePolicy, - TestFailurePolicy, - IntegrityFailurePolicy, - TestShardingPolicy, - TargetOutputCapture, - MaxConcurrency, - TestTargetTimeout, - GlobalTimeout, - SuiteFilter, - SafeMode, + ConfigKey, + ChangeListKey, + OutputChangeListKey, + SequenceKey, + TestPrioritizationPolicyKey, + ExecutionFailurePolicyKey, + FailedTestCoveragePolicyKey, + TestFailurePolicyKey, + IntegrityFailurePolicyKey, + TestShardingPolicyKey, + TargetOutputCaptureKey, + MaxConcurrencyKey, + TestTargetTimeoutKey, + GlobalTimeoutKey, + SuiteFilterKey, + SafeModeKey, // Values None, Seed, @@ -93,17 +93,17 @@ namespace TestImpact RepoPath ParseConfigurationFile(const AZ::CommandLine& cmd) { - return ParsePathOption(OptionKeys[Config], cmd).value_or(LY_TEST_IMPACT_DEFAULT_CONFIG_FILE); + return ParsePathOption(OptionKeys[ConfigKey], cmd).value_or(LY_TEST_IMPACT_DEFAULT_CONFIG_FILE); } AZStd::optional ParseChangeListFile(const AZ::CommandLine& cmd) { - return ParsePathOption(OptionKeys[ChangeList], cmd); + return ParsePathOption(OptionKeys[ChangeListKey], cmd); } bool ParseOutputChangeList(const AZ::CommandLine& cmd) { - return ParseOnOffOption(OptionKeys[OutputChangeList], BinaryStateValue{ false, true }, cmd).value_or(false); + return ParseOnOffOption(OptionKeys[OutputChangeListKey], BinaryStateValue{ false, true }, cmd).value_or(false); } TestSequenceType ParseTestSequenceType(const AZ::CommandLine& cmd) @@ -118,7 +118,7 @@ namespace TestImpact {OptionKeys[ImpactAnalysisOrSeed], TestSequenceType::ImpactAnalysisOrSeed} }; - return ParseMultiStateOption(OptionKeys[Sequence], states, cmd).value_or(TestSequenceType::None); + return ParseMultiStateOption(OptionKeys[SequenceKey], states, cmd).value_or(TestSequenceType::None); } Policy::TestPrioritization ParseTestPrioritizationPolicy(const AZ::CommandLine& cmd) @@ -129,7 +129,7 @@ namespace TestImpact {OptionKeys[Locality], Policy::TestPrioritization::DependencyLocality} }; - return ParseBinaryStateOption(OptionKeys[TestPrioritizationPolicy], states, cmd).value_or(Policy::TestPrioritization::None); + return ParseBinaryStateOption(OptionKeys[TestPrioritizationPolicyKey], states, cmd).value_or(Policy::TestPrioritization::None); } Policy::ExecutionFailure ParseExecutionFailurePolicy(const AZ::CommandLine& cmd) @@ -140,7 +140,7 @@ namespace TestImpact {OptionKeys[Continue], Policy::ExecutionFailure::Continue}, {OptionKeys[Ignore], Policy::ExecutionFailure::Ignore} }; - return ParseMultiStateOption(OptionKeys[ExecutionFailurePolicy], states, cmd).value_or(Policy::ExecutionFailure::Continue); + return ParseMultiStateOption(OptionKeys[ExecutionFailurePolicyKey], states, cmd).value_or(Policy::ExecutionFailure::Continue); } Policy::FailedTestCoverage ParseFailedTestCoveragePolicy(const AZ::CommandLine& cmd) @@ -151,7 +151,7 @@ namespace TestImpact {OptionKeys[Keep], Policy::FailedTestCoverage::Keep} }; - return ParseMultiStateOption(OptionKeys[FailedTestCoveragePolicy], states, cmd).value_or(Policy::FailedTestCoverage::Keep); + return ParseMultiStateOption(OptionKeys[FailedTestCoveragePolicyKey], states, cmd).value_or(Policy::FailedTestCoverage::Keep); } Policy::TestFailure ParseTestFailurePolicy(const AZ::CommandLine& cmd) @@ -162,7 +162,7 @@ namespace TestImpact Policy::TestFailure::Continue }; - return ParseAbortContinueOption(OptionKeys[TestFailurePolicy], states, cmd).value_or(Policy::TestFailure::Abort); + return ParseAbortContinueOption(OptionKeys[TestFailurePolicyKey], states, cmd).value_or(Policy::TestFailure::Abort); } Policy::IntegrityFailure ParseIntegrityFailurePolicy(const AZ::CommandLine& cmd) @@ -173,7 +173,7 @@ namespace TestImpact Policy::IntegrityFailure::Continue }; - return ParseAbortContinueOption(OptionKeys[IntegrityFailurePolicy], states, cmd).value_or(Policy::IntegrityFailure::Abort); + return ParseAbortContinueOption(OptionKeys[IntegrityFailurePolicyKey], states, cmd).value_or(Policy::IntegrityFailure::Abort); } Policy::TestSharding ParseTestShardingPolicy(const AZ::CommandLine& cmd) @@ -189,7 +189,7 @@ namespace TestImpact Policy::TargetOutputCapture ParseTargetOutputCapture(const AZ::CommandLine& cmd) { - if (const auto numSwitchValues = cmd.GetNumSwitchValues(OptionKeys[TargetOutputCapture]); + if (const auto numSwitchValues = cmd.GetNumSwitchValues(OptionKeys[TargetOutputCaptureKey]); numSwitchValues) { AZ_TestImpact_Eval( @@ -236,23 +236,23 @@ namespace TestImpact AZStd::optional ParseMaxConcurrency(const AZ::CommandLine& cmd) { - return ParseUnsignedIntegerOption(OptionKeys[MaxConcurrency], cmd); + return ParseUnsignedIntegerOption(OptionKeys[MaxConcurrencyKey], cmd); } AZStd::optional ParseTestTargetTimeout(const AZ::CommandLine& cmd) { - return ParseSecondsOption(OptionKeys[TestTargetTimeout], cmd); + return ParseSecondsOption(OptionKeys[TestTargetTimeoutKey], cmd); } AZStd::optional ParseGlobalTimeout(const AZ::CommandLine& cmd) { - return ParseSecondsOption(OptionKeys[GlobalTimeout], cmd); + return ParseSecondsOption(OptionKeys[GlobalTimeoutKey], cmd); } bool ParseSafeMode(const AZ::CommandLine& cmd) { const BinaryStateValue states = { false, true }; - return ParseOnOffOption(OptionKeys[SafeMode], states, cmd).value_or(false); + return ParseOnOffOption(OptionKeys[SafeModeKey], states, cmd).value_or(false); } SuiteType ParseSuiteFilter(const AZ::CommandLine& cmd) @@ -264,7 +264,7 @@ namespace TestImpact {GetSuiteTypeName(SuiteType::Sandbox), SuiteType::Sandbox} }; - return ParseMultiStateOption(OptionKeys[SuiteFilter], states, cmd).value_or(SuiteType::Main); + return ParseMultiStateOption(OptionKeys[SuiteFilterKey], states, cmd).value_or(SuiteType::Main); } } From dd93c42342352d3c9445e18f362a983f5db44d32 Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 16:49:49 +0100 Subject: [PATCH 112/233] Fix missing keys --- .../Static/Code/Source/TestImpactCommandLineOptions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp index 9a05fc6fc9..7eff998dda 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp @@ -184,7 +184,7 @@ namespace TestImpact Policy::TestSharding::Always }; - return ParseOnOffOption("shard", states, cmd).value_or(Policy::TestSharding::Never); + return ParseOnOffOption(OptionKeys[TestShardingPolicyKey], states, cmd).value_or(Policy::TestSharding::Never); } Policy::TargetOutputCapture ParseTargetOutputCapture(const AZ::CommandLine& cmd) @@ -198,7 +198,7 @@ namespace TestImpact Policy::TargetOutputCapture targetOutputCapture = Policy::TargetOutputCapture::None; for (auto i = 0; i < numSwitchValues; i++) { - const auto option = cmd.GetSwitchValue(OptionKeys[TargetOutputCapture], i); + const auto option = cmd.GetSwitchValue(OptionKeys[TargetOutputCaptureKey], i); if (option == OptionKeys[StdOut]) { if (targetOutputCapture == Policy::TargetOutputCapture::File) From f78ad7f3770ad95392c2f582d72af52fb28a07fe Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 16:58:25 +0100 Subject: [PATCH 113/233] Add missing tiaf scripts --- scripts/build/TestImpactAnalysis/tiaf.py | 166 ++++++++++++++---- .../build/TestImpactAnalysis/tiaf_driver.py | 31 +--- 2 files changed, 135 insertions(+), 62 deletions(-) diff --git a/scripts/build/TestImpactAnalysis/tiaf.py b/scripts/build/TestImpactAnalysis/tiaf.py index 01e2cde8ba..1523d39b4a 100644 --- a/scripts/build/TestImpactAnalysis/tiaf.py +++ b/scripts/build/TestImpactAnalysis/tiaf.py @@ -23,33 +23,40 @@ def is_child_path(parent_path, child_path): child_path = os.path.abspath(child_path) return os.path.commonpath([os.path.abspath(parent_path)]) == os.path.commonpath([os.path.abspath(parent_path), os.path.abspath(child_path)]) -# Enumerations for test sequence types -class SequenceType(Enum): - # Regular sequence as-per the tiaf regular sequence - REGULAR = 1 - # TIA sequence as-per the tiaf read-only impact analysis sequence - TEST_IMPACT_ANALYSIS = 2 - # Seed sequence as-per the tiaf seed sequence - SEED = 3 - class TestImpact: - def __init__(self, config_file, dst_commit): + def __init__(self, config_file, pipeline, dst_commit): + self.__pipeline = pipeline self.__parse_config_file(config_file) self.__init_repo(dst_commit) - self.__generate_change_list() + if self.__use_test_impact_analysis and not self.__is_pipeline_of_truth: + self.__generate_change_list() # Parse the configuration file and retrieve the data needed for launching the test impact analysis runtime def __parse_config_file(self, config_file): print(f"Attempting to parse configuration file '{config_file}'...") with open(config_file, "r") as config_data: config = json.load(config_data) + # Repository self.__repo_dir = config["repo"]["root"] + # Jenkins + self.__use_test_impact_analysis = config["jenkins"]["use_test_impact_analysis"] + self.__pipeline_of_truth = config["jenkins"]["pipeline_of_truth"] + print(f"Pipeline of truth: '{self.__pipeline_of_truth}'.") + print(f"This pipeline: '{self.__pipeline}'.") + if self.__pipeline in self.__pipeline_of_truth: + self.__is_pipeline_of_truth = True + else: + self.__is_pipeline_of_truth = False + print(f"Is pipeline of truth: '{self.__is_pipeline_of_truth}'.") + # TIAF binary self.__tiaf_bin = config["repo"]["tiaf_bin"] - if not os.path.isfile(self.__tiaf_bin): + if self.__use_test_impact_analysis and not os.path.isfile(self.__tiaf_bin): raise FileNotFoundError("Could not find tiaf binary") + # Workspaces self.__active_workspace = config["workspace"]["active"]["root"] self.__historic_workspace = config["workspace"]["historic"]["root"] self.__temp_workspace = config["workspace"]["temp"]["root"] + # Last commit hash last_commit_hash_path_rel = config["workspace"]["historic"]["relative_paths"]["last_run_hash_file"] self.__last_commit_hash_path = os.path.join(self.__historic_workspace, last_commit_hash_path_rel) print("The configuration file was parsed successfully.") @@ -71,7 +78,7 @@ class TestImpact: def __read_last_run_hash(self): self.__has_src_commit = False if os.path.isfile(self.__last_commit_hash_path): - print(f"Previous commit hash found at '{self.__last_commit_hash_path}'") + print(f"Previous commit hash found at '{self.__last_commit_hash_path}'.") with open(self.__last_commit_hash_path) as file: self.__src_commit = file.read() self.__has_src_commit = True @@ -144,43 +151,79 @@ class TestImpact: return # Runs the specified test sequence - def run(self, sequence_type, safe_mode, test_timeout, global_timeout): + def run(self, suite, safe_mode, test_timeout, global_timeout): args = [] - print("Please note: test impact analysis sequences will be run in read-only mode (seed sequences are unaffected).") - if sequence_type == SequenceType.REGULAR: - print("Sequence type: regular.") - args.append("--sequence=regular") - args.append("--fpolicy=abort") - elif sequence_type == SequenceType.SEED: - print("Sequence type: seed.") - args.append("--sequence=seed") - args.append("--fpolicy=continue") - elif sequence_type == SequenceType.TEST_IMPACT_ANALYSIS: - print("Sequence type: test impact analysis (no write).") - args.append("--fpolicy=abort") - if self.__has_change_list: - args.append(f"-changelist={self.__change_list_path}") - args.append("--sequence=tianowrite") - else: - print(f"No change list was generated, falling back to a regular sequence.") - print("Sequence type: Regular.") - args.append("--sequence=regular") - else: - raise ValueError(sequence_type) - + # Suite + args.append(f"--suite={suite}") + print(f"Test suite is set to '{suite}'.") + # Timeouts if test_timeout != None: args.append(f"--ttimeout={test_timeout}") print(f"Test target timeout is set to {test_timeout} seconds.") if global_timeout != None: args.append(f"--gtimeout={global_timeout}") print(f"Global sequence timeout is set to {test_timeout} seconds.") - + # If test impact analysis is enabled: + # -> Pipleine of truth will perform a seed that will continue until the sequence is complete regardless of test failues + # -> Non pipline of truth will attempt to perform an impact analysis sequence and exit early upon the fist test failure + # If test impact analysis is disabled: + # -> Pipleine of truth will perform a regular sequence that will continue until the sequence is complete regardless of test failues + # -> Non pipline of truth will perform a regular sequence and exit early upon the fist test failure + if self.__use_test_impact_analysis: + print("Test impact analysis ie enabled.") + # Pipeline of truth sequence + if self.__is_pipeline_of_truth: + # Sequence type + args.append("--sequence=seed") + print("Sequence type is set to 'seed'.") + # Test failure policy + args.append("--fpolicy=continue") + print("Test failure policy is set to 'continue'.") + # Non pipeline of truth sequence + else: + if self.__has_change_list: + # Change list + args.append(f"--changelist={self.__change_list_path}") + print(f"Change list is set to '{self.__change_list_path}'.") + # Sequence type + args.append("--sequence=tianowrite") + print("Sequence type is set to 'tianowrite'.") + # Safe mode + if safe_mode: + args.append("--safemode=on") + print("Safe mode set to 'on'.") + else: + args.append("--safemode=off") + print("Safe mode set to 'off'.") + else: + args.append("--sequence=regular") + print("Sequence type is set to 'regular'.") + # Test failure policy + args.append("--fpolicy=abort") + print("Test failure policy is set to 'abort'.") + else: + print("Test impact analysis ie disabled.") + # Sequence type + args.append("--sequence=regular") + print("Sequence type is set to 'seed'.") + # Pipeline of truth sequence + if self.__is_pipeline_of_truth: + # Test failure policy + args.append("--fpolicy=continue") + print("Test failure policy is set to 'continue'.") + # Non pipeline of truth sequence + else: + # Test failure policy + args.append("--fpolicy=abort") + print("Test failure policy is set to 'abort'.") + print("Args: ", end='') print(*args) result = subprocess.run([self.__tiaf_bin] + args) - if result.returncode == 0: + # If the sequence completed 9with or without failures) we will update the historical meta-data + if result.returncode == 0 or result.returncode == 7: print("Test impact analysis runtime returned successfully.") - if sequence_type == SequenceType.SEED: + if self.__is_pipeline_of_truth: print("Writing historical meta-data...") self.__write_last_run_hash(self.__dst_commit) print("Complete!") @@ -188,3 +231,48 @@ class TestImpact: print(f"The test impact analysis runtime returned with error: '{result.returncode}'.") return result.returncode + + + #args = [] + #print("Please note: test impact analysis sequences will be run in read-only mode (seed sequences are unaffected).") + #if sequence_type == SequenceType.REGULAR: + # print("Sequence type: regular.") + # args.append("--sequence=regular") + # args.append("--fpolicy=abort") + #elif sequence_type == SequenceType.SEED: + # print("Sequence type: seed.") + # args.append("--sequence=seed") + # args.append("--fpolicy=continue") + #elif sequence_type == SequenceType.TEST_IMPACT_ANALYSIS: + # print("Sequence type: test impact analysis (no write).") + # args.append("--fpolicy=abort") + # if self.__has_change_list: + # args.append(f"-changelist={self.__change_list_path}") + # args.append("--sequence=tianowrite") + # else: + # print(f"No change list was generated, falling back to a regular sequence.") + # print("Sequence type: Regular.") + # args.append("--sequence=regular") + #else: + # raise ValueError(sequence_type) + # + #if test_timeout != None: + # args.append(f"--ttimeout={test_timeout}") + # print(f"Test target timeout is set to {test_timeout} seconds.") + #if global_timeout != None: + # args.append(f"--gtimeout={global_timeout}") + # print(f"Global sequence timeout is set to {test_timeout} seconds.") + # + #print("Args: ", end='') + #print(*args) + #result = subprocess.run([self.__tiaf_bin] + args) + #if result.returncode == 0: + # print("Test impact analysis runtime returned successfully.") + # if sequence_type == SequenceType.SEED: + # print("Writing historical meta-data...") + # self.__write_last_run_hash(self.__dst_commit) + # print("Complete!") + #else: + # print(f"The test impact analysis runtime returned with error: '{result.returncode}'.") + #return result.returncode + diff --git a/scripts/build/TestImpactAnalysis/tiaf_driver.py b/scripts/build/TestImpactAnalysis/tiaf_driver.py index 1cc7ecb630..917fd66fc6 100644 --- a/scripts/build/TestImpactAnalysis/tiaf_driver.py +++ b/scripts/build/TestImpactAnalysis/tiaf_driver.py @@ -11,7 +11,6 @@ import argparse from tiaf import TestImpact -from tiaf import SequenceType import sys import os @@ -26,16 +25,6 @@ def parse_args(): else: raise FileNotFoundError(value) - def sequence_type(value): - if value == "regular": - return SequenceType.REGULAR - elif value == "tia": - return SequenceType.TEST_IMPACT_ANALYSIS - elif value == "seed": - return SequenceType.SEED - else: - raise ValueError(value) - def timout_type(value): value = int(value) if value <= 0: @@ -44,24 +33,20 @@ def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('--config', dest="config", type=file_path, help="Path to the test impact analysis framework configuration file", required=True) - parser.add_argument('--destCommit', dest="dst_commit", help="Commit to run test impact analysis on (not required for seed)") - parser.add_argument('--sequenceType', dest="sequence_type", type=sequence_type, help="Test sequence type to run ('regular', 'seed' or 'tia')", required=True) - parser.add_argument('--suites', dest="suites", nargs='*', help="Suites to include for regular tes sequences (use '*' for all suites)") - parser.add_argument('--testTimeout', dest="test_timeout", type=timout_type, help="Maximum flight time (in seconds) of any test target before being terminated", required=False) - parser.add_argument('--globalTimeout', dest="global_timeout", type=timout_type, help="Maximum tun time of the sequence before being terminated", required=False) - parser.set_defaults(suites="*") - parser.set_defaults(safe_mode=False) + parser.add_argument('--pipeline', dest="pipeline", help="Pipeline the test impact analysis framework is running on", required=True) + parser.add_argument('--destCommit', dest="dst_commit", help="Commit to run test impact analysis on (ignored when seeding)", required=True) + parser.add_argument('--suite', dest="suite", help="Test suite to run", required=True) + parser.add_argument('--safeMode', dest="safe_mode", action='store_true', help="Run impact analysis tests in safe mode (ignored when seeding)") + parser.add_argument('--testTimeout', dest="test_timeout", type=timout_type, help="Maximum run time (in seconds) of any test target before being terminated", required=False) + parser.add_argument('--globalTimeout', dest="global_timeout", type=timout_type, help="Maximum run time of the sequence before being terminated", required=False) parser.set_defaults(test_timeout=None) parser.set_defaults(global_timeout=None) args = parser.parse_args() - - if args.sequence_type == SequenceType.TEST_IMPACT_ANALYSIS and args.dst_commit == None: - raise ValueError("Test impact analysis sequence must have a change list") return args if __name__ == "__main__": args = parse_args() - tiaf = TestImpact(args.config, args.dst_commit) - return_code = tiaf.run(args.sequence_type, args.test_timeout, args.global_timeout) + tiaf = TestImpact(args.config, args.pipeline, args.dst_commit) + return_code = tiaf.run(args.suite, args.safe_mode, args.test_timeout, args.global_timeout) sys.exit(return_code) \ No newline at end of file From 46be3efd5de449c7e8c625f05e7674bdba32a0af Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 18:47:36 +0100 Subject: [PATCH 114/233] Add missing test impact pipe --- scripts/build/Platform/Windows/build_config.json | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index bb4a0decbf..98ed04250c 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -33,6 +33,7 @@ ], "steps": [ "profile_vs2019", + "test_impact_analysis", "asset_profile_vs2019", "test_cpu_profile_vs2019" ] From efd0d8bcc921e89e3db6d146f35f782fede0a7f9 Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 19:03:59 +0100 Subject: [PATCH 115/233] Fix detatched head in tiaf driver --- scripts/build/TestImpactAnalysis/tiaf.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/scripts/build/TestImpactAnalysis/tiaf.py b/scripts/build/TestImpactAnalysis/tiaf.py index 1523d39b4a..93b876836e 100644 --- a/scripts/build/TestImpactAnalysis/tiaf.py +++ b/scripts/build/TestImpactAnalysis/tiaf.py @@ -26,8 +26,10 @@ def is_child_path(parent_path, child_path): class TestImpact: def __init__(self, config_file, pipeline, dst_commit): self.__pipeline = pipeline + self.__dst_commit = dst_commit + self.__src_commit = None + self.__has_src_commit = False self.__parse_config_file(config_file) - self.__init_repo(dst_commit) if self.__use_test_impact_analysis and not self.__is_pipeline_of_truth: self.__generate_change_list() @@ -61,15 +63,6 @@ class TestImpact: self.__last_commit_hash_path = os.path.join(self.__historic_workspace, last_commit_hash_path_rel) print("The configuration file was parsed successfully.") - # Initializes the internal representation of the repository being targeted for test impact analysis - def __init_repo(self, dst_commit): - self.__repo = Repo(self.__repo_dir) - self.__branch = self.__repo.current_branch - self.__dst_commit = dst_commit - self.__src_commit = None - self.__has_src_commit = False - print(f"The repository is located at '{self.__repo_dir}' and the current branch is '{self.__branch}'.") - # Restricts change lists from checking in test impact analysis files def __check_for_restricted_files(self, file_path): if is_child_path(self.__active_workspace, file_path) or is_child_path(self.__historic_workspace, file_path) or is_child_path(self.__temp_workspace, file_path): From 112493c26bda15c425833af5f1b14ff3b627190c Mon Sep 17 00:00:00 2001 From: jonawals Date: Tue, 8 Jun 2021 19:27:37 +0100 Subject: [PATCH 116/233] Non pipline of truth no early exit experiment --- scripts/build/TestImpactAnalysis/tiaf.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/scripts/build/TestImpactAnalysis/tiaf.py b/scripts/build/TestImpactAnalysis/tiaf.py index 93b876836e..478322ca17 100644 --- a/scripts/build/TestImpactAnalysis/tiaf.py +++ b/scripts/build/TestImpactAnalysis/tiaf.py @@ -146,6 +146,8 @@ class TestImpact: # Runs the specified test sequence def run(self, suite, safe_mode, test_timeout, global_timeout): args = [] + pipeline_of_truth_test_failure_policy = "continue" + non_pipeline_of_truth_test_failure_policy = "continue" # Suite args.append(f"--suite={suite}") print(f"Test suite is set to '{suite}'.") @@ -170,8 +172,8 @@ class TestImpact: args.append("--sequence=seed") print("Sequence type is set to 'seed'.") # Test failure policy - args.append("--fpolicy=continue") - print("Test failure policy is set to 'continue'.") + args.append(f"--fpolicy={pipeline_of_truth_test_failure_policy}") + print(f"Test failure policy is set to '{pipeline_of_truth_test_failure_policy}'.") # Non pipeline of truth sequence else: if self.__has_change_list: @@ -192,8 +194,8 @@ class TestImpact: args.append("--sequence=regular") print("Sequence type is set to 'regular'.") # Test failure policy - args.append("--fpolicy=abort") - print("Test failure policy is set to 'abort'.") + args.append(f"--fpolicy={non_pipeline_of_truth_test_failure_policy}") + print(f"Test failure policy is set to '{non_pipeline_of_truth_test_failure_policy}'.") else: print("Test impact analysis ie disabled.") # Sequence type @@ -202,13 +204,13 @@ class TestImpact: # Pipeline of truth sequence if self.__is_pipeline_of_truth: # Test failure policy - args.append("--fpolicy=continue") - print("Test failure policy is set to 'continue'.") + args.append(f"--fpolicy={pipeline_of_truth_test_failure_policy}") + print(f"Test failure policy is set to '{pipeline_of_truth_test_failure_policy}'.") # Non pipeline of truth sequence else: # Test failure policy - args.append("--fpolicy=abort") - print("Test failure policy is set to 'abort'.") + args.append(f"--fpolicy={non_pipeline_of_truth_test_failure_policy}") + print(f"Test failure policy is set to '{non_pipeline_of_truth_test_failure_policy}'.") print("Args: ", end='') print(*args) From e795dd5210b1da9d689b4ce34c9eb155be868d5b Mon Sep 17 00:00:00 2001 From: moudgils Date: Tue, 8 Jun 2021 22:35:48 -0700 Subject: [PATCH 117/233] - Added enums for write mask and adding suppooort for that across all backends. - Batch calls to bind argument buffers - Fix swapchain creation for Editor --- .../Include/Atom/RHI.Reflect/RenderStates.h | 18 +++ .../RHI/DX12/Code/Source/RHI/Conversions.cpp | 25 ++- .../RHI/DX12/Code/Source/RHI/Conversions.h | 2 + .../RHI/Metal/Code/Source/RHI/CommandList.cpp | 143 +++++++++++++++--- .../RHI/Metal/Code/Source/RHI/CommandList.h | 4 + .../RHI/Metal/Code/Source/RHI/Conversions.cpp | 22 ++- .../Metal/Code/Source/RHI/PipelineLayout.cpp | 4 +- .../RHI/Metal/Code/Source/RHI/SwapChain.cpp | 29 ++-- .../RHI/Metal/Code/Source/RHI/SwapChain.h | 2 + .../RHI/Vulkan/Code/Source/RHI/Conversion.cpp | 8 +- 10 files changed, 213 insertions(+), 44 deletions(-) diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h index 51b15c4bcf..d5c55e2c7f 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h @@ -160,6 +160,24 @@ namespace AZ StencilState m_stencil; }; + enum class WriteChannel : uint32_t + { + ColorWriteMaskRed = 0, + ColorWriteMaskGreen, + ColorWriteMaskBlue, + ColorWriteMaskAlpha, + }; + + enum class WriteChannelMask : uint32_t + { + ColorWriteMaskNone = 0, + ColorWriteMaskRed = AZ_BIT(static_cast(WriteChannel::ColorWriteMaskRed)), + ColorWriteMaskGreen = AZ_BIT(static_cast(WriteChannel::ColorWriteMaskGreen)), + ColorWriteMaskBlue = AZ_BIT(static_cast(WriteChannel::ColorWriteMaskBlue)), + ColorWriteMaskAlpha = AZ_BIT(static_cast(WriteChannel::ColorWriteMaskAlpha)), + ColorWriteMaskAll = ColorWriteMaskRed | ColorWriteMaskGreen | ColorWriteMaskBlue | ColorWriteMaskAlpha + }; + struct TargetBlendState { AZ_TYPE_INFO(TargetBlendState, "{2CDF00FE-614D-44FC-929F-E6B50C348578}"); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp index a3d14e3803..73d80ed78c 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp @@ -10,6 +10,7 @@ * */ #include "RHI/Atom_RHI_DX12_precompiled.h" +#include #include #include #include @@ -1268,7 +1269,7 @@ namespace AZ dst.BlendOpAlpha = ConvertBlendOp(src.m_blendAlphaOp); dst.DestBlend = ConvertBlendFactor(src.m_blendDest); dst.DestBlendAlpha = ConvertBlendFactor(src.m_blendAlphaDest); - dst.RenderTargetWriteMask = src.m_writeMask; + dst.RenderTargetWriteMask = ConvertColorWriteMask(src.m_writeMask); dst.SrcBlend = ConvertBlendFactor(src.m_blendSource); dst.SrcBlendAlpha = ConvertBlendFactor(src.m_blendAlphaSource); dst.LogicOp = D3D12_LOGIC_OP_CLEAR; @@ -1355,6 +1356,28 @@ namespace AZ }; return table[(uint32_t)mask]; } + + uint32_t ConvertColorWriteMask(uint8_t writeMask) + { + uint32_t dflags = 0; + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) + { + dflags |= D3D12_COLOR_WRITE_ENABLE_RED; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) + { + dflags |= D3D12_COLOR_WRITE_ENABLE_GREEN; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) + { + dflags |= D3D12_COLOR_WRITE_ENABLE_BLUE; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) + { + dflags |= D3D12_COLOR_WRITE_ENABLE_ALPHA; + } + return dflags; + } D3D12_DEPTH_STENCIL_DESC ConvertDepthStencilState(const RHI::DepthStencilState& depthStencil) { diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h index d8385203f8..887598de79 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h @@ -164,5 +164,7 @@ namespace AZ uint32_t shaderRegisterSpace, D3D12_SHADER_VISIBILITY shaderVisibility, D3D12_STATIC_SAMPLER_DESC& staticSamplerDesc); + + uint32_t ConvertColorWriteMask(uint8_t writeMask); } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp index 10fbe372b1..7df8f1e547 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -249,68 +250,88 @@ namespace AZ ShaderResourceBindings& bindings = GetShaderResourceBindingsByPipelineType(stateType); const PipelineLayout& pipelineLayout = pipelineState->GetPipelineLayout(); - for (uint32_t srgIndex = 0; srgIndex < RHI::Limits::Pipeline::ShaderResourceGroupCountMax; ++srgIndex) + uint32_t bufferVertexRegisterIdMin = RHI::Limits::Pipeline::ShaderResourceGroupCountMax; + uint32_t bufferFragmentOrComputeRegisterIdMin = RHI::Limits::Pipeline::ShaderResourceGroupCountMax; + uint32_t bufferVertexRegisterIdMax = 0; + uint32_t bufferFragmentOrComputeRegisterIdMax = 0; + + MetalArgumentBufferArray mtlVertexArgBuffers; + MetalArgumentBufferArrayOffsets mtlVertexArgBufferOffsets; + MetalArgumentBufferArray mtlFragmentOrComputeArgBuffers; + MetalArgumentBufferArrayOffsets mtlFragmentOrComputeArgBufferOffsets; + + mtlVertexArgBuffers.fill(nil); + mtlFragmentOrComputeArgBuffers.fill(nil); + mtlVertexArgBufferOffsets.fill(0); + mtlFragmentOrComputeArgBufferOffsets.fill(0); + + for (uint32_t slot = 0; slot < RHI::Limits::Pipeline::ShaderResourceGroupCountMax; ++slot) { - const ShaderResourceGroup* shaderResourceGroup = bindings.m_srgsBySlot[srgIndex]; - uint32_t slotIndex = pipelineLayout.GetSlotByIndex(srgIndex); + const ShaderResourceGroup* shaderResourceGroup = bindings.m_srgsBySlot[slot]; + uint32_t slotIndex = pipelineLayout.GetIndexBySlot(slot); if(!shaderResourceGroup || slotIndex == RHI::Limits::Pipeline::ShaderResourceGroupCountMax) { continue; } - uint32_t srgVisIndex = pipelineLayout.GetSlotByIndex(shaderResourceGroup->GetBindingSlot()); + uint32_t srgVisIndex = pipelineLayout.GetIndexBySlot(shaderResourceGroup->GetBindingSlot()); const RHI::ShaderStageMask& srgVisInfo = pipelineLayout.GetSrgVisibility(srgVisIndex); - if (bindings.m_srgsByIndex[srgIndex] != shaderResourceGroup) + bool isSrgUpdatd = bindings.m_srgsByIndex[slot] != shaderResourceGroup; + if(isSrgUpdatd) { - bindings.m_srgsByIndex[srgIndex] = shaderResourceGroup; + bindings.m_srgsByIndex[slot] = shaderResourceGroup; auto& compiledArgBuffer = shaderResourceGroup->GetCompiledArgumentBuffer(); id argBuffer = compiledArgBuffer.GetArgEncoderBuffer(); size_t argBufferOffset = compiledArgBuffer.GetOffset(); if(srgVisInfo != RHI::ShaderStageMask::None) { - //For graphics and compute encoder bind the argument buffer + //For graphics and compute shader stages, cache all the argument buffers, offsets and track the min/max indices if(m_commandEncoderType == CommandEncoderType::Render) { id renderEncoder = GetEncoder>(); uint8_t numBitsSet = RHI::CountBitsSet(static_cast(srgVisInfo)); if( numBitsSet > 1 || srgVisInfo == RHI::ShaderStageMask::Vertex) { - [renderEncoder setVertexBuffer:argBuffer - offset:argBufferOffset - atIndex:slotIndex]; + mtlVertexArgBuffers[slotIndex] = argBuffer; + mtlVertexArgBufferOffsets[slotIndex] = argBufferOffset; + bufferVertexRegisterIdMin = AZStd::min(slotIndex, bufferVertexRegisterIdMin); + bufferVertexRegisterIdMax = AZStd::max(slotIndex, bufferVertexRegisterIdMax); + } if( numBitsSet > 1 || srgVisInfo == RHI::ShaderStageMask::Fragment) { - [renderEncoder setFragmentBuffer:argBuffer - offset:argBufferOffset - atIndex:slotIndex]; + mtlFragmentOrComputeArgBuffers[slotIndex] = argBuffer; + mtlFragmentOrComputeArgBufferOffsets[slotIndex] = argBufferOffset; + bufferFragmentOrComputeRegisterIdMin = AZStd::min(slotIndex, bufferFragmentOrComputeRegisterIdMin); + bufferFragmentOrComputeRegisterIdMax = AZStd::max(slotIndex, bufferFragmentOrComputeRegisterIdMax); } } else if(m_commandEncoderType == CommandEncoderType::Compute) { - id computeEncoder = GetEncoder>(); - [computeEncoder setBuffer:argBuffer - offset:argBufferOffset - atIndex:pipelineLayout.GetSlotByIndex(srgIndex)]; + mtlFragmentOrComputeArgBuffers[slotIndex] = argBuffer; + mtlFragmentOrComputeArgBufferOffsets[slotIndex] = argBufferOffset; + bufferFragmentOrComputeRegisterIdMin = AZStd::min(slotIndex, bufferFragmentOrComputeRegisterIdMin); + bufferFragmentOrComputeRegisterIdMax = AZStd::max(slotIndex, bufferFragmentOrComputeRegisterIdMax); } } } - //Check againgst the srg resources visibility hash as it is possible for draw items to have different PSO in the same pass. + //Check if the srg has been updated or if the srg resources visibility hash has been updated + //as it is possible for draw items to have different PSOs in the same pass. const AZ::HashValue64 srgResourcesVisHash = pipelineLayout.GetSrgResourcesVisibilityHash(srgVisIndex); - if(bindings.m_srgVisHashByIndex[srgIndex] != srgResourcesVisHash) + if(bindings.m_srgVisHashByIndex[slot] != srgResourcesVisHash || isSrgUpdatd) { - bindings.m_srgVisHashByIndex[srgIndex] = srgResourcesVisHash; + bindings.m_srgVisHashByIndex[slot] = srgResourcesVisHash; if(srgVisInfo != RHI::ShaderStageMask::None) { const ShaderResourceGroupVisibility& srgResourcesVisInfo = pipelineLayout.GetSrgResourcesVisibility(srgVisIndex); - //For graphics and compute encoder bind the argument buffer and - //make the resource resident for the duration of the work associated with the current scope - //and ensure that it's in a format compatible with the appropriate metal function. + //For graphics and compute encoder make the resource resident (call UseResource) for the duration + //of the work associated with the current scope and ensure that it's in a + //format compatible with the appropriate metal function. if(m_commandEncoderType == CommandEncoderType::Render) { shaderResourceGroup->AddUntrackedResourcesToEncoder(m_encoder, srgResourcesVisInfo); @@ -322,9 +343,81 @@ namespace AZ } } } - + + //For graphics and compute encoder bind all the argument buffers + if(m_commandEncoderType == CommandEncoderType::Render) + { + BindArgumentBuffers(RHI::ShaderStage::Vertex, bufferVertexRegisterIdMin, bufferVertexRegisterIdMax, mtlVertexArgBuffers, mtlVertexArgBufferOffsets); + BindArgumentBuffers(RHI::ShaderStage::Fragment, bufferFragmentOrComputeRegisterIdMin, bufferFragmentOrComputeRegisterIdMax, mtlFragmentOrComputeArgBuffers, mtlFragmentOrComputeArgBufferOffsets); + } + else if(m_commandEncoderType == CommandEncoderType::Compute) + { + BindArgumentBuffers(RHI::ShaderStage::Compute, bufferFragmentOrComputeRegisterIdMin, bufferFragmentOrComputeRegisterIdMax, mtlFragmentOrComputeArgBuffers, mtlFragmentOrComputeArgBufferOffsets); + } + return true; } + + void CommandList::BindArgumentBuffers(RHI::ShaderStage shaderStage, uint16_t registerIdMin, uint16_t registerIdMax, MetalArgumentBufferArray& mtlArgBuffers, MetalArgumentBufferArrayOffsets mtlArgBufferOffsets) + { + //Metal Api only lets you bind multiple argument buffers in an array as long as there are no gaps in the array + //In order to accomodate that we break up the calls when a gap is noticed in the array and reconfigure the NSRange. + uint16_t startingIndex = registerIdMin; + bool trackingRange = true; + for(int i = registerIdMin; i <= registerIdMax+1; i++) + { + if(trackingRange) + { + if(mtlArgBuffers[i] == nil) + { + NSRange range = { startingIndex, i-startingIndex }; + + switch(shaderStage) + { + case RHI::ShaderStage::Vertex: + { + id renderEncoder = GetEncoder>(); + [renderEncoder setVertexBuffers:&mtlArgBuffers[startingIndex] + offsets:&mtlArgBufferOffsets[startingIndex] + withRange:range]; + break; + } + case RHI::ShaderStage::Fragment: + { + id renderEncoder = GetEncoder>(); + [renderEncoder setFragmentBuffers:&mtlArgBuffers[startingIndex] + offsets:&mtlArgBufferOffsets[startingIndex] + withRange:range]; + break; + } + case RHI::ShaderStage::Compute: + { + id computeEncoder = GetEncoder>(); + [computeEncoder setBuffers:&mtlArgBuffers[startingIndex] + offsets:&mtlArgBufferOffsets[startingIndex] + withRange:range]; + break; + } + default: + { + AZ_Assert(false, "Not supported"); + } + } + + trackingRange = false; + + } + } + else + { + if(mtlArgBuffers[i] != nil) + { + startingIndex = i; + trackingRange = true; + } + } + } + } void CommandList::Submit(const RHI::DrawItem& drawItem) { @@ -486,7 +579,7 @@ namespace AZ void CommandList::SetStreamBuffers(const RHI::StreamBufferView* streams, uint32_t count) { - int bufferArrayLen = 0; + uint16_t bufferArrayLen = 0; AZStd::array, METAL_MAX_ENTRIES_BUFFER_ARG_TABLE> mtlStreamBuffers; AZStd::array mtlStreamBufferOffsets; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h index dd4e382471..9dd14cd175 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h @@ -102,6 +102,10 @@ namespace AZ AZStd::array m_srgVisHashByIndex; }; + using MetalArgumentBufferArray = AZStd::array, RHI::Limits::Pipeline::ShaderResourceGroupCountMax>; + using MetalArgumentBufferArrayOffsets = AZStd::array; + void BindArgumentBuffers(RHI::ShaderStage shaderStage, uint16_t registerIdMin, uint16_t registerIdMax, MetalArgumentBufferArray& mtlArgBuffers, MetalArgumentBufferArrayOffsets mtlArgBufferOffsets); + ShaderResourceBindings& GetShaderResourceBindingsByPipelineType(RHI::PipelineStateType pipelineType); //! This is kept as a separate struct so that we can robustly reset it. Every property diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp index f95e871d33..e41ba9bf2a 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp @@ -12,6 +12,7 @@ #include "Atom_RHI_Metal_precompiled.h" #include +#include #include #include #include @@ -456,8 +457,25 @@ namespace AZ MTLColorWriteMask ConvertColorWriteMask(AZ::u8 writeMask) { - //todo::Based on the mask set the correct writemask - return MTLColorWriteMaskAll; + MTLColorWriteMask colorMask = MTLColorWriteMaskNone; + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) + { + colorMask |= MTLColorWriteMaskRed; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) + { + colorMask |= MTLColorWriteMaskGreen; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) + { + colorMask |= MTLColorWriteMaskBlue; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) + { + colorMask |= MTLColorWriteMaskAlpha; + } + + return colorMask; } MTLVertexFormat ConvertVertexFormat(RHI::Format format) diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp index 70d36f8d6d..9a30a04deb 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp @@ -125,12 +125,12 @@ namespace AZ size_t PipelineLayout::GetSlotByIndex(size_t index) const { - return m_slotToIndexTable[index]; + return m_indexToSlotTable[index]; } size_t PipelineLayout::GetIndexBySlot(size_t slot) const { - return m_indexToSlotTable[slot]; + return m_slotToIndexTable[slot]; } const RHI::ShaderStageMask& PipelineLayout::GetSrgVisibility(uint32_t index) const diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp index 1f870548cc..94599bc390 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp @@ -73,6 +73,10 @@ namespace AZ m_metalView.metalLayer.drawableSize = CGSizeMake(descriptor.m_dimensions.m_imageWidth, descriptor.m_dimensions.m_imageHeight); } + else + { + AddSubView(); + } m_drawables.resize(descriptor.m_dimensions.m_imageCount); @@ -83,6 +87,20 @@ namespace AZ return RHI::ResultCode::Success; } + void SwapChain::AddSubView() + { + NativeViewType* superView = reinterpret_cast(m_nativeWindow); + + CGFloat screenScale = Platform::GetScreenScale(); + CGRect screenBounds = [superView bounds]; + m_metalView = [[RHIMetalView alloc] initWithFrame: screenBounds + scale: screenScale + device: m_mtlDevice]; + + [m_metalView retain]; + [superView addSubview: m_metalView]; + } + void SwapChain::ShutdownInternal() { if (m_viewController) @@ -161,16 +179,7 @@ namespace AZ } else { - NativeViewType* superView = reinterpret_cast(m_nativeWindow); - - CGFloat screenScale = Platform::GetScreenScale(); - CGRect screenBounds = [superView bounds]; - m_metalView = [[RHIMetalView alloc] initWithFrame: screenBounds - scale: screenScale - device: m_mtlDevice]; - - [m_metalView retain]; - [superView addSubview: m_metalView]; + AddSubView(); } } return RHI::ResultCode::Success; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h index d455f3dd0c..dfe8b3365d 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h @@ -49,6 +49,8 @@ namespace AZ RHI::ResultCode ResizeInternal(const RHI::SwapChainDimensions& dimensions, RHI::SwapChainDimensions* nativeDimensions) override; ////////////////////////////////////////////////////////////////////////// + void AddSubView(); + id m_mtlCommandBuffer; RHIMetalView* m_metalView = nullptr; NativeViewControllerType* m_viewController = nullptr; diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp index accc18b5ec..7945dd2894 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp @@ -334,19 +334,19 @@ namespace AZ VkColorComponentFlags ConvertComponentFlags(uint8_t sflags) { VkColorComponentFlags dflags = 0; - if (RHI::CheckBitsAny(sflags, static_cast(1))) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) { dflags |= VK_COLOR_COMPONENT_R_BIT; } - if (RHI::CheckBitsAny(sflags, static_cast(2))) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) { dflags |= VK_COLOR_COMPONENT_G_BIT; } - if (RHI::CheckBitsAny(sflags, static_cast(4))) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) { dflags |= VK_COLOR_COMPONENT_B_BIT; } - if (RHI::CheckBitsAny(sflags, static_cast(8))) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) { dflags |= VK_COLOR_COMPONENT_A_BIT; } From 351d54e4f603c13c66383c40ed3c15851ccd3ec3 Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 9 Jun 2021 08:49:18 +0100 Subject: [PATCH 118/233] Fix non-existent dir for historic data --- scripts/build/TestImpactAnalysis/tiaf.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/build/TestImpactAnalysis/tiaf.py b/scripts/build/TestImpactAnalysis/tiaf.py index 478322ca17..887d5f1085 100644 --- a/scripts/build/TestImpactAnalysis/tiaf.py +++ b/scripts/build/TestImpactAnalysis/tiaf.py @@ -59,8 +59,8 @@ class TestImpact: self.__historic_workspace = config["workspace"]["historic"]["root"] self.__temp_workspace = config["workspace"]["temp"]["root"] # Last commit hash - last_commit_hash_path_rel = config["workspace"]["historic"]["relative_paths"]["last_run_hash_file"] - self.__last_commit_hash_path = os.path.join(self.__historic_workspace, last_commit_hash_path_rel) + last_commit_hash_path_file = config["workspace"]["historic"]["relative_paths"]["last_run_hash_file"] + self.__last_commit_hash_path = os.path.join(self.__historic_workspace, last_commit_hash_path_file) print("The configuration file was parsed successfully.") # Restricts change lists from checking in test impact analysis files @@ -77,6 +77,7 @@ class TestImpact: self.__has_src_commit = True def __write_last_run_hash(self, last_run_hash): + os.mkdir(self.__historic_workspace) f = open(self.__last_commit_hash_path, "w") f.write(last_run_hash) f.close() From d44a20f4702624521c69951fa5bcad82c50c3f95 Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 9 Jun 2021 08:59:11 +0100 Subject: [PATCH 119/233] Add flag for non-seed sequence test failure policy --- .../build/Platform/Windows/build_config.json | 2 +- scripts/build/TestImpactAnalysis/tiaf.py | 21 +++++++------------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 98ed04250c..9201e13a80 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -88,7 +88,7 @@ "COMMAND": "python_windows.cmd", "PARAMETERS": { "SCRIPT_PATH": "scripts/build/TestImpactAnalysis/tiaf_driver.py", - "SCRIPT_PARAMETERS": "--safeMode --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.profile.json\"" + "SCRIPT_PARAMETERS": "--testFailurePolicy abort --safeMode --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.profile.json\"" } }, "debug_vs2019": { diff --git a/scripts/build/TestImpactAnalysis/tiaf.py b/scripts/build/TestImpactAnalysis/tiaf.py index 887d5f1085..c040ae1147 100644 --- a/scripts/build/TestImpactAnalysis/tiaf.py +++ b/scripts/build/TestImpactAnalysis/tiaf.py @@ -71,7 +71,7 @@ class TestImpact: def __read_last_run_hash(self): self.__has_src_commit = False if os.path.isfile(self.__last_commit_hash_path): - print(f"Previous commit hash found at '{self.__last_commit_hash_path}'.") + print(f"Previous commit hash found at '{self.__last_commit_hash_path}'.") with open(self.__last_commit_hash_path) as file: self.__src_commit = file.read() self.__has_src_commit = True @@ -145,10 +145,9 @@ class TestImpact: return # Runs the specified test sequence - def run(self, suite, safe_mode, test_timeout, global_timeout): + def run(self, suite, test_failure_policy, safe_mode, test_timeout, global_timeout): args = [] pipeline_of_truth_test_failure_policy = "continue" - non_pipeline_of_truth_test_failure_policy = "continue" # Suite args.append(f"--suite={suite}") print(f"Test suite is set to '{suite}'.") @@ -159,14 +158,8 @@ class TestImpact: if global_timeout != None: args.append(f"--gtimeout={global_timeout}") print(f"Global sequence timeout is set to {test_timeout} seconds.") - # If test impact analysis is enabled: - # -> Pipleine of truth will perform a seed that will continue until the sequence is complete regardless of test failues - # -> Non pipline of truth will attempt to perform an impact analysis sequence and exit early upon the fist test failure - # If test impact analysis is disabled: - # -> Pipleine of truth will perform a regular sequence that will continue until the sequence is complete regardless of test failues - # -> Non pipline of truth will perform a regular sequence and exit early upon the fist test failure if self.__use_test_impact_analysis: - print("Test impact analysis ie enabled.") + print("Test impact analysis is enabled.") # Pipeline of truth sequence if self.__is_pipeline_of_truth: # Sequence type @@ -195,8 +188,8 @@ class TestImpact: args.append("--sequence=regular") print("Sequence type is set to 'regular'.") # Test failure policy - args.append(f"--fpolicy={non_pipeline_of_truth_test_failure_policy}") - print(f"Test failure policy is set to '{non_pipeline_of_truth_test_failure_policy}'.") + args.append(f"--fpolicy={test_failure_policy}") + print(f"Test failure policy is set to '{test_failure_policy}'.") else: print("Test impact analysis ie disabled.") # Sequence type @@ -210,8 +203,8 @@ class TestImpact: # Non pipeline of truth sequence else: # Test failure policy - args.append(f"--fpolicy={non_pipeline_of_truth_test_failure_policy}") - print(f"Test failure policy is set to '{non_pipeline_of_truth_test_failure_policy}'.") + args.append(f"--fpolicy={test_failure_policy}") + print(f"Test failure policy is set to '{test_failure_policy}'.") print("Args: ", end='') print(*args) From fdbc15b195037bc64740df42af5406f5f6e13ea9 Mon Sep 17 00:00:00 2001 From: jonawals Date: Wed, 9 Jun 2021 10:00:53 +0100 Subject: [PATCH 120/233] Fix missing args for tiaf driver --- scripts/build/TestImpactAnalysis/tiaf_driver.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/build/TestImpactAnalysis/tiaf_driver.py b/scripts/build/TestImpactAnalysis/tiaf_driver.py index 917fd66fc6..7ff73e505b 100644 --- a/scripts/build/TestImpactAnalysis/tiaf_driver.py +++ b/scripts/build/TestImpactAnalysis/tiaf_driver.py @@ -30,15 +30,23 @@ def parse_args(): if value <= 0: raise ValueError("Timer values must be positive integers") return value - + + def test_failure_policy(value): + if value == "continue" or value == "abort" or value == "ignore": + return value + else: + raise ValueError("Test failure policy must be 'abort', 'continue' or 'ignore'") + parser = argparse.ArgumentParser() parser.add_argument('--config', dest="config", type=file_path, help="Path to the test impact analysis framework configuration file", required=True) parser.add_argument('--pipeline', dest="pipeline", help="Pipeline the test impact analysis framework is running on", required=True) parser.add_argument('--destCommit', dest="dst_commit", help="Commit to run test impact analysis on (ignored when seeding)", required=True) parser.add_argument('--suite', dest="suite", help="Test suite to run", required=True) + parser.add_argument('--testFailurePolicy', dest="test_failure_policy", type=test_failure_policy, help="Test failure policy for regular and test impact sequences (ignored when seeding)", required=True) parser.add_argument('--safeMode', dest="safe_mode", action='store_true', help="Run impact analysis tests in safe mode (ignored when seeding)") parser.add_argument('--testTimeout', dest="test_timeout", type=timout_type, help="Maximum run time (in seconds) of any test target before being terminated", required=False) parser.add_argument('--globalTimeout', dest="global_timeout", type=timout_type, help="Maximum run time of the sequence before being terminated", required=False) + parser.set_defaults(test_failure_policy="abort") parser.set_defaults(test_timeout=None) parser.set_defaults(global_timeout=None) args = parser.parse_args() @@ -48,5 +56,5 @@ def parse_args(): if __name__ == "__main__": args = parse_args() tiaf = TestImpact(args.config, args.pipeline, args.dst_commit) - return_code = tiaf.run(args.suite, args.safe_mode, args.test_timeout, args.global_timeout) + return_code = tiaf.run(args.suite, args.test_failure_policy, args.safe_mode, args.test_timeout, args.global_timeout) sys.exit(return_code) \ No newline at end of file From 66bbcb08e51991b9140a93a59f9e3a455e962dce Mon Sep 17 00:00:00 2001 From: igarri Date: Wed, 9 Jun 2021 11:02:09 +0100 Subject: [PATCH 121/233] Fixing Thumbnails preview from TableView --- .../AssetBrowser/AssetBrowserTableModel.cpp | 4 +- .../Views/AssetBrowserTreeView.cpp | 7 +-- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 46 ++++--------------- .../AzAssetBrowser/AzAssetBrowserWindow.h | 1 - 4 files changed, 12 insertions(+), 46 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index 70d66f4e1f..60f15970b1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -95,7 +95,9 @@ namespace AzToolsFramework for (int i = 0; i < rows; ++i) { QModelIndex index = model->index(i, 0, parent); - if (!model->hasChildren(index)) + AssetBrowserEntry* entry = GetAssetEntry(m_filterModel->mapToSource(index)); + //We only wanna see the source assets. + if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source) { beginInsertRows(parent, row, row); m_indexMap[row] = index; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp index 12a43218d4..026343c2a4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp @@ -100,16 +100,11 @@ namespace AzToolsFramework AZStd::vector AssetBrowserTreeView::GetSelectedAssets() const { - const QModelIndexList& selectedIndexes = selectionModel()->selectedRows(); QModelIndexList sourceIndexes; for (const auto& index : selectedIndexes) { - //If we check for more than one column then the model will try to select the same entry several times. - if (index.column() == 0) - { - sourceIndexes.push_back(m_assetBrowserSortFilterProxyModel->mapToSource(index)); - } + sourceIndexes.push_back(m_assetBrowserSortFilterProxyModel->mapToSource(index)); } AZStd::vector entries; diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 60ff6f9549..1bc8b9d324 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -97,7 +97,7 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) &AzAssetBrowserWindow::SelectionChangedSlot); connect( m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, - &AzAssetBrowserWindow::DoubleClickedItemTableModel); + &AzAssetBrowserWindow::DoubleClickedItem); connect( m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget, &AzAssetBrowser::SearchWidget::ClearStringFilter); @@ -163,7 +163,10 @@ QObject* AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(QObject* pa void AzAssetBrowserWindow::UpdatePreview() const { - auto selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); + const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->isVisible() + ? m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets() + : m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets(); + if (selectedAssets.size() != 1) { m_ui->m_previewerFrame->Clear(); @@ -234,43 +237,10 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& { namespace AzAssetBrowser = AzToolsFramework::AssetBrowser; - // assumption: Double clicking an item selects it before telling us we double clicked it. - const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); - for (const AzAssetBrowser::AssetBrowserEntry* entry : selectedAssets) - { - AZ::Data::AssetId assetIdToOpen; - AZStd::string fullFilePath; - - if (const AzAssetBrowser::ProductAssetBrowserEntry* productEntry = azrtti_cast(entry)) - { - assetIdToOpen = productEntry->GetAssetId(); - fullFilePath = entry->GetFullPath(); - } - else if (const AzAssetBrowser::SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry)) - { - // manufacture an empty AssetID with the source's UUID - assetIdToOpen = AZ::Data::AssetId(sourceEntry->GetSourceUuid(), 0); - fullFilePath = entry->GetFullPath(); - } + const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->isVisible() + ? m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets() + : m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets(); - bool handledBySomeone = false; - if (assetIdToOpen.IsValid()) - { - AzAssetBrowser::AssetBrowserInteractionNotificationBus::Broadcast( - &AzAssetBrowser::AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); - } - - if (!handledBySomeone && !fullFilePath.empty()) - { - AzAssetBrowserRequestHandler::OpenWithOS(fullFilePath); - } - } -} - -void AzAssetBrowserWindow::DoubleClickedItemTableModel([[maybe_unused]] const QModelIndex& element) -{ - namespace AzAssetBrowser = AzToolsFramework::AssetBrowser; - const auto& selectedAssets = m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets(); for (const AzAssetBrowser::AssetBrowserEntry* entry : selectedAssets) { AZ::Data::AssetId assetIdToOpen; diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h index 1174335995..57a5371ab9 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h @@ -63,7 +63,6 @@ private: private Q_SLOTS: void SelectionChangedSlot(const QItemSelection& selected, const QItemSelection& deselected) const; void DoubleClickedItem(const QModelIndex& element); - void DoubleClickedItemTableModel(const QModelIndex& element); void SwitchDisplayView(bool state); void LockToDefaultView(bool state); }; From dda6fee2276f5e2418159a40dc6f978ddf789ab6 Mon Sep 17 00:00:00 2001 From: gallowj Date: Wed, 9 Jun 2021 09:58:38 -0500 Subject: [PATCH 122/233] fixed a type-o --- Gems/AtomContent/gem.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/AtomContent/gem.json b/Gems/AtomContent/gem.json index 941e7dea20..b043cbbaca 100644 --- a/Gems/AtomContent/gem.json +++ b/Gems/AtomContent/gem.json @@ -8,7 +8,7 @@ "Gem" ], "user_tags": [ - "AtomConent" + "AtomContent" ], "icon_path": "preview.png" } From d5916630b88fc19bd017c9fff6591a3db2a7818a Mon Sep 17 00:00:00 2001 From: gallowj Date: Wed, 9 Jun 2021 14:27:59 -0500 Subject: [PATCH 123/233] Changes related to ATOM-15021, this allows maya on py2.7 to work with .o3de\boostrap changes --- .../Solutions/.wing/DCCsi_7x.wpr | 6 ++++++ .../DccScriptingInterface/azpy/config_utils.py | 8 +++++--- .../DccScriptingInterface/azpy/constants.py | 13 ++++++++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr index d57e91f35d..aa7a8c4bd9 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr @@ -69,10 +69,16 @@ proj.launch-config = {loc('../../SDK/Atom/Scripts/Python/DCC_Materials/maya_mate loc('../../azpy/__init__.py'): ('custom', (u'', 'launch-oobMrvXFf1SwtYBg')), + loc('../../azpy/constants.py'): ('custom', + (u'', + 'launch-GeaM41WYMGA1sEfm')), loc('../../azpy/env_base.py'): ('project', (u'', 'launch-GeaM41WYMGA1sEfm')), loc('../../azpy/maya/callbacks/node_message_callback_handler.py'): ('c'\ 'ustom', + (u'', + 'launch-GeaM41WYMGA1sEfm')), + loc('../../config.py'): ('custom', (u'', 'launch-GeaM41WYMGA1sEfm'))} diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py index 0a0c6b8337..5b5c5aa079 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py @@ -165,12 +165,14 @@ def get_current_project(): bootstrap_box = None try: - bootstrap_box = Box.from_json(filename=PATH_USER_O3DE_BOOTSTRAP, + bootstrap_box = Box.from_json(filename=str(Path(PATH_USER_O3DE_BOOTSTRAP).resolve()), encoding="utf-8", errors="strict", object_pairs_hook=OrderedDict) - except FileExistsError as e: - _LOGGER.error('File does not exist: {}'.format(PATH_USER_O3DE_BOOTSTRAP)) + except Exception as e: + # this file runs in py2.7 for Maya 2020, FileExistsError is not defined + _LOGGER.error('FileExistsError: {}'.format(PATH_USER_O3DE_BOOTSTRAP)) + _LOGGER.error('exception is: {}'.format(e)) if bootstrap_box: # this seems fairly hard coded - what if the data changes? diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py index e10221d324..7f6b0e4523 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py @@ -226,7 +226,18 @@ TAG_DEFAULT_PY = str('Launch_pyBASE.bat') FILENAME_DEFAULT_CONFIG = str('DCCSI_config.json') # new o3de related paths -PATH_USER_O3DE = str('{home}\\{o3de}').format(home=expanduser("~"), +# os.path.expanduser("~") returns different values in py2.7 vs 3 +PATH_USER_HOME = expanduser("~") +_LOGGER.debug('user home: {}'.format(PATH_USER_HOME)) + +# special case, make sure didn't return \documents +parts = os.path.split(PATH_USER_HOME) + +if str(parts[1].lower()) == 'documents': + PATH_USER_HOME = parts[0] + _LOGGER.debug('user home CORRECTED: {}'.format(PATH_USER_HOME)) + +PATH_USER_O3DE = str('{home}\\{o3de}').format(home=PATH_USER_HOME, o3de=TAG_O3DE_FOLDER) PATH_USER_O3DE_REGISTRY = str('{0}\\Registry').format(PATH_USER_O3DE) PATH_USER_O3DE_BOOTSTRAP = str('{reg}\\{file}').format(reg=PATH_USER_O3DE_REGISTRY, From ca475fab80dff4e94fdcfd4334447dc9b2813347 Mon Sep 17 00:00:00 2001 From: moudgils Date: Wed, 9 Jun 2021 15:28:22 -0700 Subject: [PATCH 124/233] Fix Editor crash where the scissor regions is bigger than the metal drawable --- .../RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp b/Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp index cc8c4c80c0..b81c51a57b 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp @@ -94,7 +94,7 @@ namespace Platform void ResizeInternal(RHIMetalView* metalView, CGSize viewSize) { - [metalView resizeSubviewsWithOldSize:viewSize]; + [metalView.metalLayer setDrawableSize: viewSize]; } RHIMetalView* GetMetalView(NativeWindowType* nativeWindow) From a55edd518ace92f3ef6de5e660271b5d7be49376 Mon Sep 17 00:00:00 2001 From: moudgils Date: Wed, 9 Jun 2021 18:35:52 -0700 Subject: [PATCH 125/233] Update some comments --- Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp | 3 ++- Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h | 2 ++ Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp index 5d6c275184..6bdb5a4aa8 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp @@ -386,8 +386,9 @@ namespace AZ void ArgumentBuffer::AddUntrackedResourcesToEncoder(id commandEncoder, const ShaderResourceGroupVisibility& srgResourcesVisInfo) const { - + //Map tp cache all the resoources based on the usage as we can batch all the resources for a given usage ComputeResourcesToMakeResidentMap resourcesToMakeResidentCompute; + //Map tp cache all the resoources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage GraphicsResourcesToMakeResidentMap resourcesToMakeResidentGraphics; //Cache the constant buffer associated with a srg diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h index 06380162b6..eab9389e9b 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h @@ -125,7 +125,9 @@ namespace AZ AZStd::array, MaxEntriesInArgTable> m_resourceArray; int m_resourceArrayLen = 0; }; + //Map tp cache all the resoources based on the usage as we can batch all the resources for a given usage using ComputeResourcesToMakeResidentMap = AZStd::unordered_map; + //Map tp cache all the resoources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage using GraphicsResourcesToMakeResidentMap = AZStd::unordered_map, MetalResourceArray>; void CollectResourcesForCompute(id encoder, const ResourceBindingsSet& resourceBindingData, ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp index 7df8f1e547..5546be1bd9 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp @@ -255,6 +255,7 @@ namespace AZ uint32_t bufferVertexRegisterIdMax = 0; uint32_t bufferFragmentOrComputeRegisterIdMax = 0; + //Arrays to cache all the buffers and offsets in order to make batch calls MetalArgumentBufferArray mtlVertexArgBuffers; MetalArgumentBufferArrayOffsets mtlVertexArgBufferOffsets; MetalArgumentBufferArray mtlFragmentOrComputeArgBuffers; @@ -298,7 +299,6 @@ namespace AZ mtlVertexArgBufferOffsets[slotIndex] = argBufferOffset; bufferVertexRegisterIdMin = AZStd::min(slotIndex, bufferVertexRegisterIdMin); bufferVertexRegisterIdMax = AZStd::max(slotIndex, bufferVertexRegisterIdMax); - } if( numBitsSet > 1 || srgVisInfo == RHI::ShaderStageMask::Fragment) @@ -595,7 +595,7 @@ namespace AZ AZ_Assert(count <= METAL_MAX_ENTRIES_BUFFER_ARG_TABLE , "Slots needed cannot exceed METAL_MAX_ENTRIES_BUFFER_ARG_TABLE"); NSRange range = {METAL_MAX_ENTRIES_BUFFER_ARG_TABLE - count, count}; - //For metal the stream buffers are populated from bottom to top as the top slots are taken by argument buffers + //The stream buffers are populated from bottom to top as the top slots are taken by argument buffers for (int i = count-1; i >= 0; --i) { if (streams[i].GetBuffer()) From 8e3a68a34f02903942622ee4863d62433c7bf325 Mon Sep 17 00:00:00 2001 From: moudgils Date: Wed, 9 Jun 2021 18:37:27 -0700 Subject: [PATCH 126/233] Fix comment spelling --- Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp | 4 ++-- Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp index 6bdb5a4aa8..068bc5f162 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp @@ -386,9 +386,9 @@ namespace AZ void ArgumentBuffer::AddUntrackedResourcesToEncoder(id commandEncoder, const ShaderResourceGroupVisibility& srgResourcesVisInfo) const { - //Map tp cache all the resoources based on the usage as we can batch all the resources for a given usage + //Map to cache all the resources based on the usage as we can batch all the resources for a given usage ComputeResourcesToMakeResidentMap resourcesToMakeResidentCompute; - //Map tp cache all the resoources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage + //Map to cache all the resources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage GraphicsResourcesToMakeResidentMap resourcesToMakeResidentGraphics; //Cache the constant buffer associated with a srg diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h index eab9389e9b..7cc77ae478 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h @@ -125,9 +125,9 @@ namespace AZ AZStd::array, MaxEntriesInArgTable> m_resourceArray; int m_resourceArrayLen = 0; }; - //Map tp cache all the resoources based on the usage as we can batch all the resources for a given usage + //Map to cache all the resources based on the usage as we can batch all the resources for a given usage using ComputeResourcesToMakeResidentMap = AZStd::unordered_map; - //Map tp cache all the resoources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage + //Map to cache all the resources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage using GraphicsResourcesToMakeResidentMap = AZStd::unordered_map, MetalResourceArray>; void CollectResourcesForCompute(id encoder, const ResourceBindingsSet& resourceBindingData, ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; From 169d4da28848bda599e48f7896ffe0fa4c7aa086 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 10 Jun 2021 14:54:46 +0100 Subject: [PATCH 127/233] Address PR comments --- .../Frontend/Console/CMakeLists.txt | 3 +- .../Console/{Static => }/Code/CMakeLists.txt | 41 +- .../TestImpactConsoleMain.h | 0 .../Source/TestImpactCommandLineOptions.cpp | 0 .../Source/TestImpactCommandLineOptions.h | 0 .../TestImpactCommandLineOptionsException.h | 0 .../TestImpactCommandLineOptionsUtils.cpp | 0 .../TestImpactCommandLineOptionsUtils.h | 0 .../Code/Source/TestImpactConsole.cpp | 0 .../Code/Source/TestImpactConsoleMain.cpp | 0 ...tImpactConsoleTestSequenceEventHandler.cpp | 0 ...estImpactConsoleTestSequenceEventHandler.h | 0 .../Code/Source/TestImpactConsoleUtils.cpp | 0 .../Code/Source/TestImpactConsoleUtils.h | 0 .../TestImpactRuntimeConfigurationFactory.cpp | 0 .../TestImpactRuntimeConfigurationFactory.h | 0 .../TestImpactCommandLineOptionsTest.cpp | 1436 +++++++++++++++++ .../Code/Tests/TestImpactConsoleMainTest.cpp | 0 ...actConsoleTestSequenceEventHandlerTest.cpp | 42 + ...estImpactFrontendConsoleStaticTestMain.cpp | 34 + ...tImpactRuntimeConfigurationFactoryTest.cpp | 27 + ...pactframework_frontend_console_files.cmake | 0 ...mework_frontend_console_static_files.cmake | 0 ..._frontend_console_static_tests_files.cmake | 5 + .../Console/Executable/CMakeLists.txt | 12 - .../Console/Executable/Code/CMakeLists.txt | 25 - .../Frontend/Console/Static/CMakeLists.txt | 12 - .../Runtime/Code/CMakeLists.txt | 91 +- cmake/LYTestWrappers.cmake | 1 + .../build/Platform/Windows/build_config.json | 2 +- scripts/build/TestImpactAnalysis/tiaf.py | 47 +- .../build/TestImpactAnalysis/tiaf_driver.py | 3 +- 32 files changed, 1624 insertions(+), 157 deletions(-) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/CMakeLists.txt (64%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Include/TestImpactFramework/TestImpactConsoleMain.h (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Source/TestImpactCommandLineOptions.cpp (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Source/TestImpactCommandLineOptions.h (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Source/TestImpactCommandLineOptionsException.h (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Source/TestImpactCommandLineOptionsUtils.cpp (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Source/TestImpactCommandLineOptionsUtils.h (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Executable => }/Code/Source/TestImpactConsole.cpp (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Source/TestImpactConsoleMain.cpp (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Source/TestImpactConsoleTestSequenceEventHandler.h (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Source/TestImpactConsoleUtils.cpp (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Source/TestImpactConsoleUtils.h (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Source/TestImpactRuntimeConfigurationFactory.cpp (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/Source/TestImpactRuntimeConfigurationFactory.h (100%) create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactCommandLineOptionsTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleMainTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleTestSequenceEventHandlerTest.cpp create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactFrontendConsoleStaticTestMain.cpp create mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactRuntimeConfigurationFactoryTest.cpp rename Code/Tools/TestImpactFramework/Frontend/Console/{Executable => }/Code/testimpactframework_frontend_console_files.cmake (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/testimpactframework_frontend_console_static_files.cmake (100%) rename Code/Tools/TestImpactFramework/Frontend/Console/{Static => }/Code/testimpactframework_frontend_console_static_tests_files.cmake (67%) delete mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Executable/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Static/CMakeLists.txt diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/CMakeLists.txt index a25d506156..8298bb7123 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/CMakeLists.txt +++ b/Code/Tools/TestImpactFramework/Frontend/Console/CMakeLists.txt @@ -9,5 +9,4 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -add_subdirectory(Executable) -add_subdirectory(Static) \ No newline at end of file +add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/Code/CMakeLists.txt similarity index 64% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/CMakeLists.txt rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/CMakeLists.txt index 2302cb5484..a26814fa51 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/CMakeLists.txt +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Code/CMakeLists.txt @@ -24,27 +24,42 @@ ly_add_target( AZ::TestImpact.Runtime.Static ) -################################################################################ -# Tests -################################################################################ - ly_add_target( - NAME TestImpact.Frontend.Console.Static.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAME TestImpact.Frontend.Console EXECUTABLE + OUTPUT_NAME tiaf NAMESPACE AZ FILES_CMAKE - testimpactframework_frontend_console_static_tests_files.cmake + testimpactframework_frontend_console_files.cmake INCLUDE_DIRECTORIES PRIVATE - Include Source - Tests BUILD_DEPENDENCIES PRIVATE - AZ::AzTestShared - AZ::AzTest AZ::TestImpact.Frontend.Console.Static ) -ly_add_googletest( - NAME AZ::TestImpact.Frontend.Console.Static.Tests -) +################################################################################ +# Tests +################################################################################ + +# Disbled:SPEC-7246 +#ly_add_target( +# NAME TestImpact.Frontend.Console.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} +# NAMESPACE AZ +# FILES_CMAKE +# testimpactframework_frontend_console_static_tests_files.cmake +# INCLUDE_DIRECTORIES +# PRIVATE +# Include +# Source +# Tests +# BUILD_DEPENDENCIES +# PRIVATE +# AZ::AzTestShared +# AZ::AzTest +# AZ::TestImpact.Frontend.Console.Static +#) +# +#ly_add_googletest( +# NAME AZ::TestImpact.Frontend.Console.Static.Tests +#) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleMain.h b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Include/TestImpactFramework/TestImpactConsoleMain.h similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Include/TestImpactFramework/TestImpactConsoleMain.h rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Include/TestImpactFramework/TestImpactConsoleMain.h diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactCommandLineOptions.cpp similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.cpp rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactCommandLineOptions.cpp diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactCommandLineOptions.h similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptions.h rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactCommandLineOptions.h diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsException.h b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactCommandLineOptionsException.h similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsException.h rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactCommandLineOptionsException.h diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactCommandLineOptionsUtils.cpp similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.cpp rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactCommandLineOptionsUtils.cpp diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactCommandLineOptionsUtils.h similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactCommandLineOptionsUtils.h rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactCommandLineOptionsUtils.h diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/Source/TestImpactConsole.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsole.cpp similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/Source/TestImpactConsole.cpp rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsole.cpp diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleMain.cpp similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleMain.cpp rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleMain.cpp diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.h b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleTestSequenceEventHandler.h similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleTestSequenceEventHandler.h rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleTestSequenceEventHandler.h diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleUtils.cpp similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.cpp rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleUtils.cpp diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.h b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleUtils.h similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactConsoleUtils.h rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleUtils.h diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactRuntimeConfigurationFactory.cpp similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.cpp rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactRuntimeConfigurationFactory.cpp diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.h b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactRuntimeConfigurationFactory.h similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/Source/TestImpactRuntimeConfigurationFactory.h rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactRuntimeConfigurationFactory.h diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactCommandLineOptionsTest.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactCommandLineOptionsTest.cpp new file mode 100644 index 0000000000..cd02f5c5c7 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactCommandLineOptionsTest.cpp @@ -0,0 +1,1436 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include +#include + +namespace UnitTest +{ + class CommandLineOptionsTestFixture + : public AllocatorsTestFixture + { + public: + void SetUp() override + { + AllocatorsTestFixture::SetUp(); + m_args.push_back("program.exe"); + } + protected: + void InitOptions(); + + AZStd::unique_ptr m_options; + AZStd::vector m_args; + }; + + void CommandLineOptionsTestFixture::InitOptions() + { + m_options = AZStd::make_unique(m_args.size(), const_cast(m_args.data())); + } + + TEST_F(CommandLineOptionsTestFixture, CheckEmptyArgs_ExpectDefaultValues) + { + InitOptions(); + EXPECT_EQ(m_options->GetConfigurationFile(), LY_TEST_IMPACT_DEFAULT_CONFIG_FILE); + EXPECT_EQ(m_options->GetFailedTestCoveragePolicy(), TestImpact::Policy::FailedTestCoverage::Keep); + EXPECT_EQ(m_options->GetExecutionFailurePolicy(), TestImpact::Policy::ExecutionFailure::Continue); + EXPECT_FALSE(m_options->GetGlobalTimeout().has_value()); + EXPECT_FALSE(m_options->GetTestTargetTimeout().has_value()); + EXPECT_FALSE(m_options->GetMaxConcurrency().has_value()); + EXPECT_FALSE(m_options->HasOutputChangeList()); + EXPECT_EQ(m_options->GetTargetOutputCapture(), TestImpact::Policy::TargetOutputCapture::None); + EXPECT_EQ(m_options->GetTestFailurePolicy(), TestImpact::Policy::TestFailure::Abort); + EXPECT_EQ(m_options->GetIntegrityFailurePolicy(), TestImpact::Policy::IntegrityFailure::Abort); + EXPECT_EQ(m_options->GetTestPrioritizationPolicy(), TestImpact::Policy::TestPrioritization::None); + EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::None); + EXPECT_EQ(m_options->GetTestShardingPolicy(), TestImpact::Policy::TestSharding::Never); + EXPECT_FALSE(m_options->HasChangeListFile()); + EXPECT_FALSE(m_options->GetChangeListFile().has_value()); + EXPECT_FALSE(m_options->HasSafeMode()); + EXPECT_EQ(m_options->GetSuiteFilter(), TestImpact::SuiteType::Main); + } + + TEST_F(CommandLineOptionsTestFixture, ConfigurationFileHasEmptyPath_ExpectCommandLineOptionsException) + { + m_args.push_back("-config"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, ConfigurationFileHasSpecifiedPath_ExpectPath) + { + m_args.push_back("-config"); + m_args.push_back("Foo\\Bar"); + InitOptions(); + EXPECT_STREQ(m_options->GetConfigurationFile().c_str(), "Foo\\Bar"); + } + + TEST_F(CommandLineOptionsTestFixture, ConfigurationFileHasMultiplePaths_ExpectCommandLineOptionsException) + { + m_args.push_back("-config"); + m_args.push_back("value1,value2"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, UnifiedDiffFileHasEmptyPath_ExpectCommandLineOptionsException) + { + m_args.push_back("-changelist"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, UnifiedDiffFileHasSpecifiedPath_ExpectPath) + { + m_args.push_back("-changelist"); + m_args.push_back("Foo\\Bar"); + InitOptions(); + EXPECT_TRUE(m_options->HasChangeListFile()); + EXPECT_STREQ(m_options->GetChangeListFile()->c_str(), "Foo\\Bar"); + } + + TEST_F(CommandLineOptionsTestFixture, UnifiedDiffFileHasMultiplePaths_ExpectCommandLineOptionsException) + { + m_args.push_back("-changelist"); + m_args.push_back("value1,value2"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, OutputChangeListHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-ochangelist"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, OutputChangeListHasMultipleValues_ExpectCommandLineOptionsException) + { + m_args.push_back("-ochangelist"); + m_args.push_back("value1,value2,value3"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-sequence"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasNoneOption_ExpectNoneTestSequenceType) + { + m_args.push_back("-sequence"); + m_args.push_back("none"); + InitOptions(); + EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::None); + } + + TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasSeedOption_ExpectSeedTestSequenceType) + { + m_args.push_back("-sequence"); + m_args.push_back("seed"); + InitOptions(); + EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::Seed); + } + + TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasRegularOption_ExpectRegularTestSequenceType) + { + m_args.push_back("-sequence"); + m_args.push_back("regular"); + InitOptions(); + EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::Regular); + } + + TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasImpactAnalysisOption_ExpectImpactAnalysisTestSequenceType) + { + m_args.push_back("-sequence"); + m_args.push_back("tia"); + InitOptions(); + EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::ImpactAnalysis); + } + + TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasImpactAnalysisNoWriteOption_ExpectImpactAnalysisNoWriteTestSequenceType) + { + m_args.push_back("-sequence"); + m_args.push_back("tianowrite"); + InitOptions(); + EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::ImpactAnalysisNoWrite); + } + + TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasSafeImpactAnalysisOption_ExpectSafeImpactAnalysisTestSequenceType) + { + m_args.push_back("-sequence"); + m_args.push_back("tiaorseed"); + InitOptions(); + EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::ImpactAnalysisOrSeed); + } + + TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasInvalidOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-sequence"); + m_args.push_back("foo"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasMultipleValues_ExpectCommandLineOptionsException) + { + m_args.push_back("-sequence"); + m_args.push_back("seed,tia"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, TestPrioritizationPolicyHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-ppolicy"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, TestPrioritizationPolicyHasNoneOption_ExpectNoneTestPrioritizationPolicy) + { + m_args.push_back("-ppolicy"); + m_args.push_back("none"); + InitOptions(); + EXPECT_EQ(m_options->GetTestPrioritizationPolicy(), TestImpact::Policy::TestPrioritization::None); + } + + TEST_F(CommandLineOptionsTestFixture, TestPrioritizationPolicyHasDependencyLocalityOption_ExpectDependencyLocalityTestPrioritizationPolicy) + { + m_args.push_back("-ppolicy"); + m_args.push_back("locality"); + InitOptions(); + EXPECT_EQ(m_options->GetTestPrioritizationPolicy(), TestImpact::Policy::TestPrioritization::DependencyLocality); + } + + TEST_F(CommandLineOptionsTestFixture, TestPrioritizationPolicyInvalidOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-ppolicy"); + m_args.push_back("none,locality"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, ExecutionFailurePolicyHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-ppolicy"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, ExecutionFailurePolicyHasAbortOption_ExpectAbortExecutionFailurePolicy) + { + m_args.push_back("-epolicy"); + m_args.push_back("abort"); + InitOptions(); + EXPECT_EQ(m_options->GetExecutionFailurePolicy(), TestImpact::Policy::ExecutionFailure::Abort); + } + + TEST_F(CommandLineOptionsTestFixture, ExecutionFailurePolicyHasContinueOption_ExpectContinueExecutionFailurePolicy) + { + m_args.push_back("-epolicy"); + m_args.push_back("continue"); + InitOptions(); + EXPECT_EQ(m_options->GetExecutionFailurePolicy(), TestImpact::Policy::ExecutionFailure::Continue); + } + + TEST_F(CommandLineOptionsTestFixture, ExecutionFailurePolicyHasIgnoreOption_ExpectIgnoreExecutionFailurePolicy) + { + m_args.push_back("-epolicy"); + m_args.push_back("ignore"); + InitOptions(); + EXPECT_EQ(m_options->GetExecutionFailurePolicy(), TestImpact::Policy::ExecutionFailure::Ignore); + } + + TEST_F(CommandLineOptionsTestFixture, ExecutionFailurePolicyHasInvalidOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-epolicy"); + m_args.push_back("foo"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, ExecutionFailurePolicyHasMultipleValues_ExpectCommandLineOptionsException) + { + m_args.push_back("-epolicy"); + m_args.push_back("abort,ingore"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, ExecutionFailureDraftingPolicyHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-rexecfailures"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, FailedTestCoveragePolicyHasKeepOption_ExpectKeepFailedTestCoveragePolicy) + { + m_args.push_back("-cpolicy"); + m_args.push_back("keep"); + InitOptions(); + EXPECT_EQ(m_options->GetFailedTestCoveragePolicy(), TestImpact::Policy::FailedTestCoverage::Keep); + } + + TEST_F(CommandLineOptionsTestFixture, FailedTestCoveragePolicyHasDiscardOption_ExpectDiscardFailedTestCoveragePolicy) + { + m_args.push_back("-cpolicy"); + m_args.push_back("discard"); + InitOptions(); + EXPECT_EQ(m_options->GetFailedTestCoveragePolicy(), TestImpact::Policy::FailedTestCoverage::Discard); + } + + TEST_F(CommandLineOptionsTestFixture, FailedTestCoveragePolicyInvalidOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-cpolicy"); + m_args.push_back("foo"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, FailedTestCoveragePolicyPolicyHasMultipleValues_ExpectCommandLineOptionsException) + { + m_args.push_back("--cpolicy"); + m_args.push_back("keep,discard"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, TestFailurePolicyHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-fpolicy"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, TestFailurePolicyHasAbortOption_ExpectAbortTestFailurePolicy) + { + m_args.push_back("-fpolicy"); + m_args.push_back("abort"); + InitOptions(); + EXPECT_EQ(m_options->GetTestFailurePolicy(), TestImpact::Policy::TestFailure::Abort); + } + + TEST_F(CommandLineOptionsTestFixture, TestFailurePolicyHasContinueOption_ExpectContinueTestFailurePolicy) + { + m_args.push_back("-fpolicy"); + m_args.push_back("continue"); + InitOptions(); + EXPECT_EQ(m_options->GetTestFailurePolicy(), TestImpact::Policy::TestFailure::Continue); + } + + TEST_F(CommandLineOptionsTestFixture, TestFailurePolicyInvalidOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-fpolicy"); + m_args.push_back("foo"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, TestFailurePolicyHasMultipeValues_ExpectCommandLineOptionsException) + { + m_args.push_back("-fpolicy"); + m_args.push_back("abort,continue"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, IntegrityFailurePolicyHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-ipolicy"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, IntegrityFailurePolicyHasAbortOption_ExpectAbortIntegrityFailurePolicy) + { + m_args.push_back("-ipolicy"); + m_args.push_back("abort"); + InitOptions(); + EXPECT_EQ(m_options->GetIntegrityFailurePolicy(), TestImpact::Policy::IntegrityFailure::Abort); + } + + TEST_F(CommandLineOptionsTestFixture, IntegrityFailurePolicyHasContinueOption_ExpectContinueIntegrityFailurePolicy) + { + m_args.push_back("-ipolicy"); + m_args.push_back("continue"); + InitOptions(); + EXPECT_EQ(m_options->GetIntegrityFailurePolicy(), TestImpact::Policy::IntegrityFailure::Continue); + } + + TEST_F(CommandLineOptionsTestFixture, IntegrityFailurePolicyInvalidOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-ipolicy"); + m_args.push_back("foo"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, IntegrityFailurePolicyHasMultipeValues_ExpectCommandLineOptionsException) + { + m_args.push_back("-ipolicy"); + m_args.push_back("abort,continue"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, TestShardingHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-shard"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, TestShardingHasOnOption_ExpectOnTestSharding) + { + m_args.push_back("-shard"); + m_args.push_back("on"); + InitOptions(); + EXPECT_EQ(m_options->GetTestShardingPolicy(), TestImpact::Policy::TestSharding::Always); + } + + TEST_F(CommandLineOptionsTestFixture, TestShardingHasOffOption_ExpectOffTestSharding) + { + m_args.push_back("-shard"); + m_args.push_back("off"); + InitOptions(); + EXPECT_EQ(m_options->GetTestShardingPolicy(), TestImpact::Policy::TestSharding::Never); + } + + TEST_F(CommandLineOptionsTestFixture, TestShardingInvalidOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-shard"); + m_args.push_back("foo"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, TestShardingHasMultipeValues_ExpectCommandLineOptionsException) + { + m_args.push_back("-shard"); + m_args.push_back("on,off"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, TargetOutputCaptureHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-targetout"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, TargetOutputCaptureHasStdOutOption_ExpectStdOutTargetOutputCapture) + { + m_args.push_back("-targetout"); + m_args.push_back("stdout"); + InitOptions(); + EXPECT_EQ(m_options->GetTargetOutputCapture(), TestImpact::Policy::TargetOutputCapture::StdOut); + } + + TEST_F(CommandLineOptionsTestFixture, TargetOutputCaptureHasFileOption_ExpectFileTargetOutputCapture) + { + m_args.push_back("-targetout"); + m_args.push_back("file"); + InitOptions(); + EXPECT_EQ(m_options->GetTargetOutputCapture(), TestImpact::Policy::TargetOutputCapture::File); + } + + TEST_F(CommandLineOptionsTestFixture, TargetOutputCaptureHasStdOutAndFileOption_ExpectStdOutAndFileTargetOutputCapture) + { + m_args.push_back("-targetout"); + m_args.push_back("stdout,file"); + InitOptions(); + EXPECT_EQ(m_options->GetTargetOutputCapture(), TestImpact::Policy::TargetOutputCapture::StdOutAndFile); + } + + TEST_F(CommandLineOptionsTestFixture, TargetOutputCaptureInvalidOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-targetout"); + m_args.push_back("foo"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, TargetOutputCaptureHasExcessValues_ExpectCommandLineOptionsException) + { + m_args.push_back("-targetout"); + m_args.push_back("stdout,file,stdout"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, MaxConcurrencyHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-maxconcurrency"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, MaxConcurrencyHasInRangeOptions_ExpectInRangeMaxConcurrency) + { + m_args.push_back("-maxconcurrency"); + m_args.push_back("10"); + InitOptions(); + EXPECT_EQ(m_options->GetMaxConcurrency(), 10); + } + + TEST_F(CommandLineOptionsTestFixture, MaxConcurrencyHasOutOfRangeOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-maxconcurrency"); + m_args.push_back("-1"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, MaxConcurrencyInvalidOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-maxconcurrency"); + m_args.push_back("foo"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, MaxConcurrencyHasMultipeValues_ExpectCommandLineOptionsException) + { + m_args.push_back("-maxconcurrency"); + m_args.push_back("10,20"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, TestTargetTimeoutHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-ttimeout"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, TestTargetTimeoutHasInRangeOptions_ExpectInRangeTestTargetTimeout) + { + m_args.push_back("-ttimeout"); + m_args.push_back("10"); + InitOptions(); + EXPECT_EQ(m_options->GetTestTargetTimeout(), AZStd::chrono::seconds(10)); + } + + TEST_F(CommandLineOptionsTestFixture, TestTargetTimeoutHasOutOfRangeOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-ttimeout"); + m_args.push_back("-1"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, TestTargetTimeoutInvalidOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-ttimeout"); + m_args.push_back("foo"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, TestTargetTimeoutHasMultipeValues_ExpectCommandLineOptionsException) + { + m_args.push_back("-ttimeout"); + m_args.push_back("10,20"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, GlobalTimeoutHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-gtimeout"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, GlobalTimeoutHasInRangeOptions_ExpectInRangeGlobalTimeout) + { + m_args.push_back("-gtimeout"); + m_args.push_back("10"); + InitOptions(); + EXPECT_EQ(m_options->GetGlobalTimeout(), AZStd::chrono::seconds(10)); + } + + TEST_F(CommandLineOptionsTestFixture, GlobalTimeoutHasOutOfRangeOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-gtimeout"); + m_args.push_back("-1"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, GlobalTimeoutInvalidOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-gtimeout"); + m_args.push_back("foo"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, GlobalTimeoutHasMultipeValues_ExpectCommandLineOptionsException) + { + m_args.push_back("-gtimeout"); + m_args.push_back("10,20"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + // + + TEST_F(CommandLineOptionsTestFixture, SafeModeHasEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-safemode"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, SafeModeHasOnOption_ExpectOnSafeMode) + { + m_args.push_back("-safemode"); + m_args.push_back("on"); + InitOptions(); + EXPECT_TRUE(m_options->HasSafeMode()); + } + + TEST_F(CommandLineOptionsTestFixture, SafeModeHasOffOption_ExpectOffSafeMode) + { + m_args.push_back("-safemode"); + m_args.push_back("off"); + InitOptions(); + EXPECT_FALSE(m_options->HasSafeMode()); + } + + TEST_F(CommandLineOptionsTestFixture, SafeModeInvalidOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-safemode"); + m_args.push_back("foo"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, SafeModeHasMultipeValues_ExpectCommandLineOptionsException) + { + m_args.push_back("-safemode"); + m_args.push_back("on,off"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, SuiteFilterEmptyOption_ExpectCommandLineOptionsException) + { + m_args.push_back("-suite"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } + catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } + catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, SuiteFilterMultipleOptions_ExpectCommandLineOptionsException) + { + m_args.push_back("-suite"); + m_args.push_back("periodic,smoke"); + + try + { + InitOptions(); + + // Do not expect the command line options construction to succeed + FAIL(); + } catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) + { + // Expect a command line options to be thrown + SUCCEED(); + } catch (...) + { + // Do not expect any other exceptions + FAIL(); + } + } + + TEST_F(CommandLineOptionsTestFixture, SuitesFilterMainOption_ExpectMainSuiteFilter) + { + m_args.push_back("-suites"); + m_args.push_back("main"); + InitOptions(); + EXPECT_EQ(m_options->GetSuiteFilter(), TestImpact::SuiteType::Main); + } + + TEST_F(CommandLineOptionsTestFixture, SuitesFilterPeriodicOption_ExpectPeriodicSuiteFilter) + { + m_args.push_back("-suites"); + m_args.push_back("periodic"); + InitOptions(); + EXPECT_EQ(m_options->GetSuiteFilter(), TestImpact::SuiteType::Periodic); + } + + TEST_F(CommandLineOptionsTestFixture, SuitesFilterSandboxOption_ExpectSandboxSuiteFilter) + { + m_args.push_back("-suites"); + m_args.push_back("sandbox"); + InitOptions(); + EXPECT_EQ(m_options->GetSuiteFilter(), TestImpact::SuiteType::Sandbox); + } +} diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleMainTest.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleMainTest.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleTestSequenceEventHandlerTest.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleTestSequenceEventHandlerTest.cpp new file mode 100644 index 0000000000..52422daa31 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleTestSequenceEventHandlerTest.cpp @@ -0,0 +1,42 @@ +///* +// * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +// * its licensors. +// * +// * For complete copyright and license terms please see the LICENSE at the root of this +// * distribution (the "License"). All use of this software is governed by the License, +// * or, if provided, by the license below or the license accompanying this file. Do not +// * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * +// */ +// +//#include +//#include +// +//#include +// +//#include +//#include +//#include +//#include +// +//namespace UnitTest +//{ +// class ConsoleTestSequenceTestFixture +// : public AllocatorsTestFixture +// { +// }; +// +// TEST_F(ConsoleTestSequenceTestFixture, CheckEmptyArgs_ExpectDefaultValues) +// { +// AZStd::unordered_set suites = { "PERIODIC","MAIN" }; +// TestImpact::Console::TestSequenceEventHandler seq(&suites); +// AZStd::vector selectedTests = { "Test1", "Test2", "Test3", "Test4", "Test5" }; +// seq.operator()(TestImpact::Client::TestRunSelection(selectedTests, {"Test6"})); +// +// for (auto i = 0; i < selectedTests.size(); i++) +// { +// seq.operator()(TestImpact::Client::TestRun(selectedTests[i], (TestImpact::Client::TestRunResult)i, AZStd::chrono::milliseconds(i))); +// } +// } +//} diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactFrontendConsoleStaticTestMain.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactFrontendConsoleStaticTestMain.cpp new file mode 100644 index 0000000000..9e128f90af --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactFrontendConsoleStaticTestMain.cpp @@ -0,0 +1,34 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +class TestImpactTestEnvironment + : public AZ::Test::ITestEnvironment +{ +protected: + void SetupEnvironment() override + { + AZ::AllocatorInstance::Create(); + AZ::AllocatorInstance::Create(); + } + + void TeardownEnvironment() override + { + AZ::AllocatorInstance::Destroy(); + AZ::AllocatorInstance::Destroy(); + } +}; + +AZ_UNIT_TEST_HOOK(new TestImpactTestEnvironment); diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactRuntimeConfigurationFactoryTest.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactRuntimeConfigurationFactoryTest.cpp new file mode 100644 index 0000000000..4b1b3f162e --- /dev/null +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactRuntimeConfigurationFactoryTest.cpp @@ -0,0 +1,27 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include + +#include + +namespace UnitTest +{ + //TEST(FOO, BAR) + //{ + // std::ifstream configFile("C:\\o3de_TIF_Feature\\windows_vs2019\\bin\\debug\\tiaf.debug.json"); + // AZStd::string configData((std::istreambuf_iterator(configFile)), std::istreambuf_iterator()); + // const auto configuration = TestImpact::ConfigurationFactory(configData); + //} +} diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/testimpactframework_frontend_console_files.cmake b/Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_files.cmake similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/testimpactframework_frontend_console_files.cmake rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_files.cmake diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_files.cmake b/Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_static_files.cmake similarity index 100% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_files.cmake rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_static_files.cmake diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_tests_files.cmake b/Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_static_tests_files.cmake similarity index 67% rename from Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_tests_files.cmake rename to Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_static_tests_files.cmake index 5714be5dfb..4d8e65b2cc 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/Code/testimpactframework_frontend_console_static_tests_files.cmake +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_static_tests_files.cmake @@ -10,4 +10,9 @@ # set(FILES + Tests/TestImpactCommandLineOptionsTest.cpp + Tests/TestImpactRuntimeConfigurationFactoryTest.cpp + Tests/TestImpactFrontendConsoleStaticTestMain.cpp + Tests/TestImpactConsoleMainTest.cpp + Tests/TestImpactConsoleTestSequenceEventHandlerTest.cpp ) diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/CMakeLists.txt deleted file mode 100644 index 8298bb7123..0000000000 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/CMakeLists.txt deleted file mode 100644 index 88da472e1e..0000000000 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Executable/Code/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -ly_add_target( - NAME TestImpact.Frontend.Console EXECUTABLE - OUTPUT_NAME tiaf - NAMESPACE AZ - FILES_CMAKE - testimpactframework_frontend_console_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - Source - BUILD_DEPENDENCIES - PRIVATE - AZ::TestImpact.Frontend.Console.Static -) - diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Static/CMakeLists.txt b/Code/Tools/TestImpactFramework/Frontend/Console/Static/CMakeLists.txt deleted file mode 100644 index 8298bb7123..0000000000 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Static/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/CMakeLists.txt index 4c021a12b4..bcfeeacb33 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/CMakeLists.txt +++ b/Code/Tools/TestImpactFramework/Runtime/Code/CMakeLists.txt @@ -40,48 +40,49 @@ ly_add_target( # Tests ################################################################################ -ly_add_target( - NAME TestImpact.Runtime.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} - NAMESPACE AZ - FILES_CMAKE - testimpactframework_runtime_tests_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - Include - Source - Tests - BUILD_DEPENDENCIES - PRIVATE - AZ::AzTestShared - AZ::AzTest - AZ::TestImpact.Runtime.Static - RUNTIME_DEPENDENCIES - AZ::AzTestRunner - AZ::TestImpact.TestProcess.Console - AZ::TestImpact.TestTargetA.Tests - AZ::TestImpact.TestTargetB.Tests - AZ::TestImpact.TestTargetC.Tests - AZ::TestImpact.TestTargetD.Tests - COMPILE_DEFINITIONS - PRIVATE - LY_TEST_IMPACT_AZ_TESTRUNNER_BIN="$" - LY_TEST_IMPACT_TEST_PROCESS_BIN="$" - LY_TEST_IMPACT_TEST_TARGET_A_BIN="$" - LY_TEST_IMPACT_TEST_TARGET_B_BIN="$" - LY_TEST_IMPACT_TEST_TARGET_C_BIN="$" - LY_TEST_IMPACT_TEST_TARGET_D_BIN="$" - LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME="$" - LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME="$" - LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME="$" - LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME="$" - LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR="${GTEST_XML_OUTPUT_DIR}/TestImpact/Temp/Exclusive/Enum" - LY_TEST_IMPACT_TEST_TARGET_RESULTS_DIR="${GTEST_XML_OUTPUT_DIR}/TestImpact/Temp/Exclusive/Result" - LY_TEST_IMPACT_TEST_TARGET_COVERAGE_DIR="${GTEST_XML_OUTPUT_DIR}/TestImpact/Temp/Exclusive/Coverage" - LY_TEST_IMPACT_INSTRUMENTATION_BIN="${LY_TEST_IMPACT_INSTRUMENTATION_BIN}" - LY_TEST_IMPACT_MODULES_DIR="${CMAKE_BINARY_DIR}" - LY_TEST_IMPACT_COVERAGE_SOURCES_DIR="${CMAKE_CURRENT_SOURCE_DIR}" -) - -ly_add_googletest( - NAME AZ::TestImpact.Runtime.Tests -) +# Disabled: SPEC-7246 +#ly_add_target( +# NAME TestImpact.Runtime.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} +# NAMESPACE AZ +# FILES_CMAKE +# testimpactframework_runtime_tests_files.cmake +# INCLUDE_DIRECTORIES +# PRIVATE +# Include +# Source +# Tests +# BUILD_DEPENDENCIES +# PRIVATE +# AZ::AzTestShared +# AZ::AzTest +# AZ::TestImpact.Runtime.Static +# RUNTIME_DEPENDENCIES +# AZ::AzTestRunner +# AZ::TestImpact.TestProcess.Console +# AZ::TestImpact.TestTargetA.Tests +# AZ::TestImpact.TestTargetB.Tests +# AZ::TestImpact.TestTargetC.Tests +# AZ::TestImpact.TestTargetD.Tests +# COMPILE_DEFINITIONS +# PRIVATE +# LY_TEST_IMPACT_AZ_TESTRUNNER_BIN="$" +# LY_TEST_IMPACT_TEST_PROCESS_BIN="$" +# LY_TEST_IMPACT_TEST_TARGET_A_BIN="$" +# LY_TEST_IMPACT_TEST_TARGET_B_BIN="$" +# LY_TEST_IMPACT_TEST_TARGET_C_BIN="$" +# LY_TEST_IMPACT_TEST_TARGET_D_BIN="$" +# LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME="$" +# LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME="$" +# LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME="$" +# LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME="$" +# LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR="${GTEST_XML_OUTPUT_DIR}/TestImpact/Temp/Exclusive/Enum" +# LY_TEST_IMPACT_TEST_TARGET_RESULTS_DIR="${GTEST_XML_OUTPUT_DIR}/TestImpact/Temp/Exclusive/Result" +# LY_TEST_IMPACT_TEST_TARGET_COVERAGE_DIR="${GTEST_XML_OUTPUT_DIR}/TestImpact/Temp/Exclusive/Coverage" +# LY_TEST_IMPACT_INSTRUMENTATION_BIN="${LY_TEST_IMPACT_INSTRUMENTATION_BIN}" +# LY_TEST_IMPACT_MODULES_DIR="${CMAKE_BINARY_DIR}" +# LY_TEST_IMPACT_COVERAGE_SOURCES_DIR="${CMAKE_CURRENT_SOURCE_DIR}" +#) +# +#ly_add_googletest( +# NAME AZ::TestImpact.Runtime.Tests +#) diff --git a/cmake/LYTestWrappers.cmake b/cmake/LYTestWrappers.cmake index eebc281d06..1bacc4feb0 100644 --- a/cmake/LYTestWrappers.cmake +++ b/cmake/LYTestWrappers.cmake @@ -315,6 +315,7 @@ function(ly_add_pytest) ${ly_add_pytest_UNPARSED_ARGUMENTS} ) + set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${ly_add_pytest_NAME}_SCRIPT_PATH ${ly_add_pytest_PATH}) set_tests_properties(${LY_ADDED_TEST_NAME} PROPERTIES RUN_SERIAL "${ly_add_pytest_TEST_SERIAL}") endfunction() diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 9201e13a80..81100eead7 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -88,7 +88,7 @@ "COMMAND": "python_windows.cmd", "PARAMETERS": { "SCRIPT_PATH": "scripts/build/TestImpactAnalysis/tiaf_driver.py", - "SCRIPT_PARAMETERS": "--testFailurePolicy abort --safeMode --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.profile.json\"" + "SCRIPT_PARAMETERS": "--testFailurePolicy abort --safeMode --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"!OUTPUT_DIRECTORY!\\TestImpactFramework\\persistent\\tiaf.profile.json\"" } }, "debug_vs2019": { diff --git a/scripts/build/TestImpactAnalysis/tiaf.py b/scripts/build/TestImpactAnalysis/tiaf.py index c040ae1147..19c1b2754d 100644 --- a/scripts/build/TestImpactAnalysis/tiaf.py +++ b/scripts/build/TestImpactAnalysis/tiaf.py @@ -219,49 +219,4 @@ class TestImpact: else: print(f"The test impact analysis runtime returned with error: '{result.returncode}'.") return result.returncode - - - - #args = [] - #print("Please note: test impact analysis sequences will be run in read-only mode (seed sequences are unaffected).") - #if sequence_type == SequenceType.REGULAR: - # print("Sequence type: regular.") - # args.append("--sequence=regular") - # args.append("--fpolicy=abort") - #elif sequence_type == SequenceType.SEED: - # print("Sequence type: seed.") - # args.append("--sequence=seed") - # args.append("--fpolicy=continue") - #elif sequence_type == SequenceType.TEST_IMPACT_ANALYSIS: - # print("Sequence type: test impact analysis (no write).") - # args.append("--fpolicy=abort") - # if self.__has_change_list: - # args.append(f"-changelist={self.__change_list_path}") - # args.append("--sequence=tianowrite") - # else: - # print(f"No change list was generated, falling back to a regular sequence.") - # print("Sequence type: Regular.") - # args.append("--sequence=regular") - #else: - # raise ValueError(sequence_type) - # - #if test_timeout != None: - # args.append(f"--ttimeout={test_timeout}") - # print(f"Test target timeout is set to {test_timeout} seconds.") - #if global_timeout != None: - # args.append(f"--gtimeout={global_timeout}") - # print(f"Global sequence timeout is set to {test_timeout} seconds.") - # - #print("Args: ", end='') - #print(*args) - #result = subprocess.run([self.__tiaf_bin] + args) - #if result.returncode == 0: - # print("Test impact analysis runtime returned successfully.") - # if sequence_type == SequenceType.SEED: - # print("Writing historical meta-data...") - # self.__write_last_run_hash(self.__dst_commit) - # print("Complete!") - #else: - # print(f"The test impact analysis runtime returned with error: '{result.returncode}'.") - #return result.returncode - + \ No newline at end of file diff --git a/scripts/build/TestImpactAnalysis/tiaf_driver.py b/scripts/build/TestImpactAnalysis/tiaf_driver.py index 7ff73e505b..f452b9738f 100644 --- a/scripts/build/TestImpactAnalysis/tiaf_driver.py +++ b/scripts/build/TestImpactAnalysis/tiaf_driver.py @@ -57,4 +57,5 @@ if __name__ == "__main__": args = parse_args() tiaf = TestImpact(args.config, args.pipeline, args.dst_commit) return_code = tiaf.run(args.suite, args.test_failure_policy, args.safe_mode, args.test_timeout, args.global_timeout) - sys.exit(return_code) \ No newline at end of file + sys.exit(return_code) + \ No newline at end of file From af0bcb06365969d17f71b705862448b473a6556f Mon Sep 17 00:00:00 2001 From: John Date: Thu, 10 Jun 2021 14:58:34 +0100 Subject: [PATCH 128/233] Remove test files --- ...ImpactBuildTargetDescriptorFactoryTest.cpp | 370 ---- .../TestImpactModuleCoverageFactoryTest.cpp | 522 ----- ...TestImpactTargetDescriptorCompilerTest.cpp | 150 -- ...tImpactTestEnumerationSuiteFactoryTest.cpp | 589 ------ .../TestImpactTestRunSuiteFactoryTest.cpp | 592 ------ ...TestImpactTestTargetMetaMapFactoryTest.cpp | 239 --- .../TestImpactProcessSchedulerTest.cpp | 712 ------- .../Tests/Process/TestImpactProcessTest.cpp | 491 ----- .../Target/TestImpactBuildTargetTest.cpp | 352 ---- .../Code/Tests/TestImpactExceptionTest.cpp | 150 -- .../Tests/TestImpactTestJobRunnerCommon.h | 159 -- .../Code/Tests/TestImpactTestUtils.cpp | 1843 ----------------- .../Runtime/Code/Tests/TestImpactTestUtils.h | 220 -- .../Code/Tests/TestProcess/CMakeLists.txt | 12 - .../Tests/TestProcess/Code/CMakeLists.txt | 24 - .../Code/Source/TestImpactTestProcess.cpp | 93 - .../Code/Source/TestImpactTestProcess.h | 38 - .../Source/TestImpactTestProcessLargeText.cpp | 531 ----- .../Source/TestImpactTestProcessLargeText.h | 19 - .../Code/Source/TestImpactTestProcessMain.cpp | 19 - ...estimpactframework_testprocess_files.cmake | 18 - .../Code/Tests/TestTargetA/CMakeLists.txt | 12 - .../Tests/TestTargetA/Code/CMakeLists.txt | 28 - .../Code/Tests/TestImpactTestTargetA.cpp | 73 - ...actframework_testtargeta_tests_files.cmake | 14 - .../Code/Tests/TestTargetB/CMakeLists.txt | 12 - .../Tests/TestTargetB/Code/CMakeLists.txt | 29 - .../Code/Tests/TestImpactTestTargetB.cpp | 78 - ...actframework_testtargetb_tests_files.cmake | 14 - .../Code/Tests/TestTargetC/CMakeLists.txt | 12 - .../Tests/TestTargetC/Code/CMakeLists.txt | 29 - .../Code/Tests/TestImpactTestTargetC.cpp | 63 - ...actframework_testtargetc_tests_files.cmake | 14 - .../Code/Tests/TestTargetD/CMakeLists.txt | 12 - .../Tests/TestTargetD/Code/CMakeLists.txt | 29 - .../Code/Tests/TestImpactTestTargetD.cpp | 188 -- ...actframework_testtargetd_tests_files.cmake | 14 - 37 files changed, 7764 deletions(-) delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactBuildTargetDescriptorFactoryTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactModuleCoverageFactoryTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTargetDescriptorCompilerTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestEnumerationSuiteFactoryTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestRunSuiteFactoryTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestTargetMetaMapFactoryTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessSchedulerTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactExceptionTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestJobRunnerCommon.h delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.h delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.h delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.h delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessMain.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/testimpactframework_testprocess_files.cmake delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/Tests/TestImpactTestTargetA.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/testimpactframework_testtargeta_tests_files.cmake delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/Tests/TestImpactTestTargetB.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/testimpactframework_testtargetb_tests_files.cmake delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/Tests/TestImpactTestTargetC.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/testimpactframework_testtargetc_tests_files.cmake delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/CMakeLists.txt delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/Tests/TestImpactTestTargetD.cpp delete mode 100644 Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/testimpactframework_testtargetd_tests_files.cmake diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactBuildTargetDescriptorFactoryTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactBuildTargetDescriptorFactoryTest.cpp deleted file mode 100644 index a0a4a7309e..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactBuildTargetDescriptorFactoryTest.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include - -#include -#include - -namespace UnitTest -{ - class BuildTargetDescriptorFactoryTestFixture - : public AllocatorsTestFixture - { - protected: - const AZStd::vector m_staticExclude = {".cmake"}; - const AZStd::vector m_inputExclude = {".jinja"}; - const AZStd::string m_autogenMatcher = {"(.*)\\..*"}; - - const AZStd::vector m_autogenInputs = - { - "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/Log.ScriptCanvasNode.xml", - "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/DrawText.ScriptCanvasNode.xml", - "Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNode_Header.jinja", - "Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNode_Source.jinja" - }; - - const AZStd::vector m_autogenOutputs = - { - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.h", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.h", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.cpp", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.cpp" - }; - - const AZStd::vector m_staticSources = - { - "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.cpp", - "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.h", - "Gems/ScriptCanvasDiagnosticLibrary/Code/scriptcanvasdiagnosticlibrary_autogen_files.cmake", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.h", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.h", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.cpp", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.cpp" - }; - - const AZStd::vector m_expectedStaticSources = - { - "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.cpp", - "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.h", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.h", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.h", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.cpp", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.cpp" - }; - - const AZStd::string m_name = "ScriptCanvasDiagnosticLibrary.Static"; - const AZStd::string m_outputName = "ScriptCanvasDiagnosticLibrary"; - const AZStd::string m_path = "Gems/ScriptCanvasDiagnosticLibrary/Code"; - - const TestImpact::AutogenSources m_expectedAutogenSources = - { - { - "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/Log.ScriptCanvasNode.xml", - { - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.h", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.cpp" - } - }, - { - "Gems/ScriptCanvasDiagnosticLibrary/Code/Source/DrawText.ScriptCanvasNode.xml", - { - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.h", - "windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.cpp" - } - } - }; - }; - - TEST_F(BuildTargetDescriptorFactoryTestFixture, NoRawData_ExpectArtifactException) - { - // Given an empty raw descriptor string - const AZStd::string rawTargetDescriptor; - - try - { - // When attempting to construct the build target descriptor - const TestImpact::BuildTargetDescriptor buildTarget = - TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(BuildTargetDescriptorFactoryTestFixture, InvalidRawData_ExpectArtifactException) - { - // Given a raw descriptor string of invalid data - const AZStd::string rawTargetDescriptor = "abcde"; - - try - { - // When attempting to construct the build target descriptor - const TestImpact::BuildTargetDescriptor buildTarget = - TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(BuildTargetDescriptorFactoryTestFixture, NoAutogenMatcher_ExpectArtifactException) - { - // Given a valid raw descriptor string - const AZStd::string rawTargetDescriptor = - GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, m_autogenInputs, m_autogenOutputs); - - try - { - // When attempting to construct the build target descriptor with an empty autogen matcher - const TestImpact::BuildTargetDescriptor buildTarget = - TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, ""); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(BuildTargetDescriptorFactoryTestFixture, EmptyName_ExpectArtifactException) - { - // Given a invalid raw descriptor string lacking build meta-data name - const AZStd::string rawTargetDescriptor = - GenerateBuildTargetDescriptorString("", m_outputName, m_path, m_staticSources, m_autogenInputs, m_autogenOutputs); - - try - { - // When attempting to construct the build target descriptor - const TestImpact::BuildTargetDescriptor buildTarget = - TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(BuildTargetDescriptorFactoryTestFixture, EmptyOutputName_ExpectArtifactException) - { - // Given a invalid raw descriptor string lacking build meta-data output name - const AZStd::string rawTargetDescriptor = - GenerateBuildTargetDescriptorString(m_name, "", m_path, m_staticSources, m_autogenInputs, m_autogenOutputs); - - try - { - // When attempting to construct the build target descriptor - const TestImpact::BuildTargetDescriptor buildTarget = - TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(BuildTargetDescriptorFactoryTestFixture, EmptyPath_ExpectArtifactException) - { - // Given a invalid raw descriptor string lacking build meta-data path - const AZStd::string rawTargetDescriptor = - GenerateBuildTargetDescriptorString(m_name, m_outputName, "", m_staticSources, m_autogenInputs, m_autogenOutputs); - - try - { - // When attempting to construct the build target descriptor - const TestImpact::BuildTargetDescriptor buildTarget = - TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(BuildTargetDescriptorFactoryTestFixture, NoStaticSources_ExpectValidDescriptor) - { - const TestImpact::BuildTargetDescriptor expectedBuiltTarget = - GenerateBuildTargetDescriptor(m_name, m_outputName, m_path, {}, m_expectedAutogenSources); - - // Given a valid raw descriptor string with no static sources - const AZStd::string rawTargetDescriptor = - GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, {}, m_autogenInputs, m_autogenOutputs); - - // When attempting to construct the build target descriptor - const TestImpact::BuildTargetDescriptor buildTarget = - TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, {}, m_inputExclude, m_autogenMatcher); - - // Expect the constructed build target descriptor to match the specified descriptor - EXPECT_TRUE(buildTarget == expectedBuiltTarget); - } - - TEST_F(BuildTargetDescriptorFactoryTestFixture, NoAutogenSources_ExpectValidDescriptor) - { - const TestImpact::BuildTargetDescriptor expectedBuiltTarget = - GenerateBuildTargetDescriptor(m_name, m_outputName, m_path, m_expectedStaticSources, {}); - - // Given a valid raw descriptor string with no autogen sources - const AZStd::string rawTargetDescriptor = - GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, {}, {}); - - // When attempting to construct the build target descriptor - const TestImpact::BuildTargetDescriptor buildTarget = - TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); - - // Expect the constructed build target descriptor to match the specified descriptor - EXPECT_TRUE(buildTarget == expectedBuiltTarget); - } - - TEST_F(BuildTargetDescriptorFactoryTestFixture, NoStaticOrAutogenSources_ExpectValidDescriptor) - { - const TestImpact::BuildTargetDescriptor expectedBuiltTarget = GenerateBuildTargetDescriptor(m_name, m_outputName, m_path, {}, {}); - - // Given a valid raw descriptor string with no static or autogen sources - const AZStd::string rawTargetDescriptor = GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, {}, {}, {}); - - // When attempting to construct the build target descriptor - const TestImpact::BuildTargetDescriptor buildTarget = - TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); - - // Expect the constructed build target descriptor to match the specified descriptor - EXPECT_TRUE(buildTarget == expectedBuiltTarget); - } - - TEST_F(BuildTargetDescriptorFactoryTestFixture, AutogenOutputSourcesButNoAutogenInputSources_ExpectArtifactException) - { - // Given a valid raw descriptor string with autogen output sources but no autogen input sources - const AZStd::string rawTargetDescriptor = - GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, {}, m_autogenOutputs); - - try - { - // When attempting to construct the build target descriptor - const TestImpact::BuildTargetDescriptor buildTarget = - TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(BuildTargetDescriptorFactoryTestFixture, AutogenInputSourcesButNoAutogenOutputSources_ExpectArtifactException) - { - // Given a valid raw descriptor string with autogen inout sources but no autogen output sources - const AZStd::string rawTargetDescriptor = - GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, m_autogenInputs, {}); - - try - { - // When attempting to construct the build target descriptor - const TestImpact::BuildTargetDescriptor buildTarget = - TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(BuildTargetDescriptorFactoryTestFixture, StaticAndAutogenSources_ExpectValidDescriptor) - { - const TestImpact::BuildTargetDescriptor expectedBuiltTarget = - GenerateBuildTargetDescriptor(m_name, m_outputName, m_path, m_expectedStaticSources, m_expectedAutogenSources); - - // Given a valid raw descriptor string with static and autogen sources - const AZStd::string rawTargetDescriptor = - GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, m_autogenInputs, m_autogenOutputs); - - // When attempting to construct the build target descriptor - const TestImpact::BuildTargetDescriptor buildTarget = - TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher); - - // Expect the constructed build target descriptor to match the specified descriptor - EXPECT_TRUE(buildTarget == expectedBuiltTarget); - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactModuleCoverageFactoryTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactModuleCoverageFactoryTest.cpp deleted file mode 100644 index 137addc360..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactModuleCoverageFactoryTest.cpp +++ /dev/null @@ -1,522 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include - -#include -#include - -namespace UnitTest -{ - TEST(CoberturaModuleCoveragesFactory, ParseEmptyString_ThrowsArtifactException) - { - // Given an empty string - const AZStd::string rawCoverage; - - try - { - // When attempting to parse the empty coverage - const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); - - // Do not expect the parsing to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(CoberturaModuleCoveragesFactory, ParseInvalidString_ThrowsArtifactException) - { - // Given an invalid string - const AZStd::string rawCoverage = "!@?"; - - try - { - // When attempting to parse the invalid coverage - const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); - - // Do not expect the parsing to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(CoberturaModuleCoveragesFactory, ParseEmptyCoverage_ExpectEmptyModuleCoverages) - { - // Given an empty coverage string - const AZStd::string rawCoverage = - "" - "" - " " - " " - "" - ""; - - // When attempting to parse the empty coverage - const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); - - // Expect an empty module coverages - EXPECT_TRUE(coverage.empty()); - } - - TEST(CoberturaModuleCoveragesFactory, ParseTestTargetLineCoverageA_ReturnsValidLineCoverage) - { - const AZStd::vector expectedCoverage = GetTestTargetALineModuleCoverages(); - - // Given the raw line coverage output of TestTargetA - const AZStd::string rawCoverage = - "" - "" - " " - " C:" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - // When the raw line coverage text is parsed - const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); - - // Expect the generated suite data to match that of the raw coverage text - EXPECT_TRUE(coverage == expectedCoverage); - } - - TEST(CoberturaModuleCoveragesFactory, ParseTestTargetSourceCoverageA_ReturnsValidSourceCoverage) - { - const AZStd::vector expectedCoverage = GetTestTargetASourceModuleCoverages(); - - // Given the raw source coverage output of TestTargetA - const AZStd::string rawCoverage = - "" - "" - " " - " C:" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - // When the raw source coverage text is parsed - const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); - - // Expect the generated suite data to match that of the raw coverage text - EXPECT_TRUE(coverage == expectedCoverage); - } - - TEST(CoberturaModuleCoveragesFactory, ParseTestTargetLineCoverageB_ReturnsValidLineCoverage) - { - const AZStd::vector expectedCoverage = GetTestTargetBLineModuleCoverages(); - - // Given the raw line coverage output of TestTargetB - const AZStd::string rawCoverage = - "" - "" - " " - " C:" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - // When the raw line coverage text is parsed - const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); - - // Expect the generated suite data to match that of the raw coverage text - EXPECT_TRUE(coverage == expectedCoverage); - } - - TEST(CoberturaModuleCoveragesFactory, ParseTestTargetSourceCoverageB_ReturnsValidSourceCoverage) - { - const AZStd::vector expectedCoverage = GetTestTargetBSourceModuleCoverages(); - - // Given the raw source coverage output of TestTargetB - const AZStd::string rawCoverage = - "" - "" - " " - " C:" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - // When the raw source coverage text is parsed - const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); - - // Expect the generated suite data to match that of the raw coverage text - EXPECT_TRUE(coverage == expectedCoverage); - } - - TEST(CoberturaModuleCoveragesFactory, ParseTestTargetLineCoverageC_ReturnsValidLineCoverage) - { - const AZStd::vector expectedCoverage = GetTestTargetCLineModuleCoverages(); - - // Given the raw line coverage output of TestTargetC - const AZStd::string rawCoverage = - "" - "" - " " - " C:" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - // When the raw line coverage text is parsed - const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); - - // Expect the generated suite data to match that of the raw coverage text - EXPECT_TRUE(coverage == expectedCoverage); - } - - TEST(CoberturaModuleCoveragesFactory, ParseTestTargetSourceCoverageC_ReturnsValidSourceCoverage) - { - const AZStd::vector expectedCoverage = GetTestTargetCSourceModuleCoverages(); - - // Given the raw source coverage output of TestTargetC - const AZStd::string rawCoverage = - "" - "" - " " - " C:" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - // When the raw source coverage text is parsed - const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); - - // Expect the generated suite data to match that of the raw coverage text - EXPECT_TRUE(coverage == expectedCoverage); - } - - TEST(CoberturaModuleCoveragesFactory, ParseTestTargetLineCoverageD_ReturnsValidLineCoverage) - { - const AZStd::vector expectedCoverage = GetTestTargetDLineModuleCoverages(); - - // Given the raw line coverage output of TestTargetD - const AZStd::string rawCoverage = - "" - "" - " " - " C:" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - // When the raw line coverage text is parsed - const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); - - // Expect the generated suite data to match that of the raw coverage text - EXPECT_TRUE(coverage == expectedCoverage); - } - - TEST(CoberturaModuleCoveragesFactory, ParseTestTargetSourceCoverageD_ReturnsValidSourceCoverage) - { - const AZStd::vector expectedCoverage = GetTestTargetDSourceModuleCoverages(); - - // Given the raw source coverage output of TestTargetD - const AZStd::string rawCoverage = - "" - "" - " " - " C:" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - // When the raw source coverage text is parsed - const AZStd::vector coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage); - - // Expect the generated suite data to match that of the raw coverage text - EXPECT_TRUE(coverage == expectedCoverage); - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTargetDescriptorCompilerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTargetDescriptorCompilerTest.cpp deleted file mode 100644 index 1e72902b4e..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTargetDescriptorCompilerTest.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include - -#include -#include - -namespace UnitTest -{ - class TargetDescriptorCompilerTestFixture - : public AllocatorsTestFixture - { - public: - void SetUp() override - { - AllocatorsTestFixture::SetUp(); - - m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"TestTargetA", "", ""}, {}}); - m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"TestTargetB", "", ""}, {}}); - m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"ProductionTargetA", "", ""}, {}}); - m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"ProductionTargetB", "", ""}, {}}); - m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"ProductionTargetC", "", ""}, {}}); - - m_testTargetMetaMap.emplace("TestTargetA", TestImpact::TestTargetMeta{"", TestImpact::LaunchMethod::TestRunner}); - m_testTargetMetaMap.emplace("TestTargetB", TestImpact::TestTargetMeta{"", TestImpact::LaunchMethod::StandAlone}); - } - - protected: - AZStd::vector m_buildTargetDescriptors; - TestImpact::TestTargetMetaMap m_testTargetMetaMap; - }; - - TestImpact::ProductionTargetDescriptor ConstructProductionTargetDescriptor(const AZStd::string& name) - { - return TestImpact::ProductionTargetDescriptor{TestImpact::BuildTargetDescriptor{{name, "", ""}, {{}, {}}}}; - } - - TestImpact::TestTargetDescriptor ConstructTestTargetDescriptor(const AZStd::string& name, TestImpact::LaunchMethod launchMethod) - { - return TestImpact::TestTargetDescriptor{ - TestImpact::BuildTargetDescriptor{{name, "", ""}, {{}, {}}}, TestImpact::TestTargetMeta{"", launchMethod}}; - } - - TEST_F(TargetDescriptorCompilerTestFixture, EmptyBuildTargetDescriptorList_ExpectArtifactException) - { - try - { - // Given an empty build target descriptor list but valid test target meta map - // When attempting to construct the test target - const auto& [productionTargetDescriptors, testTargetDescriptors] = - TestImpact::CompileTargetDescriptors({}, AZStd::move(m_testTargetMetaMap)); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(TargetDescriptorCompilerTestFixture, EmptyTestTargetMetaMap_ExpectArtifactException) - { - try - { - // Given a valid build target descriptor list but empty test target meta map - // When attempting to construct the test target - const auto& [productionTargetDescriptors, testTargetDescriptors] = - TestImpact::CompileTargetDescriptors(AZStd::move(m_buildTargetDescriptors), {}); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(TargetDescriptorCompilerTestFixture, TestTargetWithNoMatchingMeta_ExpectArtifactException) - { - try - { - // Given a valid build target descriptor list but a test target meta map with an orphan entry - m_testTargetMetaMap.emplace("Orphan", TestImpact::TestTargetMeta{"", TestImpact::LaunchMethod::TestRunner}); - - // When attempting to construct the test target - const auto& [productionTargetDescriptors, testTargetDescriptors] = - TestImpact::CompileTargetDescriptors(AZStd::move(m_buildTargetDescriptors), {}); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(TargetDescriptorCompilerTestFixture, ValidProductionTargetsAndTestTargetMetas_ExpectValidProductionAndTestTargets) - { - // Given a valid build target descriptor list and a valid test target meta map - // When attempting to construct the test target - const auto& [productionTargetDescriptors, testTargetDescriptors] = - TestImpact::CompileTargetDescriptors(AZStd::move(m_buildTargetDescriptors), AZStd::move(m_testTargetMetaMap)); - - // Expect the production targets to match the expected targets - EXPECT_TRUE(productionTargetDescriptors.size() == 3); - EXPECT_TRUE(productionTargetDescriptors[0] == ConstructProductionTargetDescriptor("ProductionTargetA")); - EXPECT_TRUE(productionTargetDescriptors[1] == ConstructProductionTargetDescriptor("ProductionTargetB")); - EXPECT_TRUE(productionTargetDescriptors[2] == ConstructProductionTargetDescriptor("ProductionTargetC")); - - // Expect the test targets to match the expected targets - EXPECT_TRUE(testTargetDescriptors.size() == 2); - EXPECT_TRUE(testTargetDescriptors[0] == ConstructTestTargetDescriptor("TestTargetA", TestImpact::LaunchMethod::TestRunner)); - EXPECT_TRUE(testTargetDescriptors[1] == ConstructTestTargetDescriptor("TestTargetB", TestImpact::LaunchMethod::StandAlone)); - } - -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestEnumerationSuiteFactoryTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestEnumerationSuiteFactoryTest.cpp deleted file mode 100644 index d6e1dc5920..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestEnumerationSuiteFactoryTest.cpp +++ /dev/null @@ -1,589 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace UnitTest -{ - TEST(GTestEnumerationSuiteFactory, ParseEmptyString_ThrowsArtifactException) - { - // Given an empty string - const AZStd::string rawEnum; - - try - { - // When attempting to parse the empty suite - const AZStd::vector suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum); - - // Do not expect the parsing to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(GTestEnumerationSuiteFactory, ParseInvalidString_ThrowsArtifactException) - { - // Given an invalid string - const AZStd::string rawEnum = "!@?"; - - try - { - // When attempting to parse the empty suite - const AZStd::vector suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum); - - // Do not expect the parsing to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(GTestEnumerationSuiteFactory, ParseTestTargetA_ReturnsValidSuitesAndTests) - { - const AZStd::vector expectedSuites = GetTestTargetATestEnumerationSuites(); - - // Given the raw enum output of TestTargetA - const AZStd::string rawEnum = - "\n" - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - "\n" - "\n"; - - // When the raw enumeration text is parsed - const AZStd::vector suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum); - - // Expect the generated suite data to match that of the raw enum text - EXPECT_TRUE(suites == expectedSuites); - } - - TEST(GTestEnumerationSuiteFactory, ParseTestTargetB_ReturnsValidSuitesAndTests) - { - const AZStd::vector expectedSuites = GetTestTargetBTestEnumerationSuites(); - - // Given the raw enum output of TestTargetB - const AZStd::string rawEnum = - "\n" - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - "\n" - "\n"; - - // When the raw enumeration text is parsed - const AZStd::vector suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum); - - // Expect the generated suite data to match that of the raw enum text - EXPECT_TRUE(suites == expectedSuites); - } - - TEST(GTestEnumerationSuiteFactory, ParseTestTargetC_ReturnsValidSuitesAndTests) - { - const AZStd::vector expectedSuites = GetTestTargetCTestEnumerationSuites(); - - // Given the raw enum output of TestTargetC - const AZStd::string rawEnum = - "\n" - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - "\n" - "\n"; - - // When the raw enumeration text is parsed - const AZStd::vector suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum); - - // Expect the generated suite data to match that of the raw enum text - EXPECT_TRUE(suites == expectedSuites); - } - - TEST(GTestEnumerationSuiteFactory, ParseTestTargetD_ReturnsValidSuitesAndTests) - { - const AZStd::vector expectedSuites = GetTestTargetDTestEnumerationSuites(); - - // Given the raw enum output of TestTargetD - const AZStd::string rawEnum = - "\n" - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - "\n" - "\n"; - - // When the raw enumeration text is parsed - const AZStd::vector suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum); - - // Expect the generated suite data to match that of the raw enum text - EXPECT_TRUE(suites == expectedSuites); - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestRunSuiteFactoryTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestRunSuiteFactoryTest.cpp deleted file mode 100644 index 0f0a7dfdd8..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestRunSuiteFactoryTest.cpp +++ /dev/null @@ -1,592 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace UnitTest -{ - TEST(GTestRunSuiteFactory, ParseEmptyString_ThrowsArtifactException) - { - // Given an empty string - const AZStd::string rawRun; - - try - { - // When attempting to parse the empty suite - const AZStd::vector suites = TestImpact::GTest::TestRunSuitesFactory(rawRun); - - // Do not expect the parsing to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect a artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(GTestRunSuiteFactory, ParseInvalidString_ThrowsArtifactException) - { - // Given an invalid string - const AZStd::string rawRun = "!@?"; - - try - { - // When attempting to parse the invalid suite - const AZStd::vector suites = TestImpact::GTest::TestRunSuitesFactory(rawRun); - - // Do not expect the parsing to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect a artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(GTestRunSuiteFactory, ParseTestTargetA_ReturnsValidSuitesAndTests) - { - const AZStd::vector expectedSuites = GetTestTargetATestRunSuites(); - - // Given the raw run output of TestTargetA - const AZStd::string rawRun = - "" - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - // When the raw run text is parsed - const AZStd::vector suites = TestImpact::GTest::TestRunSuitesFactory(rawRun); - - // Expect the generated suite data to match that of the raw run text - EXPECT_TRUE(suites == expectedSuites); - } - - TEST(GTestRunSuiteFactory, ParseTestTargetB_ReturnsValidSuitesAndTests) - { - const AZStd::vector expectedSuites = GetTestTargetBTestRunSuites(); - - // Given the raw run output of TestTargetB - const AZStd::string rawRun = - "" - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - // When the raw run text is parsed - const AZStd::vector suites = TestImpact::GTest::TestRunSuitesFactory(rawRun); - - // Expect the generated suite data to match that of the raw run text - EXPECT_TRUE(suites == expectedSuites); - } - - TEST(GTestRunSuiteFactory, ParseTestTargetC_ReturnsValidSuitesAndTests) - { - const AZStd::vector expectedSuites = GetTestTargetCTestRunSuites(); - - // Given the raw run output of TestTargetC - const AZStd::string rawRun = - "" - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - // When the raw run text is parsed - const AZStd::vector suites = TestImpact::GTest::TestRunSuitesFactory(rawRun); - - // Expect the generated suite data to match that of the raw run text - EXPECT_TRUE(suites == expectedSuites); - } - - TEST(GTestRunSuiteFactory, ParseTestTargetD_ReturnsValidSuitesAndTests) - { - const AZStd::vector expectedSuites = GetTestTargetDTestRunSuites(); - - // Given the raw run output of TestTargetD - const AZStd::string rawRun = - "" - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - // When the raw run text is parsed - const AZStd::vector suites = TestImpact::GTest::TestRunSuitesFactory(rawRun); - - // Expect the generated suite data to match that of the raw run text - EXPECT_TRUE(suites == expectedSuites); - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestTargetMetaMapFactoryTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestTargetMetaMapFactoryTest.cpp deleted file mode 100644 index 588c46d1ce..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Artifact/TestImpactTestTargetMetaMapFactoryTest.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include - -#include -#include - -namespace UnitTest -{ - TEST(TestTargetMetaMapFactoryTest, NoRawData_ExpectArtifactException) - { - // Given an empty meta data string - const AZStd::string rawTestTargetMetaData; - - try - { - // When attempting to construct the test target - const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(TestTargetMetaMapFactoryTest, InvalidRawData_ExpectArtifactException) - { - // Given a raw meta data string of invalid data - const AZStd::string rawTestTargetMetaData = "abcde"; - - try - { - // When attempting to construct the test target - const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(TestTargetMetaMapFactoryTest, EmptyTestMetaArray_ExpectArtifactException) - { - // Given a raw meta data string of valid JSON that contains no tests - const AZStd::string rawTestTargetMetaData = - "{" - " \"google\": {" - " \"test\": {" - " \"tests\": [" - " ]" - " }" - " }" - "}"; - - try - { - // When attempting to construct the test target - const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(TestTargetMetaMapFactoryTest, EmptyName_ExpectArtifactException) - { - // Given a raw meta data string with a test that has no name value - const AZStd::string rawTestTargetMetaData = - "{" - " \"google\": {" - " \"test\": {" - " \"tests\": [" - " { \"name\": \"\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"test_runner\" }" - " ]" - " }" - " }" - "}"; - - try - { - // When attempting to construct the test target - const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(TestTargetMetaMapFactoryTest, EmptyBuildType_ExpectArtifactException) - { - // Given a raw meta data string with a test that has no build type - const AZStd::string rawTestTargetMetaData = - "{" - " \"google\": {" - " \"test\": {" - " \"tests\": [" - " { \"name\": \"TestName\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"\" }" - " ]" - " }" - " }" - "}"; - - try - { - // When attempting to construct the test target - const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(TestTargetMetaMapFactoryTest, InvalidBuildType_ExpectArtifactException) - { - // Given a raw meta data string with a test that has an invalid build type - const AZStd::string rawTestTargetMetaData = - "{" - " \"google\": {" - " \"test\": {" - " \"tests\": [" - " { \"name\": \"TestName\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"Unknown\" }" - " ]" - " }" - " }" - "}"; - - try - { - // When attempting to construct the test target - const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); - - // Do not expect this statement to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ArtifactException& e) - { - // Expect an artifact exception - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(TestTargetMetaMapFactoryTest, ValidMetaData_ExpectValidTestMetaData) - { - // Given a raw meta data string valid test meta-data - const AZStd::string rawTestTargetMetaData = - "{" - " \"google\": {" - " \"test\": {" - " \"tests\": [" - " { \"name\": \"TestA\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"test_runner\" }," - " { \"name\": \"TestB\", \"namespace\": \"\", \"suite\": \"main\", \"launch_method\": \"test_runner\" }," - " { \"name\": \"TestC\", \"namespace\": \"\", \"suite\": \"\", \"launch_method\": \"test_runner\" }," - " { \"name\": \"TestD\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"stand_alone\" }" - " ]" - " }" - " }" - "}"; - - // When attempting to construct the test target - const auto testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData); - - // Expect the constructed test meta-data to match that of the supplied raw data - EXPECT_EQ(testTargetMetaData.size(), 4); - EXPECT_TRUE(testTargetMetaData.find("TestA") != testTargetMetaData.end()); - EXPECT_TRUE((testTargetMetaData.at("TestA") == TestImpact::TestTargetMeta{"main", TestImpact::LaunchMethod::TestRunner})); - EXPECT_TRUE(testTargetMetaData.find("TestB") != testTargetMetaData.end()); - EXPECT_TRUE((testTargetMetaData.at("TestB") == TestImpact::TestTargetMeta{"main", TestImpact::LaunchMethod::TestRunner})); - EXPECT_TRUE(testTargetMetaData.find("TestC") != testTargetMetaData.end()); - EXPECT_TRUE((testTargetMetaData.at("TestC") == TestImpact::TestTargetMeta{"", TestImpact::LaunchMethod::TestRunner})); - EXPECT_TRUE(testTargetMetaData.find("TestD") != testTargetMetaData.end()); - EXPECT_TRUE((testTargetMetaData.at("TestD") == TestImpact::TestTargetMeta{"main", TestImpact::LaunchMethod::StandAlone})); - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessSchedulerTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessSchedulerTest.cpp deleted file mode 100644 index 2b32067688..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessSchedulerTest.cpp +++ /dev/null @@ -1,712 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace UnitTest -{ - using SchedulerPermutation = AZStd::tuple - < - size_t, // Number of concurrent processes - size_t // Number of processes to launch - >; - - class ProcessSchedulerTestFixtureWithParams - : public AllocatorsTestFixture - , private AZ::Debug::TraceMessageBus::Handler - , public ::testing::WithParamInterface - { - private: - bool OnOutput(const char*, const char*) override; - void SetUp() override; - void TearDown() override; - - protected: - void ConfigurePermutation(); - void ExpectSuccessfulLaunch(TestImpact::ProcessId pid); - void ExpectGracefulExit(TestImpact::ProcessId pid); - void ExpectUnsuccessfulLaunch(TestImpact::ProcessId pid); - void ExpectExitCondition( - TestImpact::ProcessId pid, AZStd::optional expectedExitCondition = AZStd::nullopt); - void DoNotExpectExitCondition(TestImpact::ProcessId pid); - void ExpectTerminatedProcess(TestImpact::ProcessId pid); - void ExpectTimeoutProcess(TestImpact::ProcessId pid); - void ExpectUnlaunchedProcess(TestImpact::ProcessId pid); - void ExpectLargeStdOutput(TestImpact::ProcessId pid); - void ExpectLargeStdError(TestImpact::ProcessId pid); - void ExpectSmallStdOutput(TestImpact::ProcessId pid); - void ExpectSmallStdError(TestImpact::ProcessId pid); - void DoNotExpectStdOutput(TestImpact::ProcessId pid); - void DoNotExpectStdError(TestImpact::ProcessId pid); - - struct ProcessResult - { - AZStd::optional m_launchResult; - AZStd::optional m_exitStatus; - AZStd::optional m_createTime; - AZStd::optional m_exitTime; - AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds(0); - AZStd::optional m_returnCode; - TestImpact::StdContent m_std; - }; - - size_t m_numMaxConcurrentProcesses = 0; - size_t m_numProcessesToLaunch = 0; - - AZStd::unique_ptr m_processScheduler; - TestImpact::ProcessLaunchCallback m_processLaunchCallback; - TestImpact::ProcessExitCallback m_processExitCallback; - AZStd::vector m_processInfos; - AZStd::vector m_processResults; - }; - - // Permutation values for small process batches - const std::vector SmallNumMaxConcurrentProcesses = {1, 4, 8}; - const std::vector SmallNumProcessesToLaunch = {1, 8, 32}; - - // Permutation values for large process batches - const std::vector LargeNumMaxConcurrentProcesses = {16, 32, 64}; - const std::vector LargeNumProcessesToLaunch = {128, 256, 512}; - - void ProcessSchedulerTestFixtureWithParams::ConfigurePermutation() - { - const auto& [numMaxConcurrentProcesses, numProcessesToLaunch] = GetParam(); - m_numMaxConcurrentProcesses = numMaxConcurrentProcesses; - m_numProcessesToLaunch = numProcessesToLaunch; - } - - // Consume AZ log spam - bool ProcessSchedulerTestFixtureWithParams::OnOutput([[maybe_unused]] const char*, [[maybe_unused]] const char*) - { - return true; - } - - void ProcessSchedulerTestFixtureWithParams::SetUp() - { - UnitTest::AllocatorsTestFixture::SetUp(); - AZ::Debug::TraceMessageBus::Handler::BusConnect(); - ConfigurePermutation(); - - m_processLaunchCallback = [this]( - TestImpact::ProcessId pid, - TestImpact::LaunchResult launchResult, - AZStd::chrono::high_resolution_clock::time_point createTime) - { - m_processResults[pid].m_launchResult = launchResult; - m_processResults[pid].m_createTime.emplace(createTime); - return TestImpact::CallbackResult::Continue; - }; - - m_processExitCallback = [this]( - TestImpact::ProcessId pid, - TestImpact::ExitCondition exitStatus, - TestImpact::ReturnCode returnCode, - TestImpact::StdContent&& std, - AZStd::chrono::high_resolution_clock::time_point exitTime) - { - m_processResults[pid].m_std = std::move(std); - m_processResults[pid].m_returnCode.emplace(returnCode); - m_processResults[pid].m_exitStatus = exitStatus; - m_processResults[pid].m_exitTime = exitTime; - m_processResults[pid].m_duration = - AZStd::chrono::duration_cast(exitTime - *m_processResults[pid].m_createTime); - return TestImpact::CallbackResult::Continue; - }; - - m_processResults.resize(m_numProcessesToLaunch); - } - - void ProcessSchedulerTestFixtureWithParams::TearDown() - { - AZ::Debug::TraceMessageBus::Handler::BusDisconnect(); - UnitTest::AllocatorsTestFixture::TearDown(); - } - - // Expects the process to have exited under the specified circumstances - void ProcessSchedulerTestFixtureWithParams::ExpectExitCondition( - TestImpact::ProcessId pid, AZStd::optional expectedExitCondition) - { - const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; - - // Expect the processes to have exited - EXPECT_TRUE(exitStatus.has_value()); - - // Expect the return code to be valid - EXPECT_TRUE(returnCode.has_value()); - - if (expectedExitCondition.has_value()) - { - // Expect the process to have exited under the specified conditions - EXPECT_EQ(exitStatus, expectedExitCondition); - - // Expect the return code to be that of the process's is (for gracefull exists) or that of the error code - // associated with the abnormal exit condition - EXPECT_EQ(returnCode, - expectedExitCondition == TestImpact::ExitCondition::Gracefull - ? pid - : static_cast(*exitStatus)); - } - - // Expect the duration to be non zero and the create time and exit time to have values as the processes has been in-flight - EXPECT_GT(duration.count(), 0); - EXPECT_TRUE(createTime.has_value()); - EXPECT_TRUE(exitTime.has_value()); - - // Expect the exit time to be later than the start time - EXPECT_GT(exitTime, createTime); - } - - // Expects the process to not have exited for to lack of being in-flight - void ProcessSchedulerTestFixtureWithParams::DoNotExpectExitCondition(TestImpact::ProcessId pid) - { - const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; - - // Expect the processes to not have exited as the process has not been launched successfully - EXPECT_FALSE(exitStatus.has_value()); - - // Expect the return code to be invalid - EXPECT_FALSE(returnCode.has_value()); - - // Expect the duration to be zero as the process has not been in-flight - EXPECT_EQ(duration.count(), 0); - - // Do not expect the exit time to have a value - EXPECT_FALSE(exitTime.has_value()); - } - - // Expects the process to have been launched successfully, makes no assumptions about how it exited - void ProcessSchedulerTestFixtureWithParams::ExpectSuccessfulLaunch(TestImpact::ProcessId pid) - { - const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; - - // Expect a launch to have been attempted by this process (not still in queue) - EXPECT_TRUE(launchResult.has_value()); - - // Expect the process to have launched successfully - EXPECT_EQ(launchResult, TestImpact::LaunchResult::Success); - } - - // Expects the process to have failed to launch - void ProcessSchedulerTestFixtureWithParams::ExpectUnsuccessfulLaunch(TestImpact::ProcessId pid) - { - const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; - - // Expect a launch to have been attempted by this process (not still in queue) - EXPECT_TRUE(launchResult.has_value()); - - // Expect the process to have launched unsuccessfully - EXPECT_EQ(launchResult, TestImpact::LaunchResult::Failure); - - // Expect the create time to have a value as the process was technically created - EXPECT_TRUE(createTime.has_value()); - - DoNotExpectExitCondition(pid); - } - - // Expects the process to have exited gracefully of its own accord (i.e. not terminated for any reason by the scheduler) - void ProcessSchedulerTestFixtureWithParams::ExpectGracefulExit(TestImpact::ProcessId pid) - { - ExpectSuccessfulLaunch(pid); - ExpectExitCondition(pid, TestImpact::ExitCondition::Gracefull); - } - - // Expects the process to have been terminated by the client or scheduler - void ProcessSchedulerTestFixtureWithParams::ExpectTerminatedProcess(TestImpact::ProcessId pid) - { - ExpectSuccessfulLaunch(pid); - ExpectExitCondition(pid, TestImpact::ExitCondition::Terminated); - } - - // Expects the process to have been terminated by the scheduler due to the process or scheduler timing out - void ProcessSchedulerTestFixtureWithParams::ExpectTimeoutProcess(TestImpact::ProcessId pid) - { - ExpectSuccessfulLaunch(pid); - ExpectExitCondition(pid, TestImpact::ExitCondition::Timeout); - } - - // Expects the process to have not been attempted to launch (was still in the queue) - void ProcessSchedulerTestFixtureWithParams::ExpectUnlaunchedProcess(TestImpact::ProcessId pid) - { - const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid]; - - // Expect a launch to not have been attempted by this process (still in queue) - EXPECT_FALSE(launchResult.has_value()); - - // Expect the create time to have a value as the process was technically created - EXPECT_FALSE(createTime.has_value()); - - DoNotExpectExitCondition(pid); - } - - // Expects the std output to have been captured from the process with a large volume of text - void ProcessSchedulerTestFixtureWithParams::ExpectLargeStdOutput(TestImpact::ProcessId pid) - { - const auto& stdOut = m_processResults[pid].m_std.m_out; - - // Expect standard output to have a value - EXPECT_TRUE(stdOut.has_value()); - - // Expect the output length to be that of the large text output from the child process - EXPECT_EQ(stdOut.value().length(), LargeTextSize); - } - - // Expects the std error to have been captured from the process with a large volume of text - void ProcessSchedulerTestFixtureWithParams::ExpectLargeStdError(TestImpact::ProcessId pid) - { - const auto& stdErr = m_processResults[pid].m_std.m_err; - - // Expect standard error to have a value - EXPECT_TRUE(stdErr.has_value()); - - // Expect the error length to be that of the large text output from the child process - EXPECT_EQ(stdErr.value().length(), LargeTextSize); - } - - // Expects the std output to have been captured from the process with a small known text string - void ProcessSchedulerTestFixtureWithParams::ExpectSmallStdOutput(TestImpact::ProcessId pid) - { - const auto& stdOut = m_processResults[pid].m_std.m_out; - - // Expect standard output to have a value - EXPECT_TRUE(stdOut.has_value()); - - // Expect the output to match the known stdout of the child - EXPECT_EQ(stdOut.value(), KnownTestProcessOutputString(pid)); - } - - // Expects the std error to have been captured from the process with a small known text string - void ProcessSchedulerTestFixtureWithParams::ExpectSmallStdError(TestImpact::ProcessId pid) - { - const auto& stdErr = m_processResults[pid].m_std.m_err; - - // Expect standard error to have a value - EXPECT_TRUE(stdErr.has_value()); - - // Expect the output to match the known stderr of the child - EXPECT_EQ(stdErr.value(), KnownTestProcessErrorString(pid)); - } - - // Expects no standard output from the child process - void ProcessSchedulerTestFixtureWithParams::DoNotExpectStdOutput(TestImpact::ProcessId pid) - { - const auto& stdOut = m_processResults[pid].m_std.m_out; - - // Do not expect standard output to have a value - EXPECT_FALSE(stdOut.has_value()); - } - - // Expects no standard error from the child process - void ProcessSchedulerTestFixtureWithParams::DoNotExpectStdError(TestImpact::ProcessId pid) - { - const auto& stdErr = m_processResults[pid].m_std.m_err; - - // Do not expect standard error to have a value - EXPECT_FALSE(stdErr.has_value()); - } - - TEST_P(ProcessSchedulerTestFixtureWithParams, ValidProcesses_SuccessfulLaunches) - { - // Given a set of processes to launch with valid arguments - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - m_processInfos.emplace_back( - pid, - ValidProcessPath, - ConstructTestProcessArgs(pid, NoSleep)); - } - - // When the process scheduler launches the processes - m_processScheduler = AZStd::make_unique( - m_processInfos, - m_processLaunchCallback, - m_processExitCallback, - m_numMaxConcurrentProcesses, - AZStd::nullopt, - AZStd::nullopt - ); - - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - ExpectGracefulExit(pid); - } - } - - TEST_P(ProcessSchedulerTestFixtureWithParams, ValidAndInvalidProcesses_LaunchSuccessesAndFailures) - { - const size_t invalidProcessGroup = 4; - - // Given a mixture of processes to launch with valid and invalid arguments - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - if (pid % invalidProcessGroup == 0) - { - m_processInfos.emplace_back( - pid, - InvalidProcessPath, - ConstructTestProcessArgs(pid, NoSleep)); - } - else - { - m_processInfos.emplace_back( - pid, - ValidProcessPath, - ConstructTestProcessArgs(pid, NoSleep)); - } - } - - // When the process scheduler launches the processes - m_processScheduler = AZStd::make_unique( - m_processInfos, - m_processLaunchCallback, - m_processExitCallback, - m_numMaxConcurrentProcesses, - AZStd::nullopt, - AZStd::nullopt - ); - - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - if (pid % invalidProcessGroup == 0) - { - ExpectUnsuccessfulLaunch(pid); - } - else - { - ExpectGracefulExit(pid); - } - - DoNotExpectStdOutput(pid); - DoNotExpectStdError(pid); - } - } - - TEST_P(ProcessSchedulerTestFixtureWithParams, ProcessTimeouts_InFlightProcessesTimeout) - { - const size_t timeoutProcessGroup = 4; - - // Given a mixture of processes to launch with some processes sleeping indefinately - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - if (pid % timeoutProcessGroup == 0) - { - m_processInfos.emplace_back(pid, ValidProcessPath, ConstructTestProcessArgs(pid, LongSleep)); - } - else - { - m_processInfos.emplace_back(pid, ValidProcessPath, ConstructTestProcessArgs(pid, NoSleep)); - } - } - - // When the process scheduler launches the processes with a process timeout value - m_processScheduler = AZStd::make_unique( - m_processInfos, - m_processLaunchCallback, - m_processExitCallback, - m_numMaxConcurrentProcesses, - AZStd::chrono::milliseconds(100), - AZStd::nullopt - ); - - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - if (pid % timeoutProcessGroup == 0) - { - ExpectTimeoutProcess(pid); - } - else - { - ExpectExitCondition(pid); - } - - DoNotExpectStdOutput(pid); - DoNotExpectStdError(pid); - } - } - - TEST_P( - ProcessSchedulerTestFixtureWithParams, ProcessLaunchCallbackAbort_InFlightProcessesTerminatedAndQueuedProcessesUnlaunched) - { - const size_t processToAbort = 8; - - // Given a set of processes to launch - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - m_processInfos.emplace_back( - pid, - ValidProcessPath, - ConstructTestProcessArgs(pid, NoSleep)); - } - - // Given a launch callback that will return the abort value for the process to abort - const auto abortingLaunchCallback = [this, processToAbort]( - TestImpact::ProcessId pid, - TestImpact::LaunchResult launchResult, - AZStd::chrono::high_resolution_clock::time_point createTime) - { - m_processLaunchCallback(pid, launchResult, createTime); - - if (pid == processToAbort) - { - return TestImpact::CallbackResult::Abort; - } - else - { - return TestImpact::CallbackResult::Continue; - } - }; - - // When the process scheduler launches the processes - m_processScheduler = AZStd::make_unique( - m_processInfos, - abortingLaunchCallback, - m_processExitCallback, - m_numMaxConcurrentProcesses, - AZStd::nullopt, - AZStd::nullopt - ); - - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - if (pid < processToAbort) - { - ExpectSuccessfulLaunch(pid); - } - else if (pid == processToAbort) - { - ExpectTerminatedProcess(pid); - } - else - { - ExpectUnlaunchedProcess(pid); - } - - DoNotExpectStdOutput(pid); - DoNotExpectStdError(pid); - } - } - - TEST_P(ProcessSchedulerTestFixtureWithParams, ProcessExitCallbackAbort_InFlightProcessesTerminated) - { - const size_t processToAbort = 4; - - // Given a set of processes to launch - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - m_processInfos.emplace_back( - pid, - ValidProcessPath, - ConstructTestProcessArgs(pid, NoSleep)); - } - - // Given an exit callback that will return the abort value for the process to abort - const auto abortingExitCallback = [this, processToAbort]( - TestImpact::ProcessId pid, - TestImpact::ExitCondition exitStatus, - TestImpact::ReturnCode returnCode, - TestImpact::StdContent&& std, - AZStd::chrono::high_resolution_clock::time_point exitTime) - { - m_processExitCallback(pid, exitStatus, returnCode, std::move(std), exitTime); - - if (pid == processToAbort) - { - return TestImpact::CallbackResult::Abort; - } - else - { - return TestImpact::CallbackResult::Continue; - } - }; - - // When the process scheduler launches the processes - m_processScheduler = AZStd::make_unique( - m_processInfos, - m_processLaunchCallback, - abortingExitCallback, - m_numMaxConcurrentProcesses, - AZStd::nullopt, - AZStd::nullopt - ); - - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - if (pid < processToAbort) - { - ExpectSuccessfulLaunch(pid); - } - else if (pid == processToAbort) - { - ExpectGracefulExit(pid); - } - else - { - if (m_processResults[pid].m_launchResult.has_value()) - { - ExpectSuccessfulLaunch(pid); - } - else - { - ExpectUnlaunchedProcess(pid); - } - } - - DoNotExpectStdOutput(pid); - DoNotExpectStdError(pid); - } - } - - TEST_P(ProcessSchedulerTestFixtureWithParams, SchedulerTimeout_QueuedAndInFlightProcessesTerminated) - { - const size_t processToTimeout = 0; - - // Given a set of processes to launch where one process will sleep indefinately - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - if (pid == processToTimeout) - { - m_processInfos.emplace_back( - pid, - ValidProcessPath, - ConstructTestProcessArgs(pid, LongSleep)); - } - else - { - m_processInfos.emplace_back( - pid, - ValidProcessPath, - ConstructTestProcessArgs(pid, NoSleep)); - } - } - - // When the process scheduler launches the processes with a scheduler timeout value - m_processScheduler = AZStd::make_unique( - m_processInfos, - m_processLaunchCallback, - m_processExitCallback, - m_numMaxConcurrentProcesses, - AZStd::nullopt, - AZStd::chrono::milliseconds(100) - ); - - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - if (pid == processToTimeout) - { - ExpectTimeoutProcess(pid); - } - else - { - if (m_processResults[pid].m_launchResult.has_value()) - { - ExpectSuccessfulLaunch(pid); - } - else - { - ExpectUnlaunchedProcess(pid); - } - } - - DoNotExpectStdOutput(pid); - DoNotExpectStdError(pid); - } - } - - TEST_P(ProcessSchedulerTestFixtureWithParams, RedirectStdOut_StdOutputIsLargeTextString) - { - // Given a set of processes to launch - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - m_processInfos.emplace_back( - pid, - TestImpact::StdOutputRouting::ToParent, - TestImpact::StdErrorRouting::None, - ValidProcessPath, - ConstructTestProcessArgsLargeText(pid, NoSleep)); - } - - // When the process scheduler launches the processes - m_processScheduler = AZStd::make_unique( - m_processInfos, - m_processLaunchCallback, - m_processExitCallback, - m_numMaxConcurrentProcesses, - AZStd::nullopt, - AZStd::nullopt - ); - - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - ExpectGracefulExit(pid); - ExpectLargeStdOutput(pid); - DoNotExpectStdError(pid); - } - } - - TEST_P(ProcessSchedulerTestFixtureWithParams, RedirectStdError_StdErrorIsLargeTextString) - { - // Given a set of processes to launch - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - m_processInfos.emplace_back( - pid, - TestImpact::StdOutputRouting::None, - TestImpact::StdErrorRouting::ToParent, - ValidProcessPath, - ConstructTestProcessArgsLargeText(pid, NoSleep)); - } - - // When the process scheduler launches the processes - m_processScheduler = AZStd::make_unique( - m_processInfos, - m_processLaunchCallback, - m_processExitCallback, - m_numMaxConcurrentProcesses, - AZStd::nullopt, - AZStd::nullopt - ); - - for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++) - { - ExpectGracefulExit(pid); - DoNotExpectStdOutput(pid); - ExpectLargeStdError(pid); - } - } - - INSTANTIATE_TEST_CASE_P( - SmallConcurrentProcesses, - ProcessSchedulerTestFixtureWithParams, - ::testing::Combine( - ::testing::ValuesIn(SmallNumMaxConcurrentProcesses), - ::testing::ValuesIn(SmallNumProcessesToLaunch) - )); - - INSTANTIATE_TEST_CASE_P( - LargeConcurrentProcesses, - ProcessSchedulerTestFixtureWithParams, - ::testing::Combine( - ::testing::ValuesIn(LargeNumMaxConcurrentProcesses), - ::testing::ValuesIn(LargeNumProcessesToLaunch) - )); -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessTest.cpp deleted file mode 100644 index df4ea5a967..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Process/TestImpactProcessTest.cpp +++ /dev/null @@ -1,491 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include - -#include -#include -#include - -namespace UnitTest -{ - class ProcessTestFixture - : public AllocatorsTestFixture - { - protected: - void SetUp() override - { - UnitTest::AllocatorsTestFixture::SetUp(); - } - - void TearDown() override - { - if (m_process && m_process->IsRunning()) - { - m_process->Terminate(0); - } - - UnitTest::AllocatorsTestFixture::TearDown(); - } - - AZStd::unique_ptr m_process; - static constexpr const TestImpact::ProcessId m_id = 1; - static constexpr const TestImpact::ReturnCode m_terminateErrorCode = 666; - }; - - TEST_F(ProcessTestFixture, LaunchValidProcess_ProcessReturnsSuccessfully) - { - // Given a process launched with a valid binary - TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process has exited - m_process->BlockUntilExit(); - - // Expect the process to no longer be running - EXPECT_FALSE(m_process->IsRunning()); - } - - TEST_F(ProcessTestFixture, LaunchInvalidProcessInfo_ThrowsProcessException) - { - try - { - // Given a process launched with an invalid binary - TestImpact::ProcessInfo processInfo(m_id, ""); - - // Do not expect this code to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ProcessException& e) - { - // Except a process exception to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(ProcessTestFixture, LaunchInvalidBinary_ThrowsProcessException) - { - try - { - // Given a process launched with an ivalid binary - TestImpact::ProcessInfo processInfo(m_id, "#!#zz:/z/z/z.exe.z@"); - - // When the process is attempted launch - m_process = TestImpact::LaunchProcess(processInfo); - - // Do not expect this code to be reachable - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::ProcessException& e) - { - // Except a process exception to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(ProcessTestFixture, GetProcessInfo_ReturnsProcessInfo) - { - // Given a process launched with a valid binary and args - const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); - TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process has exited - m_process->BlockUntilExit(); - - // Expect the retrieved process info to match to process info used to launch the process - EXPECT_EQ(m_process->GetProcessInfo().GetId(), m_id); - EXPECT_EQ(m_process->GetProcessInfo().GetProcessPath(), ValidProcessPath); - EXPECT_EQ(m_process->GetProcessInfo().GetStartupArgs(), args); - } - - TEST_F(ProcessTestFixture, GetReturnCodeAfterExit_ReturnsArg) - { - // Given a process launched with a valid binary and args - const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); - TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process has exited - m_process->BlockUntilExit(); - - // Expect the process to no longer be running - EXPECT_FALSE(m_process->IsRunning()); - - // Expect the return value to be the process id - EXPECT_EQ(m_process->GetReturnCode(), m_id); - } - - TEST_F(ProcessTestFixture, GetReturnCodeInFlight_ReturnsNullOpt) - { - // Given a process launched with a valid binary and args to sleep for 100 seconds - const AZStd::string args = ConstructTestProcessArgs(m_id, LongSleep); - TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args); - - // When the process is running - m_process = TestImpact::LaunchProcess(processInfo); - - // Expect the return value to be empty - EXPECT_EQ(m_process->GetReturnCode(), AZStd::nullopt); - } - - TEST_F(ProcessTestFixture, TerminateWithErrorCode_ReturnsErrorCode) - { - // Given a process launched with a valid binary and args to sleep for 100 seconds - const AZStd::string args = ConstructTestProcessArgs(m_id, LongSleep); - TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process is terminated by the client with the specified error code - m_process->Terminate(m_terminateErrorCode); - - // Expect the return value to be the error code specified in the Terminate call - EXPECT_EQ(m_process->GetReturnCode(), m_terminateErrorCode); - - // Expect the process to no longer be running - EXPECT_FALSE(m_process->IsRunning()); - } - - TEST_F(ProcessTestFixture, CheckIsRunningWhilstRunning_ProcessIsRunning) - { - // Given a process launched with a valid binary and args to sleep for 100 seconds - const AZStd::string args = ConstructTestProcessArgs(m_id, LongSleep); - TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args); - - // When the process is running - m_process = TestImpact::LaunchProcess(processInfo); - - // Expect the process to be running - EXPECT_TRUE(m_process->IsRunning()); - } - - TEST_F(ProcessTestFixture, CheckIsRunningWhilstNotRunning_ReturnsFalse) - { - // Given a process launched with a valid binary - TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process is terminated by the client - m_process->Terminate(0); - - // Expect the process to be running - EXPECT_FALSE(m_process->IsRunning()); - } - - TEST_F(ProcessTestFixture, RedirectStdOut_OutputIsKnownTestProcessOutputString) - { - // Given a process launched with a valid binary and standard output redirected to parent - const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); - TestImpact::ProcessInfo processInfo( - m_id, - TestImpact::StdOutputRouting::ToParent, - TestImpact::StdErrorRouting::None, - ValidProcessPath, - args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process exits - m_process->BlockUntilExit(); - - // Expect stdoutput to be rerouted to the parent - EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdOutput()); - - // Do not expect stdouterr to be rerouted to the parent - EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdError()); - - // Expect the output to match the known stdout of the child - EXPECT_EQ(m_process->ConsumeStdOut(), KnownTestProcessOutputString(m_id)); - } - - TEST_F(ProcessTestFixture, RedirectStdErr_OutputIsKnownTestProcessOutputString) - { - // Given a process launched with a valid binary and standard error redirected to parent - const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); - TestImpact::ProcessInfo processInfo( - m_id, - TestImpact::StdOutputRouting::None, - TestImpact::StdErrorRouting::ToParent, - ValidProcessPath, - args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process exits - m_process->BlockUntilExit(); - - // Do not expect stdoutput to be rerouted to the parent - EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdOutput()); - - // Expect stderror to be rerouted to the parent - EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdError()); - - // Expect the output to match the known stdout of the child - EXPECT_EQ(m_process->ConsumeStdErr(), KnownTestProcessErrorString(m_id)); - } - - TEST_F(ProcessTestFixture, RedirectStdOutAndTerminate_OutputIsEmpty) - { - // Given a process launched with a valid binary and standard output redirected to parent - const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); - TestImpact::ProcessInfo processInfo( - m_id, - TestImpact::StdOutputRouting::ToParent, - TestImpact::StdErrorRouting::None, - ValidProcessPath, - args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process is terminated - m_process->Terminate(0); - - // Expect stdoutput to be rerouted to the parent - EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdOutput()); - - // Do not expect stdouterr to be rerouted to the parent - EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdError()); - - // Expect the output to be empty - EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); - } - - TEST_F(ProcessTestFixture, RedirectStdErrAndTerminate_OutputIsEmpty) - { - // Given a process launched with a valid binary and standard error redirected to parent - const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); - TestImpact::ProcessInfo processInfo( - m_id, - TestImpact::StdOutputRouting::None, - TestImpact::StdErrorRouting::ToParent, - ValidProcessPath, - args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process is terminated - m_process->Terminate(0); - - // Do not expect stdoutput to be rerouted to the parent - EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdOutput()); - - // Expect stderror to be rerouted to the parent - EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdError()); - - // Expect the output to be empty - EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); - } - - TEST_F(ProcessTestFixture, RedirectStdOutAndStdErrorRouting_OutputsAreKnownTestProcessOutputStrings) - { - // Given a process launched with a valid binary and both standard output and standard error redirected to parent - const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); - TestImpact::ProcessInfo processInfo( - m_id, - TestImpact::StdOutputRouting::ToParent, - TestImpact::StdErrorRouting::ToParent, - ValidProcessPath, - args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process exits - m_process->BlockUntilExit(); - - // Expect stdoutput to be rerouted to the parent - EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdOutput()); - - // Expect stderror to be rerouted to the parent - EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdError()); - - // Expect the output to match the known stdout and stderr of the child - EXPECT_EQ(m_process->ConsumeStdOut(), KnownTestProcessOutputString(m_id)); - EXPECT_EQ(m_process->ConsumeStdErr(), KnownTestProcessErrorString(m_id)); - } - - TEST_F(ProcessTestFixture, NoStdOutOrStdErrRedirect_OutputIsEmpty) - { - // Given a process launched with a valid binary and both standard output and standard error redirected to parent - const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep); - TestImpact::ProcessInfo processInfo( - m_id, - TestImpact::StdOutputRouting::None, - TestImpact::StdErrorRouting::None, - ValidProcessPath, - args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process exits - m_process->BlockUntilExit(); - - // Do not expect stdoutput to be rerouted to the parent - EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdOutput()); - - // Do not expect stdouterr to be rerouted to the parent - EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdError()); - - // Expect the output to to be empty - EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); - EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); - } - - TEST_F(ProcessTestFixture, LargePipeNoRedirects_OutputsAreEmpty) - { - // Given a process outputting large text with no standard output and no standard error redirected to parent - const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, NoSleep); - TestImpact::ProcessInfo processInfo( - m_id, - TestImpact::StdOutputRouting::None, - TestImpact::StdErrorRouting::None, - ValidProcessPath, - args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process exits - m_process->BlockUntilExit(); - - // Expect the output to to be empty - EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); - EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); - } - - TEST_F(ProcessTestFixture, LargePipeNoRedirectsAndTerminated_OutputsAreEmpty) - { - // Given a process outputting large text with no standard output and standard error redirected to parent - const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, LongSleep); - TestImpact::ProcessInfo processInfo( - m_id, - TestImpact::StdOutputRouting::None, - TestImpact::StdErrorRouting::None, - ValidProcessPath, - args); - - // When the process is running - m_process = TestImpact::LaunchProcess(processInfo); - - // Expect the output to to be empty - EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); - EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); - - // When the process is terminated - m_process->Terminate(0); - - // Expect the output to to be empty - EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); - EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); - } - - TEST_F(ProcessTestFixture, LargePipeRedirectsAndReadWhilstRunning_OutputsAreEmpty) - { - // Given a process outputting large text with no standard output and standard error redirected to parent - const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, ShortSleep); - TestImpact::ProcessInfo processInfo( - m_id, - TestImpact::StdOutputRouting::None, - TestImpact::StdErrorRouting::None, - ValidProcessPath, - args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the outputs are read as the process is running - while (m_process->IsRunning()) - { - // Expect the outputs to be empty - EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); - EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); - } - } - - TEST_F(ProcessTestFixture, LargePipeRedirectsAndTerminated_OutputsAreEmpty) - { - // Given a process outputting large text with both standard output and standard error redirected to parent - const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, LongSleep); - TestImpact::ProcessInfo processInfo( - m_id, - TestImpact::StdOutputRouting::ToParent, - TestImpact::StdErrorRouting::ToParent, - ValidProcessPath, - args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process is terminated - m_process->Terminate(0); - - // Expect the outputs to be empty - EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); - EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); - } - - TEST_F(ProcessTestFixture, LargePipeRedirectsAndBlockedUntilExit_OutputsAreLargeTextFileSize) - { - // Given a process outputting large text with both standard output and standard error redirected to parent - const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, NoSleep); - TestImpact::ProcessInfo processInfo( - m_id, - TestImpact::StdOutputRouting::ToParent, - TestImpact::StdErrorRouting::ToParent, - ValidProcessPath, - args); - m_process = TestImpact::LaunchProcess(processInfo); - - // When the process exits - m_process->BlockUntilExit(); - - // Expect the output lengths to be that of the large text output from the child process - EXPECT_EQ(m_process->ConsumeStdOut().value().length(), LargeTextSize); - EXPECT_EQ(m_process->ConsumeStdErr().value().length(), LargeTextSize); - } - - TEST_F(ProcessTestFixture, LargePipeRedirectsAndReadWhilstRunning_TotalOutputsAreLargeTextFileSize) - { - // Given a process outputting large text with both standard output and standard error redirected to parent - const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, NoSleep); - TestImpact::ProcessInfo processInfo( - m_id, - TestImpact::StdOutputRouting::ToParent, - TestImpact::StdErrorRouting::ToParent, - ValidProcessPath, args); - m_process = TestImpact::LaunchProcess(processInfo); - - size_t stdOutBytes = 0; - size_t stdErrBytes = 0; - while (m_process->IsRunning()) - { - // When the outputs are read as the process is running - if (auto output = m_process->ConsumeStdOut(); output.has_value()) - { - stdOutBytes += output.value().length(); - } - - if (auto output = m_process->ConsumeStdErr(); output.has_value()) - { - stdErrBytes += output.value().length(); - } - } - - // Expect the output lengths to be that of the large text output from the child process - EXPECT_EQ(stdOutBytes, LargeTextSize); - EXPECT_EQ(stdErrBytes, LargeTextSize); - - // Expect the outputs to be empty - EXPECT_FALSE(m_process->ConsumeStdOut().has_value()); - EXPECT_FALSE(m_process->ConsumeStdErr().has_value()); - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp deleted file mode 100644 index e7ebec17d1..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/Target/TestImpactBuildTargetTest.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include - -#include -#include - -namespace UnitTest -{ - namespace - { - AZStd::string GenerateBuildTargetName(size_t index) - { - return AZStd::string::format("Target%u", index); - } - - AZStd::string GenerateBuildTargetOutputName(size_t index) - { - return AZStd::string::format("Output%u", index); - } - - AZ::IO::Path GenerateBuildTargetPath(size_t index) - { - return AZStd::string::format("C:\\Repo\\Dir%u", index); - } - - AZStd::string GenerateTestTargetSuite(size_t index) - { - return AZStd::string::format("Suite%u", index); - } - - TestImpact::LaunchMethod GenerateLaunchMethod(size_t index) - { - return index % 2 == 0 ? TestImpact::LaunchMethod::StandAlone : TestImpact::LaunchMethod::TestRunner; - } - - AZStd::string GenerateStaticSourceFile(size_t index) - { - return AZStd::string::format("StaticSource%u", index); - } - - TestImpact::AutogenPairs GenerateAutogenSourceFiles(size_t index) - { - TestImpact::AutogenPairs autogen; - autogen.m_input = AZStd::string::format("InputSource%u", index); - autogen.m_outputs.push_back(AZStd::string::format("OutputSource%u", index)); - autogen.m_outputs.push_back(AZStd::string::format("OutputHeader%u", index)); - return autogen; - } - - TestImpact::TargetSources GenerateTargetSources(size_t index) - { - TestImpact::TargetSources sources; - sources.m_staticSources.resize(index + 1); - for (size_t i = 0; i < sources.m_staticSources.size(); i++) - { - sources.m_staticSources[i] = GenerateStaticSourceFile(i); - } - - // Only generate autogen sources for even-numbered indexes - if (index % 2 == 0) - { - sources.m_autogenSources.resize(index + 1); - for (size_t i = 0; i < sources.m_autogenSources.size(); i++) - { - sources.m_autogenSources[i] = GenerateAutogenSourceFiles(i); - } - } - - return sources; - } - - TestImpact::ProductionTargetDescriptor GenerateProductionTargetDescriptor(size_t index = 0) - { - return TestImpact::ProductionTargetDescriptor( - { - TestImpact::BuildMetaData - { - GenerateBuildTargetName(index), GenerateBuildTargetOutputName(index), GenerateBuildTargetPath(index) - }, - GenerateTargetSources(index) - }); - } - - TestImpact::TestTargetDescriptor GenerateTestTargetDescriptor(size_t index = 0) - { - return TestImpact::TestTargetDescriptor( - { - TestImpact::BuildMetaData - { - GenerateBuildTargetName(index), GenerateBuildTargetOutputName(index), GenerateBuildTargetPath(index) - }, - GenerateTargetSources(index) - }, - TestImpact::TestTargetMeta - { - GenerateTestTargetSuite(index), GenerateLaunchMethod(index) - }); - } - } // namespace - - template - typename Target::Descriptor GenerateTargetDescriptor(size_t index = 0) - { - static_assert( - AZStd::is_same_v || AZStd::is_same_v, - "Unexpected target type, wants TestImpact::ProductionTarget or TestImpact::TestTarget"); - - if constexpr (AZStd::is_same_v) - { - return GenerateProductionTargetDescriptor(index); - } - else - { - return GenerateTestTargetDescriptor(index); - } - } - - void ValidateSources(const TestImpact::TargetSources& sources, size_t index = 0) - { - EXPECT_EQ(sources.m_staticSources.size(), index + 1); - for (size_t i = 0; i < sources.m_staticSources.size(); i++) - { - EXPECT_EQ(sources.m_staticSources[i], GenerateStaticSourceFile(i)); - } - - // Even numbered indexes have autogen sources - if (index % 2 == 0) - { - EXPECT_EQ(sources.m_autogenSources.size(), index + 1); - for (size_t i = 0; i < sources.m_autogenSources.size(); i++) - { - enum - { - OutputSource = 0, - OutputHeader = 1 - }; - - const TestImpact::AutogenPairs expectedAutogenSources = GenerateAutogenSourceFiles(i); - EXPECT_EQ(sources.m_autogenSources[i].m_input, expectedAutogenSources.m_input); - EXPECT_EQ(sources.m_autogenSources[i].m_outputs[OutputSource], expectedAutogenSources.m_outputs[OutputSource]); - EXPECT_EQ(sources.m_autogenSources[i].m_outputs[OutputHeader], expectedAutogenSources.m_outputs[OutputHeader]); - } - } - else - { - EXPECT_TRUE(sources.m_autogenSources.empty()); - } - } - - void ValidateTarget(const TestImpact::ProductionTarget& target, size_t index = 0) - { - EXPECT_EQ(target.GetName(), GenerateBuildTargetName(index)); - EXPECT_EQ(target.GetOutputName(), GenerateBuildTargetOutputName(index)); - EXPECT_EQ(target.GetPath(), GenerateBuildTargetPath(index)); - EXPECT_EQ(target.GetType(), TestImpact::TargetType::Production); - ValidateSources(target.GetSources(), index); - } - - void ValidateTarget(const TestImpact::TestTarget& target, size_t index = 0) - { - EXPECT_EQ(target.GetName(), GenerateBuildTargetName(index)); - EXPECT_EQ(target.GetOutputName(), GenerateBuildTargetOutputName(index)); - EXPECT_EQ(target.GetPath(), GenerateBuildTargetPath(index)); - EXPECT_EQ(target.GetType(), TestImpact::TargetType::Test); - EXPECT_EQ(target.GetSuite(), GenerateTestTargetSuite(index)); - EXPECT_EQ(target.GetLaunchMethod(), GenerateLaunchMethod(index)); - ValidateSources(target.GetSources(), index); - } - - template - class TargetListFixture - : public AllocatorsTestFixture - { - public: - using TargetListType = TargetList; - using TargetType = typename TargetList::TargetType; - - static constexpr size_t m_numTargets = 10; - }; - - using TargetTypes = testing::Types; - TYPED_TEST_CASE(TargetListFixture, TargetTypes); - - TYPED_TEST(TargetListFixture, CreateTarget_ExpectValidTarget) - { - // Given a target of the specified type - TargetType target(GenerateTargetDescriptor()); - - // Expect the target to match the procedurally generated target descriptor - ValidateTarget(target); - } - - TYPED_TEST(TargetListFixture, CreateEmptyTargetList_ExpectTargetException) - { - try - { - // Given an empty target list - TargetListType targetList({}); - - // Do not expect the target list construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::TargetException& e) - { - // Expect a target exception to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TYPED_TEST(TargetListFixture, CreateTargetListWithDuplicateDescriptor_ExpectTargetException) - { - try - { - // Given a set of target descriptors containing a single duplicate - AZStd::vector descriptors; - descriptors.reserve(m_numTargets); - for (size_t i = 0; i < m_numTargets; i++) - { - // Wrap the last index round to repeat the first index - descriptors.push_back(GenerateTargetDescriptor(i % (m_numTargets - 1))); - } - - // When constructing the target list containing the duplicate target descriptor - TargetListType targetList(AZStd::move(descriptors)); - - // Do not expect the target list construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::TargetException& e) - { - // Expect a target exception to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TYPED_TEST(TargetListFixture, CreateTargetListWithValidDescriptors_ExpectValidTargetList) - { - // Given a valid set of target descriptor - AZStd::vector descriptors; - descriptors.reserve(m_numTargets); - for (size_t i = 0; i < m_numTargets; i++) - { - descriptors.push_back(GenerateTargetDescriptor(i)); - } - - // When constructing the target list containing the valid target descriptors - TargetListType targetList(AZStd::move(descriptors)); - - // Expect the number of targets in the list to match the number of target descriptors used to construct the list - EXPECT_EQ(targetList.GetNumTargets(), m_numTargets); - - for (size_t i = 0; i < targetList.GetNumTargets(); i++) - { - // Expect the target to match the procedurally generated target descriptor - ValidateTarget(targetList.GetTargets()[i], i); - - // Expect the target obtained by name to match the procedurally generated target descriptor - auto target = targetList.GetTarget(GenerateBuildTargetName(i)); - EXPECT_TRUE(target); - EXPECT_EQ(target->GetName(), GenerateBuildTargetName(i)); - } - } - - TYPED_TEST(TargetListFixture, FindNonExistantTargets_ExpectEmptyResults) - { - // Given a valid set of target descriptor - AZStd::vector descriptors; - descriptors.reserve(m_numTargets); - for (size_t i = 0; i < m_numTargets; i++) - { - descriptors.push_back(GenerateTargetDescriptor(i)); - } - - // When constructing the target list containing the valid target descriptors - TargetListType targetList(AZStd::move(descriptors)); - - // Expect the number of targets in the list to match the number of target descriptors used to construct the list - EXPECT_EQ(targetList.GetNumTargets(), m_numTargets); - - for (size_t i = 0; i < targetList.GetNumTargets(); i++) - { - // When attempting to find a target that does not exist - auto target = targetList.GetTarget(GenerateBuildTargetName(i + targetList.GetNumTargets())); - - // Expect an empty result - EXPECT_FALSE(target); - } - } - - TYPED_TEST(TargetListFixture, FindNonExistantTargetsAndThrow_ExpectTargetExceptionss) - { - // Given a valid set of target descriptor - AZStd::vector descriptors; - descriptors.reserve(m_numTargets); - for (size_t i = 0; i < m_numTargets; i++) - { - descriptors.push_back(GenerateTargetDescriptor(i)); - } - - // When constructing the target list containing the valid target descriptors - TargetListType targetList(AZStd::move(descriptors)); - - // Expect the number of targets in the list to match the number of target descriptors used to construct the list - EXPECT_EQ(targetList.GetNumTargets(), m_numTargets); - - for (size_t i = 0; i < targetList.GetNumTargets(); i++) - { - try - { - // When attempting to find a target that does not exist - targetList.GetTargetOrThrow(GenerateBuildTargetName(i + targetList.GetNumTargets())); - - // Do not expect the target list construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::TargetException& e) - { - // Expect a target exception to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactExceptionTest.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactExceptionTest.cpp deleted file mode 100644 index bf2ccb6bc7..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactExceptionTest.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include -#include - -namespace UnitTest -{ - constexpr const char* Message = "String Constructor"; - - TEST(ExceptionTest, DefaultConstructor_HasEmptyMessageString) - { - // Given an exception instantiated with the default constructor - const TestImpact::Exception e; - - // Expect the message to be an empty string - EXPECT_STREQ(e.what(), "\0"); - } - - TEST(ExceptionTest, StringConstructor_HasSpecifiedMessageString) - { - // Given an exception instantiated with a string - const AZStd::string msg(Message); - const TestImpact::Exception e(msg); - - // Expect the message to be the specified string - EXPECT_STREQ(e.what(), Message); - } - - TEST(ExceptionTest, StringLiteralConstructor_HasSpecifiedMessageString) - { - // Given an exception instantiated with a string literal - const TestImpact::Exception e(Message); - - // Expect the message to be the specified string - EXPECT_STREQ(e.what(), Message); - } - - TEST(ExceptionTest, InitializedWithLocalString_HasCopyOfLocalStringString) - { - try - { - // Given an exception instantiated with a string in local scope - throw(TestImpact::Exception(AZStd::string::format("%s", Message))); - - // Do not expect this code to be reachable - FAIL(); - } - catch (const TestImpact::Exception& e) - { - // Expect the message to be the specified out-of-scope string - EXPECT_STREQ(e.what(), Message); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(ExceptionTest, InitializedWithLocalStringLiteral_HasCopyOfLocalStringString) - { - try - { - // Given an exception instantiated with a copy of the message string in local scope - throw(TestImpact::Exception(AZStd::string::format("%s", Message).c_str())); - - // Do not expect this code to be reachable - FAIL(); - } - catch (const TestImpact::Exception& e) - { - // Expect the message to be the specified out-of-scope string literal - EXPECT_STREQ(e.what(), Message); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(ExceptionTest, EvalMacroFails_ExceptionTestThrown) - { - try - { - // Given an exception instantiated with a string literal in local scope - AZ_TestImpact_Eval(false, TestImpact::Exception, Message); - - // Do not expect this code to be reachable - FAIL(); - } - catch (const TestImpact::Exception& e) - { - // Expect the message contain the specified string - EXPECT_STREQ(e.what(), Message); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST(ExceptionTest, EvalMacroSucceeds_ExceptionTestNotThrown) - { - try - { - // Given an exception instantiated with a string literal in local scope - AZ_TestImpact_Eval(true, TestImpact::Exception, Message); - - // Expect this code to be reachable - SUCCEED(); - } - catch (...) - { - // Do not expect any exceptions - FAIL(); - } - } - - TEST(ExceptionTest, Throw_ExceptionTestThrown) - { - try - { - // Given an exception instantiated with a string literal in local scope - throw(TestImpact::Exception(Message)); - } - catch (const TestImpact::Exception& e) - { - // Expect the message contain the specified string - EXPECT_STREQ(e.what(), Message); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestJobRunnerCommon.h b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestJobRunnerCommon.h deleted file mode 100644 index 9d5ddd6c8c..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestJobRunnerCommon.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#pragma once - -#include - -#include -#include - -#include -#include -#include - -template -constexpr bool IsFlagSet(Flags flags, Flags flag) -{ - return TestImpact::Bitwise::IsFlagSet(flags, flag); -} - -namespace UnitTest -{ - // Named constants for array of targets lookup - enum - { - TestTargetA, - TestTargetB, - TestTargetC, - TestTargetD - }; - - // Named constants for max concurrency values - enum - { - OneConcurrentProcess = 1, - FourConcurrentProcesses = 4 - }; - - // Validates that the specified job was executed and returned successfully - template - void ValidateJobExecutedSuccessfully(const Job& job) - { - EXPECT_EQ(job.GetResult(), TestImpact::JobResult::ExecutedWithSuccess); - EXPECT_TRUE(job.GetDuration() > AZStd::chrono::milliseconds(0)); - EXPECT_TRUE(job.GetReturnCode().has_value()); - EXPECT_EQ(job.GetReturnCode(), 0); - EXPECT_TRUE(job.GetPayload().has_value()); - } - - // Validates that the specified job has not been executed - template - void ValidateJobNotExecuted(const Job& job) - { - EXPECT_EQ(job.GetResult(), TestImpact::JobResult::NotExecuted); - EXPECT_EQ(job.GetStartTime(), AZStd::chrono::high_resolution_clock::time_point()); - EXPECT_EQ(job.GetEndTime(), AZStd::chrono::high_resolution_clock::time_point()); - EXPECT_EQ(job.GetDuration(), AZStd::chrono::milliseconds(0)); - EXPECT_FALSE(job.GetReturnCode().has_value()); - EXPECT_FALSE(job.GetPayload().has_value()); - } - - // Validates that the specified job failed to execute - template - void ValidateJobFailedToExecute(const Job& job) - { - EXPECT_EQ(job.GetResult(), TestImpact::JobResult::FailedToExecute); - EXPECT_EQ(job.GetStartTime(), AZStd::chrono::high_resolution_clock::time_point()); - EXPECT_EQ(job.GetEndTime(), AZStd::chrono::high_resolution_clock::time_point()); - EXPECT_EQ(job.GetDuration(), AZStd::chrono::milliseconds(0)); - EXPECT_FALSE(job.GetReturnCode().has_value()); - EXPECT_FALSE(job.GetPayload().has_value()); - } - - // Validates that the specified job executed but returned with error - template - void ValidateJobExecutedWithFailure(const Job& job) - { - EXPECT_EQ(job.GetResult(), TestImpact::JobResult::ExecutedWithFailure); - EXPECT_TRUE(job.GetDuration() > AZStd::chrono::milliseconds(0)); - EXPECT_TRUE(job.GetReturnCode().has_value()); - EXPECT_GT(job.GetReturnCode().value(), 0); - EXPECT_FALSE(job.GetPayload().has_value()); - } - - // Validates that the specified job was executed but was terminated by the job runner - template - void ValidateJobTimeout(const Job& job) - { - EXPECT_EQ(job.GetResult(), TestImpact::JobResult::Terminated); - EXPECT_TRUE(job.GetDuration() > AZStd::chrono::milliseconds(0)); - EXPECT_TRUE(job.GetReturnCode().has_value()); - EXPECT_EQ(job.GetReturnCode().value(), TestImpact::ProcessTimeoutErrorCode); - EXPECT_FALSE(job.GetPayload().has_value()); - } - - // Validates that the specified job executed but returned with error - template - void ValidateJobExecutedWithFailedTests(const Job& job) - { - EXPECT_EQ(job.GetResult(), TestImpact::JobResult::ExecutedWithFailure); - EXPECT_TRUE(job.GetDuration() > AZStd::chrono::milliseconds(0)); - EXPECT_TRUE(job.GetReturnCode().has_value()); - EXPECT_GT(job.GetReturnCode().value(), 0); - EXPECT_TRUE(job.GetPayload().has_value()); - } - - // Validates whether a test run completed (passed/failed) - template - void ValidateTestRunCompleted(const Job& job, TestImpact::TestRunResult result) - { - if (result == TestImpact::TestRunResult::Passed) - { - ValidateJobExecutedSuccessfully(job); - } - else - { - ValidateJobExecutedWithFailedTests(job); - } - } - - // Validates that the specified test run matches the expected output - inline void ValidateTestTargetRun(const TestImpact::TestRun& actualResult, const TestImpact::TestRun& expectedResult) - { - EXPECT_TRUE(CheckTestRunsAreEqualIgnoreDurations(actualResult, expectedResult)); - EXPECT_EQ(actualResult.GetNumTestSuites(), CalculateNumTestSuites(expectedResult.GetTestSuites())); - EXPECT_EQ(actualResult.GetNumTests(), CalculateNumTests(expectedResult.GetTestSuites())); - EXPECT_EQ(actualResult.GetNumEnabledTests(), CalculateNumEnabledTests(expectedResult.GetTestSuites())); - EXPECT_EQ(actualResult.GetNumDisabledTests(), CalculateNumDisabledTests(expectedResult.GetTestSuites())); - EXPECT_GT(actualResult.GetDuration(), AZStd::chrono::milliseconds{0}); - EXPECT_EQ(actualResult.GetNumPasses(), CalculateNumPassedTests(expectedResult.GetTestSuites())); - EXPECT_EQ(actualResult.GetNumFailures(), CalculateNumFailedTests(expectedResult.GetTestSuites())); - EXPECT_EQ(actualResult.GetNumRuns(), CalculateNumRunTests(expectedResult.GetTestSuites())); - EXPECT_EQ(actualResult.GetNumNotRuns(), CalculateNumNotRunTests(expectedResult.GetTestSuites())); - } - - // Delete any existing data in the test run folder as not to pollute tests with data from previous test runs - // Note: the file IO operations of this fixture means it cannot be sharded by the test sharder due to file race conditions - inline void DeleteFiles(const AZStd::string& path, const AZStd::string& pattern) - { - AZ::IO::SystemFile::FindFiles(AZStd::string::format("%s/%s", path.c_str(), pattern.c_str()).c_str(), [&path](const char* file, bool isFile) - { - if (isFile) - { - AZ::IO::SystemFile::Delete(AZStd::string::format("%s/%s", path.c_str(), file).c_str()); - } - - return true; - }); - }; -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.cpp deleted file mode 100644 index 6b2fa72310..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.cpp +++ /dev/null @@ -1,1843 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include - -#include -#include - -namespace UnitTest -{ - void WriteTextToFile(const AZStd::string& text, const AZ::IO::Path& path) - { - const AZStd::vector textBytes(text.begin(), text.end()); - AZ::IO::SystemFile textFile; - AZ_TestImpact_Eval( - textFile.Open( - path.c_str(), - AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY), - TestImpact::Exception, "Couldn't open cache file for writing"); - const auto bytesWritten = textFile.Write(textBytes.data(), textBytes.size()); - AZ_TestImpact_Eval(bytesWritten == textBytes.size(), TestImpact::Exception, "Couldn't write text file data"); - } - - AZStd::string ConstructTestProcessArgs(TestImpact::ProcessId pid, AZStd::chrono::milliseconds sleepTime) - { - return AZStd::string::format("--id %i --sleep %u", pid, sleepTime.count()); - } - - AZStd::string ConstructTestProcessArgsLargeText(TestImpact::ProcessId pid, AZStd::chrono::milliseconds sleepTime) - { - return ConstructTestProcessArgs(pid, sleepTime) + " large"; - } - - AZStd::string KnownTestProcessOutputString(TestImpact::ProcessId pid) - { - return AZStd::string("TestProcessMainStdOut" + AZStd::to_string(pid)); - } - - AZStd::string KnownTestProcessErrorString(TestImpact::ProcessId pid) - { - return AZStd::string("TestProcessMainStdErr" + AZStd::to_string(pid)); - } - - AZStd::string GenerateSuiteOrFixtureName(const AZStd::string& name) - { - return name; - } - - AZStd::string GenerateTypedFixtureName(const AZStd::string& name, size_t typeNum) - { - return AZStd::string::format("%s/%u", name.c_str(), typeNum); - } - - AZStd::string GenerateParameterizedFixtureName(const AZStd::string& name, const AZStd::optional& prefix) - { - if (prefix.has_value()) - { - return AZStd::string::format("%s/%s", prefix->c_str(), name.c_str()); - } - - return AZStd::string::format("%s", name.c_str()); - } - - AZStd::string GenerateParameterizedTestName(const AZStd::string& name, size_t testNum) - { - return AZStd::string::format("%s/%u", name.c_str(), testNum); - } - - AZStd::string StringVectorToJSONElements(const AZStd::vector strings) - { - AZStd::string output; - - for (size_t i = 0, last = strings.size() - 1; i < strings.size(); i++) - { - output += AZStd::string::format("\"%s\"", strings[i].c_str()); - - if (i != last) - { - output += ","; - } - - output += "\n"; - } - - return output; - } - - AZStd::string GenerateBuildTargetDescriptorString( - const AZStd::string& name, - const AZStd::string& outputName, - const AZStd::string& path, - const AZStd::vector& staticSources, - const AZStd::vector& autogenInputs, - const AZStd::vector& autogenOutputs) - { - constexpr const char* const targetTemplate = - "{\n" - " \"sources\": {\n" - " \"input\": [\n" - "%s\n" - " ],\n" - " \"output\": [\n" - "%s\n" - " ],\n" - " \"static\": [\n" - "%s\n" - " ]\n" - " },\n" - " \"target\": {\n" - " \"name\": \"%s\",\n" - " \"output_name\": \"%s\",\n" - " \"path\": \"%s\"\n" - " }\n" - "}\n" - "\n"; - - AZStd::string output = AZStd::string::format(targetTemplate, - StringVectorToJSONElements(autogenInputs).c_str(), - StringVectorToJSONElements(autogenOutputs).c_str(), - StringVectorToJSONElements(staticSources).c_str(), - name.c_str(), - outputName.c_str(), - path.c_str()); - - return output; - } - - TestImpact::BuildTargetDescriptor GenerateBuildTargetDescriptor( - const AZStd::string& name, - const AZStd::string& outputName, - const AZStd::string& path, - const AZStd::vector& staticSources, - const TestImpact::AutogenSources& autogenSources) - { - TestImpact::BuildTargetDescriptor targetDescriptor; - targetDescriptor.m_buildMetaData = {name, outputName, path}; - targetDescriptor.m_sources = {staticSources, autogenSources}; - return targetDescriptor; - } - - TestImpact::TestEnumerationSuite GenerateParamterizedSuite( - const AZStd::pair& fixture, - const AZStd::optional& permutation, - const AZStd::vector> tests, - size_t permutationCount) - { - TestImpact::TestEnumerationSuite suite = - TestImpact::TestEnumerationSuite{GenerateParameterizedFixtureName(fixture.first, permutation), fixture.second, {}}; - - for (const auto& test : tests) - { - for (auto i = 0; i < permutationCount; i++) - { - suite.m_tests.push_back(TestImpact::TestEnumerationCase{GenerateParameterizedTestName(test.first, i), test.second}); - } - } - - return suite; - } - - void GenerateTypedSuite( - const AZStd::pair& fixture, - const AZStd::vector> tests, - size_t permutationCount, - AZStd::vector& parentSuiteList) - { - for (auto i = 0; i < permutationCount; i++) - { - parentSuiteList.push_back(TestImpact::TestEnumerationSuite{GenerateTypedFixtureName(fixture.first, i), fixture.second, {}}); - - for (const auto& test : tests) - { - parentSuiteList.back().m_tests.push_back(TestImpact::TestEnumerationCase{test.first, test.second}); - } - } - } - - size_t CalculateNumPassedTests(const AZStd::vector& suites) - { - size_t numPassedTests = 0; - for (const auto& suite : suites) - { - numPassedTests += AZStd::count_if(suite.m_tests.begin(), suite.m_tests.end(), [](const auto& test) { - return test.m_result.has_value() && test.m_result.value() == TestImpact::TestRunResult::Passed; - }); - } - - return numPassedTests; - } - - size_t CalculateNumFailedTests(const AZStd::vector& suites) - { - size_t numFailedTests = 0; - for (const auto& suite : suites) - { - for (const auto& test : suite.m_tests) - { - if (test.m_result.has_value() && test.m_result.value() == TestImpact::TestRunResult::Failed) - { - numFailedTests++; - } - } - } - - return numFailedTests; - } - - size_t CalculateNumRunTests(const AZStd::vector& suites) - { - size_t numRunTests = 0; - for (const auto& suite : suites) - { - for (const auto& test : suite.m_tests) - { - if (test.m_status == TestImpact::TestRunStatus::Run) - { - numRunTests++; - } - } - } - - return numRunTests; - } - - size_t CalculateNumNotRunTests(const AZStd::vector& suites) - { - size_t numNotRunTests = 0; - for (const auto& suite : suites) - { - for (const auto& test : suite.m_tests) - { - if (test.m_status == TestImpact::TestRunStatus::NotRun) - { - numNotRunTests++; - } - } - } - - return numNotRunTests; - } - - AZStd::vector GetTestTargetATestEnumerationSuites() - { - AZStd::vector suites; - - suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestCase"), true, {}}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test2_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test3_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test4_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test5_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test6_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test7_WillFail", true}); - - suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestFixture"), true, {}}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test2_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test3_WillPass", true}); - - return suites; - } - - AZStd::vector GetTestTargetBTestEnumerationSuites() - { - AZStd::vector suites; - - suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestCase"), true, {}}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test2_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test3_WillPass", true}); - - suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestFixture"), true, {}}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); - - const size_t numParams = 27; - suites.push_back(GenerateParamterizedSuite( - {"TestFixtureWithParams", true}, "PermutationA", {{"Test1_WillPass", true}, {"Test2_WillPass", true}}, numParams)); - suites.push_back(GenerateParamterizedSuite( - {"TestFixtureWithParams", true}, AZStd::nullopt, {{"Test1_WillPass", true}, {"Test2_WillPass", true}}, numParams)); - - return suites; - } - - AZStd::vector GetTestTargetCTestEnumerationSuites() - { - AZStd::vector suites; - - suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestFixture"), true, {}}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test2_WillPass", true}); - - const size_t numTypes = 4; - for (auto i = 0; i < numTypes; i++) - { - suites.push_back(TestImpact::TestEnumerationSuite{GenerateTypedFixtureName("TestFixtureWithTypes", i), true, {}}); - for (auto j = 1; j < numTypes + 1; j++) - { - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{AZStd::string::format("Test%u_WillPass", j), true}); - } - } - - return suites; - } - - AZStd::vector GetTestTargetDTestEnumerationSuites() - { - AZStd::vector suites; - - suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestCase"), true, {}}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"DISABLED_Test2_WillPass", false}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test3_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test4_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test5_WillPass", true}); - - suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("TestFixture1"), true, {}}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test2_WillPass", true}); - - suites.push_back(TestImpact::TestEnumerationSuite{GenerateSuiteOrFixtureName("DISABLED_TestFixture2"), false, {}}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test1_WillPass", true}); - suites.back().m_tests.push_back(TestImpact::TestEnumerationCase{"Test2_WillPass", true}); - - const size_t numTypes = 4; - GenerateTypedSuite( - {"TestFixtureWithTypes1", true}, {{"Test1_WillPass", true}, {"DISABLED_Test2_WillPass", false}, {"Test3_WillPass", true}}, - numTypes, suites); - - GenerateTypedSuite( - {"DISABLED_TestFixtureWithTypes2", false}, - {{"Test1_WillPass", true}, {"DISABLED_Test2_WillPass", false}, {"Test3_WillPass", true}}, numTypes, suites); - - const size_t numParams = 27; - suites.push_back(GenerateParamterizedSuite( - {"TestFixtureWithParams1", true}, "PermutationA", {{"Test1_WillPass", true}, {"DISABLED_Test2_WillPass", false}}, numParams)); - suites.push_back(GenerateParamterizedSuite( - {"TestFixtureWithParams1", true}, AZStd::nullopt, {{"Test1_WillPass", true}, {"DISABLED_Test2_WillPass", false}}, numParams)); - suites.push_back(GenerateParamterizedSuite( - {"DISABLED_TestFixtureWithParams2", false}, "PermutationA", {{"Test1_WillPass", true}, {"DISABLED_Test2_WillPass", false}}, - numParams)); - suites.push_back(GenerateParamterizedSuite( - {"DISABLED_TestFixtureWithParams2", false}, AZStd::nullopt, {{"Test1_WillPass", true}, {"DISABLED_Test2_WillPass", false}}, - numParams)); - - return suites; - } - - TestImpact::TestRunCase TestRunCaseFromTestEnumerationCase(const TestImpact::TestEnumerationCase& enumCase) - { - TestImpact::TestRunCase runCase; - runCase.m_name = enumCase.m_name; - runCase.m_enabled = enumCase.m_enabled; - return runCase; - } - - TestImpact::TestRunSuite TestRunSuiteFromTestEnumerationSuite(const TestImpact::TestEnumerationSuite& enumSuite) - { - TestImpact::TestRunSuite runSuite; - runSuite.m_name = enumSuite.m_name; - runSuite.m_enabled = enumSuite.m_enabled; - - for (const auto& enumCase : enumSuite.m_tests) - { - runSuite.m_tests.push_back(TestRunCaseFromTestEnumerationCase(enumCase)); - } - - return runSuite; - } - - AZStd::vector TestRunSuitesFromTestEnumerationSuites( - const AZStd::vector& enumSuites) - { - AZStd::vector runSuites; - runSuites.reserve(enumSuites.size()); - - for (const auto& enumSuite : enumSuites) - { - runSuites.push_back(TestRunSuiteFromTestEnumerationSuite(enumSuite)); - } - - return runSuites; - } - - void SetTestRunSuiteData(TestImpact::TestRunSuite& testSuite, AZStd::chrono::milliseconds duration) - { - testSuite.m_duration = duration; - } - - void SetTestRunCaseData( - TestImpact::TestRunCase& testCase, AZStd::chrono::milliseconds duration, TestImpact::TestRunStatus status, - AZStd::optional result = AZStd::nullopt) - { - testCase.m_duration = duration; - testCase.m_status = status; - testCase.m_result = result; - } - - AZStd::vector GetTestTargetATestRunSuites() - { - AZStd::vector suites(TestRunSuitesFromTestEnumerationSuites(GetTestTargetATestEnumerationSuites())); - - enum - { - TestCaseIndex = 0, - TestFixtureIndex - }; - - { - auto& suite = suites[TestCaseIndex]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{3}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[4], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[5], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[6], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Failed); - } - { - auto& suite = suites[TestFixtureIndex]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{38}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{4}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - - return suites; - } - - AZStd::vector GetTestTargetBTestRunSuites() - { - AZStd::vector suites(TestRunSuitesFromTestEnumerationSuites(GetTestTargetBTestEnumerationSuites())); - - enum - { - TestCaseIndex = 0, - TestFixtureIndex, - PermutationATestFixtureWithParamsIndex, - TestFixtureWithParamsIndex - }; - - { - auto& suite = suites[TestCaseIndex]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{202}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{3}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[TestFixtureIndex]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{62}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{5}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[PermutationATestFixtureWithParamsIndex]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{3203}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[4], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[5], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[6], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[7], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[8], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[9], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[10], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[11], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[12], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[13], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[14], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[15], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[16], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[17], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[18], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[19], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[20], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[21], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[22], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[23], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[24], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[25], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[26], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[27], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[28], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[29], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[30], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[31], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[32], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[33], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[34], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[35], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[36], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[37], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[38], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[39], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[40], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[41], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[42], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[43], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[44], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[45], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[46], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[47], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[48], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[49], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[50], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[51], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[52], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[53], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[TestFixtureWithParamsIndex]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{3360}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[4], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[5], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[6], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[7], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[8], AZStd::chrono::milliseconds{2}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[9], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[10], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[11], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[12], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[13], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[14], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[15], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[16], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[17], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[18], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[19], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[20], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[21], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[22], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[23], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[24], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[25], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[26], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[27], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[28], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[29], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[30], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[31], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[32], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[33], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[34], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[35], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[36], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[37], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[38], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[39], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[40], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[41], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[42], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[43], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[44], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[45], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[46], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[47], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[48], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[49], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[50], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[51], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[52], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[53], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - - return suites; - } - - AZStd::vector GetTestTargetCTestRunSuites() - { - AZStd::vector suites(TestRunSuitesFromTestEnumerationSuites(GetTestTargetCTestEnumerationSuites())); - - enum - { - TestCaseIndex = 0, - TestFixtureWithTypes0Index, - TestFixtureWithTypes1Index, - TestFixtureWithTypes2Index, - TestFixtureWithTypes3Index - }; - - { - auto& suite = suites[TestCaseIndex]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{125}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{4}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[TestFixtureWithTypes0Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{210}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[3], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[TestFixtureWithTypes1Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{208}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[TestFixtureWithTypes2Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{199}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[TestFixtureWithTypes3Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{49}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - - return suites; - } - - AZStd::vector GetTestTargetDTestRunSuites() - { - AZStd::vector suites(TestRunSuitesFromTestEnumerationSuites(GetTestTargetDTestEnumerationSuites())); - - enum - { - TestCaseIndex = 0, - TestFixture1Index, - DISABLEDTestFixture2Index, - TestFixtureWithTypes10Index, - TestFixtureWithTypes11Index, - TestFixtureWithTypes12Index, - TestFixtureWithTypes13Index, - DISABLEDTestFixtureWithTypes20Index, - DISABLEDTestFixtureWithTypes21Index, - DISABLEDTestFixtureWithTypes22Index, - DISABLEDTestFixtureWithTypes23Index, - PermutationATestFixtureWithParams1Index, - TestFixtureWithParams1Index, - PermutationADISABLED_TestFixtureWithParams2Index, - DISABLED_TestFixtureWithParams2Index, - }; - - { - auto& suite = suites[TestCaseIndex]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{3}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[4], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[TestFixture1Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{4}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{2}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[DISABLEDTestFixture2Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{0}); - SetTestRunCaseData(suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - } - { - auto& suite = suites[TestFixtureWithTypes10Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{1}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[TestFixtureWithTypes11Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{3}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[TestFixtureWithTypes12Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{0}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[TestFixtureWithTypes13Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{1}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - } - { - auto& suite = suites[DISABLEDTestFixtureWithTypes20Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{0}); - SetTestRunCaseData(suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - } - { - auto& suite = suites[DISABLEDTestFixtureWithTypes21Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{0}); - SetTestRunCaseData(suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - } - { - auto& suite = suites[DISABLEDTestFixtureWithTypes22Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{0}); - SetTestRunCaseData(suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - } - { - auto& suite = suites[DISABLEDTestFixtureWithTypes23Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{0}); - SetTestRunCaseData(suite.m_tests[0], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - } - { - auto& suite = suites[PermutationATestFixtureWithParams1Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{173}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[4], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[5], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[6], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[7], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[8], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[9], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[10], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[11], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[12], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[13], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[14], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[15], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[16], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[17], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[18], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[19], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[20], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[21], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[22], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[23], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[24], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[25], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[26], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData(suite.m_tests[27], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[28], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[29], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[30], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[31], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[32], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[33], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[34], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[35], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[36], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[37], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[38], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[39], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[40], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[41], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[42], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[43], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[44], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[45], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[46], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[47], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[48], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[49], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[50], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[51], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[52], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[53], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - } - { - auto& suite = suites[TestFixtureWithParams1Index]; - SetTestRunSuiteData(suite, AZStd::chrono::milliseconds{102}); - SetTestRunCaseData( - suite.m_tests[0], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[1], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[2], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[3], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[4], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[5], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[6], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[7], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[8], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[9], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[10], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[11], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[12], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[13], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[14], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[15], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[16], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[17], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[18], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[19], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[20], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[21], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[22], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[23], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[24], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[25], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData( - suite.m_tests[26], AZStd::chrono::milliseconds{1}, TestImpact::TestRunStatus::Run, TestImpact::TestRunResult::Passed); - SetTestRunCaseData(suite.m_tests[27], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[28], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[29], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[30], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[31], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[32], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[33], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[34], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[35], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[36], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[37], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[38], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[39], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[40], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[41], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[42], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[43], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[44], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[45], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[46], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[47], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[48], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[49], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[50], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[51], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[52], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - SetTestRunCaseData(suite.m_tests[53], AZStd::chrono::milliseconds{0}, TestImpact::TestRunStatus::NotRun); - } - // The other two fixtures are all disabled (not run) - - return suites; - } - - AZStd::vector GetTestTargetASourceModuleCoverages() - { - AZStd::vector moduleCoverages; - - TestImpact::ModuleCoverage moduleCoverage; - moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetA.Tests.dll"; - - TestImpact::SourceCoverage sourceCoverage; - sourceCoverage.m_path = - "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetA\\Code\\Tests\\TestImpactTestTargetA.cpp"; - - moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); - moduleCoverages.push_back(AZStd::move(moduleCoverage)); - return moduleCoverages; - } - - AZStd::vector GetTestTargetBSourceModuleCoverages() - { - AZStd::vector moduleCoverages; - - TestImpact::ModuleCoverage moduleCoverage; - moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetB.Tests.dll"; - - TestImpact::SourceCoverage sourceCoverage; - sourceCoverage.m_path = - "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetB\\Code\\Tests\\TestImpactTestTargetB.cpp"; - - moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); - moduleCoverages.push_back(AZStd::move(moduleCoverage)); - return moduleCoverages; - } - - AZStd::vector GetTestTargetCSourceModuleCoverages() - { - AZStd::vector moduleCoverages; - - TestImpact::ModuleCoverage moduleCoverage; - moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetC.Tests.dll"; - - TestImpact::SourceCoverage sourceCoverage; - sourceCoverage.m_path = - "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp"; - - moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); - moduleCoverages.push_back(AZStd::move(moduleCoverage)); - return moduleCoverages; - } - - AZStd::vector GetTestTargetDSourceModuleCoverages() - { - AZStd::vector moduleCoverages; - - TestImpact::ModuleCoverage moduleCoverage; - moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetD.Tests.dll"; - - TestImpact::SourceCoverage sourceCoverage; - sourceCoverage.m_path = - "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp"; - - moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); - moduleCoverages.push_back(AZStd::move(moduleCoverage)); - return moduleCoverages; - } - - AZStd::vector GetTestTargetALineModuleCoverages() - { - AZStd::vector moduleCoverages; - - TestImpact::ModuleCoverage moduleCoverage; - moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetA.Tests.dll"; - - TestImpact::SourceCoverage sourceCoverage; - sourceCoverage.m_path = - "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetA\\Code\\Tests\\TestImpactTestTargetA.cpp"; - sourceCoverage.m_coverage = AZStd::vector(); - - auto& lines = sourceCoverage.m_coverage; - lines.push_back(TestImpact::LineCoverage{22, 1}); - lines.push_back(TestImpact::LineCoverage{23, 1}); - lines.push_back(TestImpact::LineCoverage{24, 1}); - lines.push_back(TestImpact::LineCoverage{25, 1}); - lines.push_back(TestImpact::LineCoverage{27, 1}); - lines.push_back(TestImpact::LineCoverage{28, 1}); - lines.push_back(TestImpact::LineCoverage{29, 1}); - lines.push_back(TestImpact::LineCoverage{30, 1}); - lines.push_back(TestImpact::LineCoverage{32, 1}); - lines.push_back(TestImpact::LineCoverage{33, 1}); - lines.push_back(TestImpact::LineCoverage{34, 1}); - lines.push_back(TestImpact::LineCoverage{35, 1}); - lines.push_back(TestImpact::LineCoverage{37, 1}); - lines.push_back(TestImpact::LineCoverage{38, 1}); - lines.push_back(TestImpact::LineCoverage{39, 1}); - lines.push_back(TestImpact::LineCoverage{40, 1}); - lines.push_back(TestImpact::LineCoverage{42, 1}); - lines.push_back(TestImpact::LineCoverage{43, 1}); - lines.push_back(TestImpact::LineCoverage{44, 1}); - lines.push_back(TestImpact::LineCoverage{45, 1}); - lines.push_back(TestImpact::LineCoverage{47, 1}); - lines.push_back(TestImpact::LineCoverage{48, 1}); - lines.push_back(TestImpact::LineCoverage{49, 1}); - lines.push_back(TestImpact::LineCoverage{50, 1}); - lines.push_back(TestImpact::LineCoverage{52, 1}); - lines.push_back(TestImpact::LineCoverage{53, 1}); - lines.push_back(TestImpact::LineCoverage{54, 1}); - lines.push_back(TestImpact::LineCoverage{55, 1}); - lines.push_back(TestImpact::LineCoverage{57, 1}); - lines.push_back(TestImpact::LineCoverage{58, 1}); - lines.push_back(TestImpact::LineCoverage{59, 1}); - lines.push_back(TestImpact::LineCoverage{60, 1}); - lines.push_back(TestImpact::LineCoverage{62, 1}); - lines.push_back(TestImpact::LineCoverage{63, 1}); - lines.push_back(TestImpact::LineCoverage{64, 1}); - lines.push_back(TestImpact::LineCoverage{65, 1}); - lines.push_back(TestImpact::LineCoverage{67, 1}); - lines.push_back(TestImpact::LineCoverage{68, 1}); - lines.push_back(TestImpact::LineCoverage{69, 1}); - lines.push_back(TestImpact::LineCoverage{70, 1}); - lines.push_back(TestImpact::LineCoverage{73, 1}); - - moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); - moduleCoverages.push_back(AZStd::move(moduleCoverage)); - return moduleCoverages; - } - - AZStd::vector GetTestTargetBLineModuleCoverages() - { - AZStd::vector moduleCoverages; - - TestImpact::ModuleCoverage moduleCoverage; - moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetB.Tests.dll"; - - TestImpact::SourceCoverage sourceCoverage; - sourceCoverage.m_path = - "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetB\\Code\\Tests\\TestImpactTestTargetB.cpp"; - sourceCoverage.m_coverage = AZStd::vector(); - - auto& lines = sourceCoverage.m_coverage; - lines.push_back(TestImpact::LineCoverage{29, 1}); - lines.push_back(TestImpact::LineCoverage{30, 1}); - lines.push_back(TestImpact::LineCoverage{31, 1}); - lines.push_back(TestImpact::LineCoverage{32, 1}); - lines.push_back(TestImpact::LineCoverage{34, 1}); - lines.push_back(TestImpact::LineCoverage{35, 1}); - lines.push_back(TestImpact::LineCoverage{36, 1}); - lines.push_back(TestImpact::LineCoverage{37, 1}); - lines.push_back(TestImpact::LineCoverage{39, 1}); - lines.push_back(TestImpact::LineCoverage{40, 1}); - lines.push_back(TestImpact::LineCoverage{41, 1}); - lines.push_back(TestImpact::LineCoverage{42, 1}); - lines.push_back(TestImpact::LineCoverage{44, 1}); - lines.push_back(TestImpact::LineCoverage{45, 1}); - lines.push_back(TestImpact::LineCoverage{46, 1}); - lines.push_back(TestImpact::LineCoverage{47, 1}); - lines.push_back(TestImpact::LineCoverage{49, 1}); - lines.push_back(TestImpact::LineCoverage{50, 1}); - lines.push_back(TestImpact::LineCoverage{51, 1}); - lines.push_back(TestImpact::LineCoverage{52, 1}); - lines.push_back(TestImpact::LineCoverage{54, 1}); - lines.push_back(TestImpact::LineCoverage{55, 1}); - lines.push_back(TestImpact::LineCoverage{56, 1}); - lines.push_back(TestImpact::LineCoverage{57, 1}); - lines.push_back(TestImpact::LineCoverage{59, 1}); - lines.push_back(TestImpact::LineCoverage{66, 1}); - lines.push_back(TestImpact::LineCoverage{68, 1}); - lines.push_back(TestImpact::LineCoverage{75, 1}); - lines.push_back(TestImpact::LineCoverage{78, 1}); - - moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); - moduleCoverages.push_back(AZStd::move(moduleCoverage)); - return moduleCoverages; - } - - AZStd::vector GetTestTargetCLineModuleCoverages() - { - AZStd::vector moduleCoverages; - - TestImpact::ModuleCoverage moduleCoverage; - moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetC.Tests.dll"; - - TestImpact::SourceCoverage sourceCoverage; - sourceCoverage.m_path = - "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp"; - sourceCoverage.m_coverage = AZStd::vector(); - - auto& lines = sourceCoverage.m_coverage; - lines.push_back(TestImpact::LineCoverage{32, 1}); - lines.push_back(TestImpact::LineCoverage{33, 1}); - lines.push_back(TestImpact::LineCoverage{34, 1}); - lines.push_back(TestImpact::LineCoverage{35, 1}); - lines.push_back(TestImpact::LineCoverage{37, 1}); - lines.push_back(TestImpact::LineCoverage{38, 1}); - lines.push_back(TestImpact::LineCoverage{39, 1}); - lines.push_back(TestImpact::LineCoverage{40, 1}); - lines.push_back(TestImpact::LineCoverage{42, 1}); - lines.push_back(TestImpact::LineCoverage{43, 1}); - lines.push_back(TestImpact::LineCoverage{44, 1}); - lines.push_back(TestImpact::LineCoverage{45, 1}); - lines.push_back(TestImpact::LineCoverage{47, 1}); - lines.push_back(TestImpact::LineCoverage{48, 1}); - lines.push_back(TestImpact::LineCoverage{49, 1}); - lines.push_back(TestImpact::LineCoverage{50, 1}); - lines.push_back(TestImpact::LineCoverage{52, 1}); - lines.push_back(TestImpact::LineCoverage{53, 1}); - lines.push_back(TestImpact::LineCoverage{54, 1}); - lines.push_back(TestImpact::LineCoverage{55, 1}); - lines.push_back(TestImpact::LineCoverage{57, 1}); - lines.push_back(TestImpact::LineCoverage{58, 1}); - lines.push_back(TestImpact::LineCoverage{59, 1}); - lines.push_back(TestImpact::LineCoverage{60, 1}); - lines.push_back(TestImpact::LineCoverage{63, 1}); - - moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); - moduleCoverages.push_back(AZStd::move(moduleCoverage)); - return moduleCoverages; - } - - AZStd::vector GetTestTargetDLineModuleCoverages() - { - AZStd::vector moduleCoverages; - - TestImpact::ModuleCoverage moduleCoverage; - moduleCoverage.m_path = "C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetD.Tests.dll"; - - TestImpact::SourceCoverage sourceCoverage; - sourceCoverage.m_path = - "C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp"; - sourceCoverage.m_coverage = AZStd::vector(); - - auto& lines = sourceCoverage.m_coverage; - lines.push_back(TestImpact::LineCoverage{56, 1}); - lines.push_back(TestImpact::LineCoverage{57, 1}); - lines.push_back(TestImpact::LineCoverage{58, 1}); - lines.push_back(TestImpact::LineCoverage{59, 1}); - lines.push_back(TestImpact::LineCoverage{61, 1}); - lines.push_back(TestImpact::LineCoverage{62, 0}); - lines.push_back(TestImpact::LineCoverage{63, 0}); - lines.push_back(TestImpact::LineCoverage{64, 0}); - lines.push_back(TestImpact::LineCoverage{66, 1}); - lines.push_back(TestImpact::LineCoverage{67, 1}); - lines.push_back(TestImpact::LineCoverage{68, 1}); - lines.push_back(TestImpact::LineCoverage{69, 1}); - lines.push_back(TestImpact::LineCoverage{71, 1}); - lines.push_back(TestImpact::LineCoverage{72, 1}); - lines.push_back(TestImpact::LineCoverage{73, 1}); - lines.push_back(TestImpact::LineCoverage{74, 1}); - lines.push_back(TestImpact::LineCoverage{76, 1}); - lines.push_back(TestImpact::LineCoverage{77, 1}); - lines.push_back(TestImpact::LineCoverage{78, 1}); - lines.push_back(TestImpact::LineCoverage{79, 1}); - lines.push_back(TestImpact::LineCoverage{81, 1}); - lines.push_back(TestImpact::LineCoverage{82, 1}); - lines.push_back(TestImpact::LineCoverage{83, 1}); - lines.push_back(TestImpact::LineCoverage{84, 1}); - lines.push_back(TestImpact::LineCoverage{86, 1}); - lines.push_back(TestImpact::LineCoverage{87, 1}); - lines.push_back(TestImpact::LineCoverage{88, 1}); - lines.push_back(TestImpact::LineCoverage{89, 1}); - lines.push_back(TestImpact::LineCoverage{91, 1}); - lines.push_back(TestImpact::LineCoverage{92, 0}); - lines.push_back(TestImpact::LineCoverage{93, 0}); - lines.push_back(TestImpact::LineCoverage{94, 0}); - lines.push_back(TestImpact::LineCoverage{96, 1}); - lines.push_back(TestImpact::LineCoverage{97, 0}); - lines.push_back(TestImpact::LineCoverage{98, 0}); - lines.push_back(TestImpact::LineCoverage{99, 0}); - lines.push_back(TestImpact::LineCoverage{101, 1}); - lines.push_back(TestImpact::LineCoverage{102, 1}); - lines.push_back(TestImpact::LineCoverage{103, 1}); - lines.push_back(TestImpact::LineCoverage{104, 1}); - lines.push_back(TestImpact::LineCoverage{106, 1}); - lines.push_back(TestImpact::LineCoverage{107, 0}); - lines.push_back(TestImpact::LineCoverage{108, 0}); - lines.push_back(TestImpact::LineCoverage{109, 0}); - lines.push_back(TestImpact::LineCoverage{111, 1}); - lines.push_back(TestImpact::LineCoverage{112, 0}); - lines.push_back(TestImpact::LineCoverage{113, 0}); - lines.push_back(TestImpact::LineCoverage{114, 0}); - lines.push_back(TestImpact::LineCoverage{116, 1}); - lines.push_back(TestImpact::LineCoverage{117, 0}); - lines.push_back(TestImpact::LineCoverage{118, 0}); - lines.push_back(TestImpact::LineCoverage{119, 0}); - lines.push_back(TestImpact::LineCoverage{121, 1}); - lines.push_back(TestImpact::LineCoverage{128, 1}); - lines.push_back(TestImpact::LineCoverage{130, 1}); - lines.push_back(TestImpact::LineCoverage{137, 1}); - lines.push_back(TestImpact::LineCoverage{139, 1}); - lines.push_back(TestImpact::LineCoverage{146, 1}); - lines.push_back(TestImpact::LineCoverage{148, 1}); - lines.push_back(TestImpact::LineCoverage{155, 1}); - lines.push_back(TestImpact::LineCoverage{157, 1}); - lines.push_back(TestImpact::LineCoverage{158, 1}); - lines.push_back(TestImpact::LineCoverage{159, 1}); - lines.push_back(TestImpact::LineCoverage{160, 1}); - lines.push_back(TestImpact::LineCoverage{162, 1}); - lines.push_back(TestImpact::LineCoverage{163, 0}); - lines.push_back(TestImpact::LineCoverage{164, 0}); - lines.push_back(TestImpact::LineCoverage{165, 0}); - lines.push_back(TestImpact::LineCoverage{167, 1}); - lines.push_back(TestImpact::LineCoverage{168, 1}); - lines.push_back(TestImpact::LineCoverage{169, 1}); - lines.push_back(TestImpact::LineCoverage{170, 1}); - lines.push_back(TestImpact::LineCoverage{172, 1}); - lines.push_back(TestImpact::LineCoverage{173, 0}); - lines.push_back(TestImpact::LineCoverage{174, 0}); - lines.push_back(TestImpact::LineCoverage{175, 0}); - lines.push_back(TestImpact::LineCoverage{177, 1}); - lines.push_back(TestImpact::LineCoverage{178, 0}); - lines.push_back(TestImpact::LineCoverage{179, 0}); - lines.push_back(TestImpact::LineCoverage{180, 0}); - lines.push_back(TestImpact::LineCoverage{182, 1}); - lines.push_back(TestImpact::LineCoverage{183, 0}); - lines.push_back(TestImpact::LineCoverage{184, 0}); - lines.push_back(TestImpact::LineCoverage{185, 0}); - lines.push_back(TestImpact::LineCoverage{188, 1}); - - moduleCoverage.m_sources.push_back(AZStd::move(sourceCoverage)); - moduleCoverages.push_back(AZStd::move(moduleCoverage)); - return moduleCoverages; - } - - template - bool CheckTestCasesAreEqual(const TestCase& lhs, const TestCase& rhs) - { - if (lhs.m_enabled != rhs.m_enabled) - { - AZ_Error("CheckTestCasesAreEqual", false, "lhs.m_enabled: %u, rhs.m_enabled: %u", lhs.m_enabled, rhs.m_enabled); - return false; - } - - if (lhs.m_name != rhs.m_name) - { - AZ_Error("CheckTestCasesAreEqual", false, "lhs.m_name: %s, rhs.m_name: %s", lhs.m_name.c_str(), rhs.m_name.c_str()); - return false; - } - - return true; - } - - template - bool CheckTestSuitesAreEqual(const TestSuite& lhs, const TestSuite& rhs) - { - if (lhs.m_enabled != rhs.m_enabled) - { - AZ_Error("CheckTestSuitesAreEqual", false, "lhs.m_enabled: %u, rhs.m_enabled: %u", lhs.m_enabled, rhs.m_enabled); - return false; - } - - if (lhs.m_name != rhs.m_name) - { - AZ_Error("CheckTestSuitesAreEqual", false, "lhs.m_name: %s, rhs.m_name: %s", lhs.m_name.c_str(), rhs.m_name.c_str()); - return false; - } - - return AZStd::equal(lhs.m_tests.begin(), lhs.m_tests.end(), rhs.m_tests.begin(), [](const auto& left, const auto& right) { - return left == right; - }); - } - - template - bool CheckTestSuiteVectorsAreEqual(const AZStd::vector& lhs, const AZStd::vector& rhs) - { - return AZStd::equal(lhs.begin(), lhs.end(), rhs.begin(), [](const TestSuite& left, const TestSuite& right) { - return left == right; - }); - } - - template - bool CheckTestContainersAreEqual(const TestContainer& lhs, const TestContainer& rhs) - { - if (lhs.GetTestSuites().size() != rhs.GetTestSuites().size()) - { - return false; - } - - return AZStd::equal( - lhs.GetTestSuites().begin(), lhs.GetTestSuites().end(), rhs.GetTestSuites().begin(), [](const auto& left, const auto& right) { - return left == right; - }); - } - - bool operator==(const TestImpact::TestEnumerationCase& lhs, const TestImpact::TestEnumerationCase& rhs) - { - return CheckTestCasesAreEqual(lhs, rhs); - } - - bool operator==(const TestImpact::TestRunCase& lhs, const TestImpact::TestRunCase& rhs) - { - if (!CheckTestCasesAreEqual(lhs, rhs)) - { - return false; - } - - if (lhs.m_status != rhs.m_status) - { - AZ_Error( - "TestRunCase ==", false, "lhs.m_status: %u, rhs.m_status: %u", static_cast(lhs.m_status), - static_cast(rhs.m_status)); - return false; - } - - if (lhs.m_duration != rhs.m_duration) - { - AZ_Error("TestRunCase ==", false, "lhs.m_duration: %u, rhs.m_duration: %u", lhs.m_duration.count(), rhs.m_duration.count()); - return false; - } - - if (lhs.m_result != rhs.m_result) - { - if (!lhs.m_result.has_value() && rhs.m_result.has_value()) - { - AZ_Error("TestRunCase ==", false, "lhs.m_result: null, rhs.m_result: %u", static_cast(rhs.m_result.value())); - } - else if (lhs.m_result.has_value() && !rhs.m_result.has_value()) - { - AZ_Error("TestRunCase ==", false, "lhs.m_result: %u, rhs.m_result: null", static_cast(lhs.m_result.value())); - } - else - { - AZ_Error( - "TestRunCase ==", false, "lhs.m_result: %u, rhs.m_result: %u", static_cast(lhs.m_result.value()), - static_cast(rhs.m_result.value())); - } - - return false; - } - - return true; - } - - bool operator==(const TestImpact::TestEnumerationSuite& lhs, const TestImpact::TestEnumerationSuite& rhs) - { - return CheckTestSuitesAreEqual(lhs, rhs); - } - - bool operator==(const TestImpact::TestRunSuite& lhs, const TestImpact::TestRunSuite& rhs) - { - if (!CheckTestSuitesAreEqual(lhs, rhs)) - { - return false; - } - - if (lhs.m_duration != rhs.m_duration) - { - AZ_Error( - "TestEnumerationSuite ==", false, "lhs.m_duration: %u, rhs.m_duration: %u", lhs.m_duration.count(), rhs.m_duration.count()); - return false; - } - - return true; - } - - bool operator==(const AZStd::vector& lhs, const AZStd::vector& rhs) - { - return CheckTestSuiteVectorsAreEqual(lhs, rhs); - } - - bool operator==(const AZStd::vector& lhs, const AZStd::vector& rhs) - { - return CheckTestSuiteVectorsAreEqual(lhs, rhs); - } - - bool operator==(const TestImpact::TestEnumeration& lhs, const TestImpact::TestEnumeration& rhs) - { - return CheckTestContainersAreEqual(lhs, rhs); - } - - bool operator==(const TestImpact::TestRun& lhs, const TestImpact::TestRun& rhs) - { - if (lhs.GetDuration() != rhs.GetDuration()) - { - AZ_Error("TestRun ==", false, "lhs.GetDuration(): %u, rhs.GetDuration(): %u", lhs.GetDuration(), rhs.GetDuration()); - return false; - } - - if (lhs.GetNumDisabledTests() != rhs.GetNumDisabledTests()) - { - AZ_Error( - "TestRun ==", false, "lhs.GetNumDisabledTests(): %u, rhs.GetNumDisabledTests(): %u", lhs.GetNumDisabledTests(), - rhs.GetNumDisabledTests()); - return false; - } - - if (lhs.GetNumEnabledTests() != rhs.GetNumEnabledTests()) - { - AZ_Error( - "TestRun ==", false, "lhs.GetNumEnabledTests(): %u, rhs.GetNumEnabledTests(): %u", lhs.GetNumEnabledTests(), - rhs.GetNumEnabledTests()); - return false; - } - - if (lhs.GetNumFailures() != rhs.GetNumFailures()) - { - AZ_Error("TestRun ==", false, "lhs.GetNumFailures(): %u, rhs.GetNumFailures(): %u", lhs.GetNumFailures(), rhs.GetNumFailures()); - return false; - } - - if (lhs.GetNumNotRuns() != rhs.GetNumNotRuns()) - { - AZ_Error("TestRun ==", false, "lhs.GetNumNotRuns(): %u, rhs.GetNumNotRuns(): %u", lhs.GetNumNotRuns(), rhs.GetNumNotRuns()); - return false; - } - - if (lhs.GetNumPasses() != rhs.GetNumPasses()) - { - AZ_Error("TestRun ==", false, "lhs.GetNumPasses(): %u, rhs.GetNumPasses(): %u", lhs.GetNumPasses(), rhs.GetNumPasses()); - return false; - } - - if (lhs.GetNumRuns() != rhs.GetNumRuns()) - { - AZ_Error("TestRun ==", false, "lhs.GetNumRuns(): %u, rhs.GetNumRuns(): %u", lhs.GetNumRuns(), rhs.GetNumRuns()); - return false; - } - - return CheckTestContainersAreEqual(lhs, rhs); - } - - bool CheckTestRunCaseVectorsAreEqual( - const AZStd::vector& lhs, const AZStd::vector& rhs) - { - return AZStd::equal( - lhs.begin(), lhs.end(), rhs.begin(), [](const TestImpact::TestRunCase& leftCase, const TestImpact::TestRunCase& rightCase) { - if (!CheckTestCasesAreEqual(leftCase, rightCase)) - { - return false; - } - - if (leftCase.m_status != rightCase.m_status) - { - AZ_Error( - "CheckTestRunsAreEqualIgnoreDurations", false, "leftCase.m_status: %u, rightCase.m_status: %u", - static_cast(leftCase.m_status), static_cast(rightCase.m_status)); - return false; - } - - if (leftCase.m_result != rightCase.m_result) - { - if (!leftCase.m_result.has_value() && rightCase.m_result.has_value()) - { - AZ_Error( - "CheckTestRunsAreEqualIgnoreDurations", false, "leftCase.m_result: null, rightCase.m_result: %u", - static_cast(rightCase.m_result.value())); - } - else if (leftCase.m_result.has_value() && !rightCase.m_result.has_value()) - { - AZ_Error( - "CheckTestRunsAreEqualIgnoreDurations", false, "leftCase.m_result: %u, rightCase.m_result: null", - static_cast(leftCase.m_result.value())); - } - else - { - AZ_Error( - "CheckTestRunsAreEqualIgnoreDurations", false, "leftCase.m_result: %u, rightCase.m_result: %u", - static_cast(leftCase.m_result.value()), static_cast(rightCase.m_result.value())); - } - - return false; - } - - return true; - }); - } - - bool CheckTestRunsAreEqualIgnoreDurations(const TestImpact::TestRun& lhs, const TestImpact::TestRun& rhs) - { - return AZStd::equal( - lhs.GetTestSuites().begin(), lhs.GetTestSuites().end(), rhs.GetTestSuites().begin(), - [](const TestImpact::TestRunSuite& leftSuite, const TestImpact::TestRunSuite& rightSuite) { - if (leftSuite.m_enabled != rightSuite.m_enabled) - { - AZ_Error( - "CheckTestRunsAreEqualIgnoreDurations", false, "leftSuite.m_enabled: %u, rightSuite.m_enabled: %u", - leftSuite.m_enabled, rightSuite.m_enabled); - return false; - } - - if (leftSuite.m_name != rightSuite.m_name) - { - AZ_Error( - "CheckTestRunsAreEqualIgnoreDurations", false, "leftSuite.m_name: %s, rightSuite.m_name: %s", - leftSuite.m_name.c_str(), rightSuite.m_name.c_str()); - return false; - } - - return CheckTestRunCaseVectorsAreEqual(leftSuite.m_tests, rightSuite.m_tests); - }); - } - - bool operator==(const TestImpact::BuildMetaData& lhs, const TestImpact::BuildMetaData& rhs) - { - if (lhs.m_name != rhs.m_name) - { - return false; - } - else if (lhs.m_outputName != rhs.m_outputName) - { - return false; - } - else if (lhs.m_path != rhs.m_path) - { - return false; - } - - return true; - } - - bool operator==(const TestImpact::TargetSources& lhs, const TestImpact::TargetSources& rhs) - { - if (lhs.m_staticSources != rhs.m_staticSources) - { - return false; - } - else if (lhs.m_autogenSources.size() != rhs.m_autogenSources.size()) - { - return false; - } - else - { - for (size_t i = 0; i < lhs.m_autogenSources.size(); i++) - { - if (lhs.m_autogenSources[i].m_input != rhs.m_autogenSources[i].m_input) - { - return false; - } - else if (lhs.m_autogenSources[i].m_outputs.size() != rhs.m_autogenSources[i].m_outputs.size()) - { - return false; - } - - for (size_t j = 0; j < lhs.m_autogenSources[i].m_outputs.size(); j++) - { - if (lhs.m_autogenSources[i].m_outputs[j] != rhs.m_autogenSources[i].m_outputs[j]) - { - return false; - } - } - } - } - - return true; - } - - bool operator==(const TestImpact::BuildTargetDescriptor& lhs, const TestImpact::BuildTargetDescriptor& rhs) - { - return lhs.m_buildMetaData == rhs.m_buildMetaData && lhs.m_sources == rhs.m_sources; - } - - bool operator==(const TestImpact::TestTargetMeta& lhs, const TestImpact::TestTargetMeta& rhs) - { - if (lhs.m_suite != rhs.m_suite) - { - return false; - } - else if (lhs.m_launchMethod != rhs.m_launchMethod) - { - return false; - } - - return true; - } - - bool operator==(const TestImpact::ProductionTargetDescriptor& lhs, const TestImpact::ProductionTargetDescriptor& rhs) - { - return lhs.m_buildMetaData == rhs.m_buildMetaData; - } - - bool operator==(const TestImpact::TestTargetDescriptor& lhs, const TestImpact::TestTargetDescriptor& rhs) - { - return lhs.m_buildMetaData == rhs.m_buildMetaData && lhs.m_sources == rhs.m_sources && lhs.m_testMetaData == rhs.m_testMetaData; - } - - bool operator==(const TestImpact::LineCoverage& lhs, const TestImpact::LineCoverage& rhs) - { - if (lhs.m_hitCount != rhs.m_hitCount) - { - AZ_Error("LineCoverage ==", false, "lhs.m_hitCount: %u, rhs.m_hitCount: %u", lhs.m_hitCount, rhs.m_hitCount); - return false; - } - - if (lhs.m_lineNumber != rhs.m_lineNumber) - { - AZ_Error("LineCoverage ==", false, "lhs.m_lineNumber: %u, rhs.m_lineNumber: %u", lhs.m_lineNumber, rhs.m_lineNumber); - return false; - } - - return true; - } - - bool operator==(const TestImpact::SourceCoverage& lhs, const TestImpact::SourceCoverage& rhs) - { - if (lhs.m_path != rhs.m_path) - { - AZ_Error("LineCoverage ==", false, "lhs.m_path: %s, rhs.m_path: %s", lhs.m_path.c_str(), rhs.m_path.c_str()); - return false; - } - - if (lhs.m_coverage.empty() != rhs.m_coverage.empty()) - { - AZ_Error( - "LineCoverage ==", false, "lhs.m_coverage.empty(): %u, rhs.m_coverage.empty(): %u", lhs.m_coverage.empty(), - rhs.m_coverage.empty()); - return false; - } - - if (!lhs.m_coverage.empty()) - { - return AZStd::equal( - lhs.m_coverage.begin(), lhs.m_coverage.end(), rhs.m_coverage.begin(), - [](const TestImpact::LineCoverage& left, const TestImpact::LineCoverage& right) { - return left == right; - }); - } - - return true; - } - - bool operator==(const TestImpact::ModuleCoverage& lhs, const TestImpact::ModuleCoverage& rhs) - { - if (lhs.m_path != rhs.m_path) - { - AZ_Error("ModuleCoverage ==", false, "lhs.m_path: %s, rhs.m_path: %s", lhs.m_path.c_str(), rhs.m_path.c_str()); - return false; - } - - return AZStd::equal( - lhs.m_sources.begin(), lhs.m_sources.end(), rhs.m_sources.begin(), - [](const TestImpact::SourceCoverage& left, const TestImpact::SourceCoverage& right) { - return left == right; - }); - } - - bool operator==(const AZStd::vector& lhs, const AZStd::vector& rhs) - { - if (lhs.size() != rhs.size()) - { - AZ_Error("ModuleCoverage ==", false, "lhs.size(): %u, rhs.size(): %u", lhs.size(), rhs.size()); - return false; - } - - return AZStd::equal( - lhs.begin(), lhs.end(), rhs.begin(), [](const TestImpact::ModuleCoverage& left, const TestImpact::ModuleCoverage& right) { - return left == right; - }); - } - - bool operator!=(const AZStd::vector& lhs, const AZStd::vector& rhs) - { - return !(lhs == rhs); - } - - bool operator==(const TestImpact::TestCoverage& lhs, const TestImpact::TestCoverage& rhs) - { - if (lhs.GetNumModulesCovered() != rhs.GetNumModulesCovered()) - { - return false; - } - - if (lhs.GetNumSourcesCovered() != rhs.GetNumSourcesCovered()) - { - return false; - } - - if (lhs.GetModuleCoverages() != rhs.GetModuleCoverages()) - { - return false; - } - - if (lhs.GetSourcesCovered().size() != rhs.GetSourcesCovered().size()) - { - return false; - } - - return true; - } -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.h deleted file mode 100644 index cc8d4190ec..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestImpactTestUtils.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace UnitTest -{ - // Common parameters for process related tests - inline constexpr const char* ValidProcessPath = LY_TEST_IMPACT_TEST_PROCESS_BIN; - inline constexpr const char* InvalidProcessPath = "!!!@@@---???"; - inline constexpr const AZStd::chrono::milliseconds LongSleep = AZStd::chrono::minutes(60); - inline constexpr const size_t LargeTextSize = 0xFFFF - 1; // 65,535 chars less the null terminator - inline constexpr const AZStd::chrono::milliseconds ShortSleep = AZStd::chrono::milliseconds(500); - inline constexpr const AZStd::chrono::milliseconds NoSleep = AZStd::chrono::milliseconds(0); - - // Writes the specified text string to the specified file - void WriteTextToFile(const AZStd::string& text, const AZ::IO::Path& path); - - // Construct the arguments for launcing the test process - AZStd::string ConstructTestProcessArgs(TestImpact::ProcessId pid, AZStd::chrono::milliseconds sleepTime); - - // Construct the arguments for launcing the test process with large text dump - AZStd::string ConstructTestProcessArgsLargeText(TestImpact::ProcessId pid, AZStd::chrono::milliseconds sleepTime); - - // Known standard output string of the test process - AZStd::string KnownTestProcessOutputString(TestImpact::ProcessId pid); - - // Known standard error string of the test process - AZStd::string KnownTestProcessErrorString(TestImpact::ProcessId pid); - - // Generate a gtest typed test fixture name string based on the specified name and type - AZStd::string GenerateTypedFixtureName(const AZStd::string& name, size_t typeNum); - - // Generate a gtest parameterized test fixture name string based on the specified name and permutation number - AZStd::string GenerateParameterizedFixtureName( - const AZStd::string& name, - const AZStd::optional& prefix = AZStd::nullopt); - - // Generate a gtest parameterized test name string based on the specified name and permutation number - AZStd::string GenerateParameterizedTestName(const AZStd::string& name, size_t testNum); - - // Generate a JSON string of array elements from the specified vector - AZStd::string StringVectorToJSONElements(const AZStd::vector strings); - - // Generate a build target descriptor string in JSON format from the specified build target description - AZStd::string GenerateBuildTargetDescriptorString( - const AZStd::string& name, - const AZStd::string& outputName, - const AZStd::string& path, - const AZStd::vector& staticSources, - const AZStd::vector& autogenInputs, - const AZStd::vector& autogenOutputs); - - // Generate a build target descriptor from the specified build target description - // Note: no check for correctness of arguments is peformed - TestImpact::BuildTargetDescriptor GenerateBuildTargetDescriptor( - const AZStd::string& name, - const AZStd::string& outputName, - const AZStd::string& path, - const AZStd::vector& staticSources, - const TestImpact::AutogenSources& autogenSources); - - // Procedurally generate a parameterized test suite based on the supplied parameters - TestImpact::TestEnumerationSuite GenerateParamterizedSuite( - const AZStd::pair& fixture, - const AZStd::optional& permutation, - const AZStd::vector> tests, - size_t permutationCount); - - // Procedurally generate a typed test suite based on the supplied parameters - void GenerateTypedSuite( - const AZStd::pair& fixture, - const AZStd::vector> tests, - size_t permutationCount, - AZStd::vector& parentSuiteList); - - // Helper functions for calculating test suite meta-data - size_t CalculateNumPassedTests(const AZStd::vector& suites); - size_t CalculateNumFailedTests(const AZStd::vector& suites); - size_t CalculateNumRunTests(const AZStd::vector& suites); - size_t CalculateNumNotRunTests(const AZStd::vector& suites); - - template - size_t CalculateNumTestSuites(const AZStd::vector& suites) - { - return suites.size(); - } - - template - size_t CalculateNumTests(const AZStd::vector& suites) - { - size_t numTests = 0; - for (const auto& suite : suites) - { - numTests += suite.m_tests.size(); - } - - return numTests; - } - - template - size_t CalculateNumEnabledTests(const AZStd::vector& suites) - { - size_t numEnabledTests = 0; - for (const auto& suite : suites) - { - if (!suite.m_enabled) - { - continue; - } - - for (const auto& test : suite.m_tests) - { - if (test.m_enabled) - { - numEnabledTests++; - } - } - } - - return numEnabledTests; - } - - template - size_t CalculateNumDisabledTests(const AZStd::vector& suites) - { - size_t numDisabledTests = 0; - for (const auto& suite : suites) - { - if (!suite.m_enabled) - { - numDisabledTests += suite.m_tests.size(); - continue; - } - - for (const auto& test : suite.m_tests) - { - if (!test.m_enabled) - { - numDisabledTests++; - } - } - } - - return numDisabledTests; - } - - // Test enumeration suite representation of the test targets used for testing - AZStd::vector GetTestTargetATestEnumerationSuites(); - AZStd::vector GetTestTargetBTestEnumerationSuites(); - AZStd::vector GetTestTargetCTestEnumerationSuites(); - AZStd::vector GetTestTargetDTestEnumerationSuites(); - - // Test run suite representation of the test targets used for testing - AZStd::vector GetTestTargetATestRunSuites(); - AZStd::vector GetTestTargetBTestRunSuites(); - AZStd::vector GetTestTargetCTestRunSuites(); - AZStd::vector GetTestTargetDTestRunSuites(); - - // Line coverage representation of the test targets used for testing - AZStd::vector GetTestTargetALineModuleCoverages(); - AZStd::vector GetTestTargetBLineModuleCoverages(); - AZStd::vector GetTestTargetCLineModuleCoverages(); - AZStd::vector GetTestTargetDLineModuleCoverages(); - - // Source coverage representation of the test targets used for testing - AZStd::vector GetTestTargetASourceModuleCoverages(); - AZStd::vector GetTestTargetBSourceModuleCoverages(); - AZStd::vector GetTestTargetCSourceModuleCoverages(); - AZStd::vector GetTestTargetDSourceModuleCoverages(); - - // Helper comparisons for test validation (could potentially be moved to production source in the future) - bool operator==(const TestImpact::TestEnumerationCase& lhs, const TestImpact::TestEnumerationCase& rhs); - bool operator==(const TestImpact::TestEnumerationSuite& lhs, const TestImpact::TestEnumerationSuite& rhs); - bool operator==(const AZStd::vector& lhs, const AZStd::vector& rhs); - bool operator==(const TestImpact::TestEnumeration& lhs, const TestImpact::TestEnumeration& rhs); - - bool operator==(const TestImpact::TestRunCase& lhs, const TestImpact::TestRunCase& rhs); - bool operator==(const TestImpact::TestRunSuite& lhs, const TestImpact::TestRunSuite& rhs); - bool operator==(const AZStd::vector& lhs, const AZStd::vector& rhs); - bool operator==(const TestImpact::TestRun& lhs, const TestImpact::TestRun& rhs); - bool CheckTestRunsAreEqualIgnoreDurations(const TestImpact::TestRun& lhs, const TestImpact::TestRun& rhs); - - bool operator==(const TestImpact::BuildMetaData& lhs, const TestImpact::BuildMetaData& rhs); - bool operator==(const TestImpact::TargetSources& lhs, const TestImpact::TargetSources& rhs); - bool operator==(const TestImpact::BuildTargetDescriptor& lhs, const TestImpact::BuildTargetDescriptor& rhs); - bool operator==(const TestImpact::TestTargetMeta& lhs, const TestImpact::TestTargetMeta& rhs); - bool operator==(const TestImpact::ProductionTargetDescriptor& lhs, const TestImpact::ProductionTargetDescriptor& rhs); - bool operator==(const TestImpact::TestTargetDescriptor& lhs, const TestImpact::TestTargetDescriptor& rhs); - - bool operator==(const TestImpact::LineCoverage& lhs, const TestImpact::LineCoverage& rhs); - bool operator==(const TestImpact::SourceCoverage& lhs, const TestImpact::SourceCoverage& rhs); - bool operator==(const TestImpact::ModuleCoverage& lhs, const TestImpact::ModuleCoverage& rhs); - bool operator==(const AZStd::vector& lhs, const AZStd::vector& rhs); - bool operator==(const TestImpact::TestCoverage& lhs, const TestImpact::TestCoverage& rhs); -} // namespace UnitTest diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/CMakeLists.txt deleted file mode 100644 index 8298bb7123..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/CMakeLists.txt deleted file mode 100644 index 4b8e9d2809..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -ly_add_target( - NAME TestImpact.TestProcess.Console EXECUTABLE - NAMESPACE AZ - FILES_CMAKE - testimpactframework_testprocess_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - Source - BUILD_DEPENDENCIES - PRIVATE - AZ::AzCore - AZ::AzFramework -) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.cpp deleted file mode 100644 index cdfad1404f..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include "TestImpactTestProcess.h" -#include "TestImpactTestProcessLargeText.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace TestImpact -{ - TestProcess::TestProcess(int argc, char* argv[]) - { - StartupEnvironment(); - ParseArgs(argc, argv); - } - - TestProcess::~TestProcess() - { - TeardownEnvironment(); - } - - void TestProcess::StartupEnvironment() - { - AZ::AllocatorInstance::Create(); - } - - void TestProcess::TeardownEnvironment() - { - AZ::AllocatorInstance::Destroy(); - } - - void TestProcess::ParseArgs(int argc, char* argv[]) - { - const AZStd::string idArg = "id"; - const AZStd::string sleepArg = "sleep"; - const AZStd::string largeArg = "large"; - - AZ::CommandLine commandLine; - commandLine.Parse(argc, argv); - - m_id = AZStd::stoi(commandLine.GetSwitchValue(idArg, 0)); - m_sleep = AZStd::stoi(commandLine.GetSwitchValue(sleepArg, 0)); - - m_dumpLargeText = false; - for (auto i = 0; i < commandLine.GetNumMiscValues(); i++) - { - const auto& miscValue = commandLine.GetMiscValue(i); - if (miscValue == largeArg) - { - m_dumpLargeText = true; - break; - } - } - } - - int TestProcess::MainFunc() - { - if (m_dumpLargeText) - { - // Dump the large text blob to stdout and stderr - std::cout << LongText; - std::cerr << LongText; - } - else - { - // Dump the short known output string with id appended to stdout and stderr - std::cout << "TestProcessMainStdOut" << m_id; - std::cerr << "TestProcessMainStdErr" << m_id; - } - - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(m_sleep)); - - return m_id; - } -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.h b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.h deleted file mode 100644 index cfe18aa3df..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcess.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#pragma once - -#include -#include - -namespace TestImpact -{ - class TestProcess - { - public: - TestProcess(int argc, char* argv[]); - ~TestProcess(); - - int MainFunc(); - - private: - void StartupEnvironment(); - void TeardownEnvironment(); - void ParseArgs(int argc, char* argv[]); - - int m_id; - int m_sleep; - bool m_dumpLargeText = false; - }; - -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.cpp deleted file mode 100644 index a4b899dd65..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.cpp +++ /dev/null @@ -1,531 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include "TestImpactTestProcessLargeText.h" - -namespace TestImpact -{ - // Long text blob measuring 65,535 in length (inc. null terminator) - const char* const LongText = - "m65NIFtFmZV9CiZj1go6fPV6xo2NcBeEHoJ1JktsoT9mHUclADmJhjTMT5qaKd0FD8GiredOA6wSUiEGFkc62QwNxuEFUQIuOeQAH8NE4MuhvjSWnw0MCXnf5TppuCfU" - "5Xx7fimsd8wYoxDiPSatXSaXWcCdn7QNttzmGbwuldr3Fmbntiz5aliAcVrlB82RCVQ4v4aT41wCivgPJ0CNbfQyiPDBzZd3cGwLmvt6Nu9pP4kbTVHRsdif0tvs8j2V" - "93fotwV7Q50Xl3jniX2AUnwO6OgZdl97lPHn9wZbO7V5vRGQe1n9aweXK4u3qlBfplvsZAVOoC1RuY1JmCz2V2iOfucmIbpxL5jR6qobgFZI0Z2ioi34ssqqyH9xFgwG" - "muF69vqJjYUzJGgneGQUqWTH1yr4AIFyqfBFvBe04UAsYfo7jPQWxCpCRXNQfTuN2609HPafOQ4IJh8IdL77CtRd8B1y1Ah8oqWzQZ54HZFOWQ1FZ1yCIxcnaBNAhauS" - "RJnYB4gcTmdtrZlFysea28CjyjroTKTzeyzqfNhfteIs3urS3IyspinKEFIAWQ8z1NYS0ks6o47r777gzQcQZ51T6fAdHO346PBHyeZBdXZxCXK615SKctxlG1p2TvhQ" - "2TMOtb9xTr24JrlijnR9WFGzG1CuoLeTN7cgm6rk86KVbFnG74XwKNvIZ5xbHaiD0qaSrZzoGtBxrDdldw8BjcXLZS9FFOToOxdUASqX8TkWEP9oA4CDImFRr3AlUtsS" - "g6m4nzc7Lp4wdM8Jd9pPl2rmF7HgZ5yjO3dew9G83ewcAyN3XhLjVH68ExbdN4RTKbHsczWEPKDWQAoEzGLn0YILmrKh1f2GaobAcnqbyPkN7pMax7CqYfUJao1DvedD" - "gfqcRzqEb9F90Xn4Il3SaW0Qmsxy6OGTJDSSOJr7hBsthALqpKdFpMbFYeAvh2PYhS6G8KvzcNeKLcZfsWdONwVWnenuyQDRxQjOzp87pHMEtWGDJ2UxTsIKGvvxjrqC" - "bBalQhDZk0mIwUJdb5hf5qkJLD5tPdozFDe53FkFYOKDlsWdRpSvTnYeBk1WMxGD31yywHFUlOfUf2a17A76dBYu6Jb2KMfHcNfxJhIJOXWfT8nnIAJwvir4SHqlzPbr" - "IEh35MHMM5XpbreQZjD7sB3ovEmZfXx4dCbztrIXs17zpskuW36cVQDxR4Mz1ZEbdUtdZg7r8AxUNHZJhe0v96QmodqTp8qh7WP8EUmmaTr8F3PMyyMXgJfuAczpmMfr" - "rXvcFE48Cr0ZRcMDuWpPthkVPFxXAcaOAn6eqaJdHRQlioODCbjjw1kEcV2VcONDXtS7pV3YguletmZOUxYIcqVqWYAyztnam06QtmuCTTjg0l4a6R3obuugJZAt1gBS" - "IN6gaVPKQwJQLvLM7ChKDB0jmYE3nIRliYjIZ02blecFbvqCxJMakyc818tULWFQNV3msn43rNLCO3VAUKaQrXGn3zWjuqeur9WFoUnANTZhndyiuspIx0mxrCuRXqpO" - "h4zNhN0d7Rh0v22PrkzgLvB23UcgxbX6xcw7UQ5g3Cq77sq1ko191ZpKuxrBXhDLye9GXv7BKh4Wcfc8qHAP3qjKmHZInIVlpholPrxgrxfPW9MZSGDFPxJfhkyb6VSG" - "SNcyMPMhdtoCtUUAfDXoqLCbQmN7On7eJ0NJZdZA9BWtzjbFIxYYmnxl39QLFmpEkgZaq0AtAsYh8L7sbm8v0yGB0wTCWLFYqAx8NBsooS2YlgT9SX4XFgG29HibgsX3" - "VkCoJ3m4nzukHgnpzI6WIoiNjj4Z8sHR5NLWYWfN3ZOCj9dw23dhcTnRVBbUxkpyOMybV2S7ytwd0vb3ZJ6hlOh6dYZNdw2M3iN0NaKYs55a5zxNAMyKBHfemBFLN18E" - "zVEQgKz9qJv8EEBmgZ2p8iKoeeQrSRLz8blVW1OPilcpKGvIh9kW4lgssMZ7qRju2oJ0DyJ3joEnFOvtGwljLgoy7AzHLpN0dF5lrD94IL3Kn7b5g2h8yAhwRAV3Pmtf" - "GHLnb0gKzLbyJVEoBhvH41B5MOHtLfiAtcyJ2zMV5R4ldJV1vJm0t79xK0X99YykIlUuguwxnzWwd7tQO6F00gaBGvpVcz1zEwJEenBbjncVzXqAQwnO9zKtzUOcC5nR" - "7CfvLz0l4dMfAAMHZ7wGlCnWdBVKO6L6OyUSLGvrpz6jhdlhS45QJKkzTB50ZgOmK7owJwPrTnvDvH3Dxq8SDbmynSfvESmx4utROmawwY9R3fySBFKsLSB3lYlmg8Q8" - "xjabKm0PCDMjeslYXgohxklk7XMCOzkutm2o72sOGgqvFNXFQz9BJAnid2f9KpGc29oIUobf1RyIcTXpnPMedrM8brEd4v1aGdcb9rJv0eE3v9xt3eznh85sf2AN7PwS" - "tROP09CXfnq0qj6SOr2Wop7uCypqKx3gRSTggIZJL2Q7bFvwhYmIIpxuGMuKOBf2xxzo4pqME0HfIuXNkElZpj729pyxas04iOheSqurWhM2EKKmaBrkvFVK0mEa2T9I" - "9Rjh1ZNYvJU59S1lrD0DP4jkOdQ9bHYA9jgpXmFFN8jgWRRu14eKdrTYsZ6w3HNAiN0nqpmQUl30d10UAeMJHkv4xD889zQRMmmPCgLLJfvhZYfiwlqzBjbh7OQhydxm" - "dHPaOcFZpEQwbvR3KhHSVcbhKewJGHLSbHNWsRRsJrfOBnwSm0TfU3FD3xEqkPEl8mmsgQvakUAxI49RV9B3ueJJ9K8mvsZBBIRhnSZoqwipEskXqIc2VIW1CnW9qi0y" - "OObnoJ7qBpu4aueF3lGn0rgPXiXpcw0KaFxoq3kUTSEJXsj7mwHVYxKJfoPdaTuWDfJZ5Hk4Xgy16pUiD11SxMl7GZcXuAGl319LqX3nIAiahaoHTAhYimesTaf8x5h6" - "kgPxITKZXkqdD21Buykh4jKvcPLVivIr7yM8NOKvDf6zgQmm6VHihI4XFLgvEUe6EoYoE0RJS7Luc8dVFdNsVcGICayaaznjL3sFV6J7d4TVd35PEGmXtvLKKSvQ6Be9" - "gb5KZEk8pcHvYqpqzyrX4ZFve3cpv0IyLAlsWSawYV5L7eluYJNsC0vBImelV5oCecW7J6WmYNxTBpQt4M1MWcFzw920KaE9mXf4gkNCQb14FrbT8sNzt5JVFgz9Fjy7" - "aoRbG9xGCqyZkUa7RwmiTgFuk5CRSCgMu2lUNydfnAHbapUpkFyYRAShQr5Hjnu9H0OPhWUSTM6tz5Zzj8OzAwTTTb3Zp5DBywqQXZZPVcUutswpPVHgRRI6t65rd9nB" - "7XGXuJNfSQHotH5V3smIAk5gJezFb31PsfLkJ898DYGkmCwWtOvJqCglscSDSLv0EUoQGIRpJu9W9PQR9pU2IKpJhYks388vSwtAnYrUNOxEJAmUfnKlUZsHbJtFezAM" - "YtcS1I1kBK4eaP61zPWBWQ0uTR2UMXE6oVNuHXKLlQZFVADVTNBNRFvRO8s7LTmE4iMEeuRiOHbOkzexztJuxi4NDnyG964iJ580zHsehJckVyRh9w8AMaRs7HE7BwKx" - "gGwKY9DmZ8V2CyvqDLjxtdt9yIaPxg4ZtsAHTzYY4gXCQGoZRp9AsWaoNSJKt2JRmZE8CwJ6kPUyN9DCqPH33c8mu1zU2JHLBrsjNpgAaOlcr95W9YRYi6eBHoE9sT4i" - "a1zNr1veiFCh281PLhH52Bh3L4eXj9Z9vitWE9nJ6U58aDGj7zP3ITxRBni1UVlvVOYkInOQysSB6Qc3kjKq2dpYjCUQBZRaJO4fGyK8GwsXkBJAVJ5cFFCY2DARgsB4" - "9vksxVhIC8iNCUHeuzaIqqoDVD4EABtN5sgbjqsTmA76iojm99w0sfOGPdDmqKfxybKi8Lyg7xOQUu0HIIF4HYOcnsG6xuHfW1CNqvkcZR9tnOeyax19PgZaAX0Ulo2L" - "0HNi98GXoKbmuD9l1eFWPRQVVFfnDDH84KuPZcciXWWAIMDhGwtfUd72GcgeUju0NqI6DsEf9WPDDAw8VS5U6rHeXmhp9hR7vbOnyAstoarOqPlx7L3q9P2jGJu7Lhzx" - "fmUY2iGty6xoG7MM4jnrywA15ErsQXjQbe1JOwVx7YycaIH3pLjd72qLp0Oy1lINFHQnbqDqdnWWYHHNFnnYrndj9fXueBzctmlxoUM2duxpZJQ6BB2CXQ7y9guWWx9N" - "PrrYO7QpMAzi5MOLLIqkbkQbta7SlxHh7eouqkBHOvWaAJR6iI52rEJkMUudh5VevD5Sh4QMXUR3OsfPTLyENMhT50zwLzBzSHKQ3EaT3MuyERp33j3KXvME05lZpUfy" - "g7UkcSCAm0duYh7RxWBYykoXmLxBtlGLs7XtqjGvzvuQohfJGMnUnYCxNTpxjIlmhORhc8lUBPZ8Flq51MhnYp6sYztV3hvyDKY1wSgCvauNNNxjdguRzMOyBl8PcNcV" - "a02kDeE2wqVZvhv48vyzMITPZvMKOzhA2U1giReSYereQOehKtyeXXqhTGooUxUfOfE5dKCbfLyBW5wuptRtY0LZ1VdxWQluqErsSNJvX6JVXic4n3vwc7ockTlU8iFk" - "UCG2tfegjf8EpSlydI4vTjphHnIkiTTG4g26ILlGEDnoxJL3U8jZhWgdf5aGNaJ2wiOLy7FZlJkD8StKWRFjyGyNM3LEDOFAmkyzWwwWOV0hBjAKwkne0Wdbn1vV88b3" - "w2u4Cfi1ZK3n7NeZwI96qpzWV4oA0aj7yl4mkjFtouALOD09aKqspPNBMkY80pMwNrwkp9EIZNDvAslGAdCs4LmmsAsXMNQqfDo9ubMeDGqgjV4FhhrA26UljfKbCKCe" - "05jYo8VhtjfRUAutYjCE3H4VSVGEs8OIkQyNdvPcI8o1kqDoG0OdadaId1v4z9Qos5KCvPh6wx1mh8OW6x8MOCUwWKy7kDbIk81zjfMPADNTHe4u5xRsir5VSiaBGb3G" - "TADf5NpPnxAOpUAJlbiaLRA4TRFAuBmITqBeL8htAJIggtZsuxUZkTIs5acgpRBYgdV3gAN8mmkBb0m78x2buNHIqdbV8ggNCuJICD14AK8PKSuxNvvnzqhEEkUZEr8h" - "dF4bDnZ5kL3djQDEl0C1eDGOOL0LqyspoADQFmxkZV2fJAJQnYU0ewqvRqZTCBNtRtg1vneRzDUDxlSkMZP6PddDOkJ03VxNDnttG250htR0M8elz9sHCzMxBh6T4k16" - "lwOGctboSL0GhW7ZFjeE7sfXgCfdH80r0K28pw2sm8l1cdY14ZWuqpRP25xI6DYlWAx78T6yQ9IdVIrF5eVYbAkVs7pUCgNgOWdJmhet21WJgc25sRRcMpy9Gxq7rugS" - "EhhVAtfM4HiA3ofo3WwDuWcsUCjqVcQjh209w0O4vRDhHediAccuO62DwZRG4Gh4xAG0yFEOsrrk9znlSV4XMc3HK2ARPEguqVCVgpjJgfr5jCZnwfwmTz4L6DAAVb9f" - "ctBNSpYhr22klJgG96CHGnnI5ybpv1BGTYlJwRS1DUDeoWh537pcRmD2l5LgyFu2aWuwPxEypG9hpPmf5Gj3uwdyL0ABlKTUleyBeEVCu5B1RgnzKe6BTnInP3XtQKJu" - "l49rhKfDo8JOtcyyQ4LXEVaWnOrx4hs6Atu1L0mWEZ2M1UGwfYrv4Cd1ORt3WU30ZWwr5EsVBj4i9WtVogL3DL66CtYNM4HTUdU9p8VUjEMiKoxt7XkligvrvgcDxqe4" - "gGRAsMd1tDSUXCxYOv9aMO64DEYOAEkODj6neQg2zZwANgRY4wAagV4vzL4YomGQmSA5xE5mE0InfMJJdSVLPdamy2wNshqwe5ow7aj5c0itCMyL0EZek5NmMBqBAn5F" - "9pULD0wljEo8TgcvPkVSmIWrA4iTMraEHeHS7KgzDYC0FtM8KHnE5j7SvGxvykldyoXfjHn8HMe5XdHqNxNREZarZcZ6nkP9CJYCaWpuYoCDza4RBp80HTTDSWZXjKpY" - "H2PTPsztToaX8GjgAfjjICrUBiuKdOUUmznqa4SUeKvKoKbLK1GIRnOOk38aS69xB8t89iuhkymMZrczCWKdVTdEaDMFKI0ILTy3wyfkbkiXjEVQ62byURuHDr3tF5K5" - "aqLl11VLvZzNsTbJ6DhWaKD0HzSD0C2r4LtRzSNtsUkWIqETP1tkqCXAzD1KeP90KAVsj9TLOa8AagEau3tF041igctfjTa0vrrXm47zncAKn6MpklJjoBrtE8c8r3p6" - "rOPEtDdM2dZxaWwukrtLao3F5aezSu1462z2PgnYMmv1FlMNe3eETZ7thoyChiQrxyxrkzTKjwj6ra1NNYok5dZE6bgUZTxhC6Y44eAOIqWLn0tTHb8mryBSwiBkrPAx" - "RzVkYak80HfVgxFyL9Rb1nsq39QZLxrWuniLssgEFrutQA89B3nvfCV13NacROJdL309VUPMbDAZXxc193UYtIEZngCj7H8DGdezs2OQ8j8eIvSizweHcbFSPanqtlKN" - "BJ4rQYsdh3Zfq5OYMdNytxp2CNoUHg36oI4zxSMwyYKPC4KicGSQnwLMEwmyXKu57CazUHdc7PZkK3Lea7kNWA5hUdnBmrxd0xIOVjhFi4e3wSgsHnGphH4UF2rDPvC2" - "GRvuPSbEwEMyXJL159oeR0O5z7DG683VtOuLQPCyLm1tgqAGUaVg2iZULOfOLZuHlC8ByzwKQnQGvTNL1g6lHxh1ygaDdO3weStxgpMe9O2mV9mfj8CsOZSbx12NtDvR" - "46yqInqtHCT5nIbzTYQloULXEuXWL5AQ67g7rUsInEqzO2iwh1LwxqLMvoB42DPBEvpb7fmVOFddxzaEE0kd4p6i7t9cUi0JR1e2ZMrMNK7zFyLmx9gvAzarDSjGOY3n" - "EvKdBn63lLSANMwoInAt4Aept7tWydGScGtaj1yusS5iwO0THRP177dKlAfCjkFGx6mR942AnCxo6oMmJ91gATIKWm2fgBaW5BUKy1fQ5MqQDnQzEzRYEjMi9k6vQEur" - "VkVM5059sI5UJxlVr9yaKGHt3KTeu1F7PGNWQCwPIMqpVMlx7CbQ3bhQWSJRNISWUWFrWCOY1n8jbtPdwojNG23ugMCGwZYedB29EHuBfHIDX2RPdKt2dJAHg4aXjx6i" - "1TRgq5nm4K0Avaq86WlMfK3JkK7VM7Tt4XzfLLx6Uutkv7XU7GGG0bbBbUrtIsug5NFvhvrwcRR9KL5kl9z9nS1iJu7tJdljEcGggHiqjRBe6hoSPxvAa7AF6kDV5iOV" - "Ca9LkWZjg9C2P6Ri4KcmiK3OwZhFzn6P6jFAF8Y0raLbZSZBLJwUUzT5fYLYzpiMMsmgT2HXl3tOzEEZKhdxRjBPR5br51LanQypKBNgFuR7FlRuSEJaNiEPZmXK3GeU" - "Pk6PSpR0AdFaEKbowx4wxsJvzKT96pSzR7LaEEqOa9phydtTh85HoIuPnKkVcFIHGpbynCGL4TUAbqv14rfQfmNrVTqx9hqpJApofNu9tyhBYJUrQ7PrOCPG2ur34lOw" - "sUg5MkHEyzVfq0WGqlC7TTGoO3bA3TWsO5SHGOhHsq5wuo4JXjPrOz5vEI2tQQlG2Pn275cZiCAJ2XZmxG7k3LRUJOQ6NpFg1Rv509bnfB1MBet2KCA05UYQMG5MMaWN" - "WlauS0eN6pka7p1l2D39aDiiIeOjwJvWfc6Tf4FM7muhWABXwDymT41VX5140sKrFZ6h84jdBDKBoTaqANexdXrs9D3Zr2BlgiP0hDX9bqmwvvxP4JACCBFFcA2ZSM0E" - "K9J9P15AjyZg6iWei3FjJ9t3B81dN7NwG2x8bGgw8RZt5m8A9D8F3D6LeVgGVLGuAa55DFnhCwtkCviBSTJn0btERDELpFhQU5H3A19P26dEFpIZIqM5kj9xQwqMUVbC" - "riaOOL3BVc9mGt6jSJn1TI7YyibFoT06lSybiMGB3CO6T2tov9puMTiVoWEQJDGNlUUhqLyokzdbo3d8lBe4aWUFq19Cs0HZt50o9xdbIQcyEtHBMXNZ3gwECJ6QbYC5" - "igpTeTLc2aXk1003QJsZ0qgr6OkeOqfdBFr37zB9EEYw29uP7en85bKbqk3Xdnvo8HbIeCIDyb4A5u3RH9BJbre6CCsWbqP9P6bCkJrtT7JuDcpb2q7sWKuP6vR3fILk" - "eR30Ptmb6XX6C6LL69TRXPDE5jkuWiO4Sb3SJi1zSqztVvDgyfrar1P0LXnE6vkJ4Anqws8raHKz9EYuo9ZLZoYqxkqieyzRiTkqyrz7e3pAk7VLpHItA1HjV5NNBWyB" - "2Z7xnMziRxWtgv6APf4huLiLE7WcTTkkdyv5vm7uPjtZHq5ziUx30IjXW4fexcBcp4kmFPwYDBTyI7yOLHPrXxJIFgcdalZTEbwxSVLdlJpucFkXfG6uIVWSTWR27O5F" - "ClBzl09FLL5w0EON184alTAtonqJou8HJjxPQC9zaqqgshTvdgLw9IVZLFGTQwfs3bgSiFH2foWClTcdns2SnY1BZf2EdPUETwJKaFlAcz9DO1shcPN7Jo1gjm0xz2um" - "vznuMIOu48BSYrkvev0C5P3fTNoaYsAmmA2c0oWfgeJNN0SgEJ1FuNWc00yLWOblaOEakAkIysiRemkijteLN2p7OoW5hzOqwADExvpRsy4gdHbCGfHGQPKf0wvhqXvm" - "OqdkPLqp2gynWmiUuKcWPulZlyhgQXAePzS0oVkYTrbv6yy5mLSvgro9L6Vf7Z0OsEa5fWOM0mC718tmE4vbtBXPgXiiTDQVqqOaUlAMndzA18rzlNLRlZtv7XW4bN4k" - "eOcuSPbNe9tTWo9XABv5tqe94TRBPyakEpX9hQ7Fc3yy0wQLXrFSzzZDPlKTajNnOOb1nBksaoD6N6x8vXBTh5rhsG2DL9jKnfrEveVJt6H5o2HrjYaNFPQuddKJb5pW" - "ns98Xb8puf5QSVs4VN8wyzW5veX5PfTUOHjDRrBAzMtmb0C0a0qCuZq8dRkqigJMRv3LCmjtoYYYs1uIuZMWyaqj1BD8g2oOxNc411hkQpwwxIqzm7cevxMFKm1SBkqb" - "JoZHNX7P91ufuEeStHxRXlwUXep029JUyu6DkxuGCxbQIAEvNTdI8zpThTbOxI0jlVKOpCNEBo9LetsePnoA0Hj7qha4owWWfElKHzdxPB5RBxxkjeVAXOQLL5waKW6V" - "nufDcpSYJkpySbB6cR1n0qeX5lIqMBTxyY2rhCq3lK8mauRd3KxTOoDRuWh06FeR6kUindQ3GmaSDvKDF3aWunZtjcpqFmg7thwPhdsw5QyUlS4JFRbpBlc7YU2PJ8zJ" - "UYY2aecQmncf9ixMiYMNQbFX9k9SPM1T0DdzF8DP7tmgKLDsTYqhOutxBRIYCxAS27jBZPsoxBYAw8oxO1NXeCCJ4SETjJw1sLGz9HcBPzONy5slxvbYpdNZumHg3bp6" - "FRXdNDFYKiHkjnWIHP23HEAc3m3gqO4ZR4t3uMORHPFWkMOPbBwoKf6RMlLJsGCEVi0f3hZ9bAMaLVdGgvNRKMvpHNELMw197TbFZiznwIfwwt8tX07AtcDMQmqatYss" - "WKReaIWsECI1KHLfAehJtSqQYy1XPnhbbE7aSWlrkHl7WIbxt9jY4huOuxxhabKaZ1mbIaeyOOefMXSwzbKY49MrblqAJG2mS5Kuj5njMpSjJXhrNndZwYHIEFaquWqk" - "PEo9mbmcDwQI432dz9hY4wmeL3GxJO2318EzCPTITS0mZ8gkNg2DzFmU7IRSSW1hb9SxjdiBZXEuwlMWLzawO3MhiWQQiQfeWp3eISuZZ84IUuC49kKrmDiWkGRxLjnW" - "6KxvADT097jBN8blD56XtitDKkCgnDVtnmQhiNPOReweIesKxwTYzCnXH7jNkZKQtqXjunfjORabiXUAe6iiWLet7Qls4frgiSSjS8oH27zifNCmgfkvTEjxQXXTTFeI" - "TdrjTU5HYmTQJ348OFFYNNuQkZhdhGKaloaMkfhKiB9zWHY6s2hSO9nAfwBNmPD80WhNQPP9WIwaW0hMTFCAGoZNMRcHrlSgdn9pW10yoErcm2yrSx3NKu8paYUwmtPs" - "1qtEla6PfuAJzyJ0VsLWoegOH3Y53UUhggjqIkfqoqIokAsw798RLLlsuMWUGl0UgGKsViEXmBTjCXGZEQNX6BdDhr32ysNyybnPvWOoHF9pNCYssG6dtdMzyxydLYQj" - "l7vnIwwhi92QKHr4qluQCmFqOHvWEc2Wdo5zVeZVjmSycRaFR08b6SZZS0Q09WyGhXO6jy6Zw45nUCY0KwmtB7vIcp0mq9mXSrvnMZt7rKivuHh8H5jOnDTXHrZa3hxW" - "QbTyE1SBTKavdQWknVhUFxvas6tOZk4H7W7JIHT7DYUoRYHQkDPhD2CbOH3ReIL2QvESfjbUBVej22j79E36JoaPGjukIXPpbiA2jsefOicv4T35jEj0bdSbQ3yEv9vb" - "z0YOZPutnxwLfffZmLnnzgU9ydVXnn5ih27Vzj7Lv9fB3spJcf2j02GbNc0rp9gOpu5PhgW5fbCe6IUyfCL3oNnjSm7BSMuumMVGFFJjIOX2idgXevLQTGbkeMwv0KHl" - "tEcMaVj6HrbKYUKvmAIhQ2lDGBF3l9ox1k2lFCRKzs9VvMZohSTLei3ueO57hBOnVCxmMKMLVEOJRTgv7ZX3yQ8CvlZQT7nTJ18O5k4HingPWGjENFctUQ2VfDQK8Out" - "ZKOoLEzCE5HzlyUEJGIjtEhvbKq8EWtBAx4p8v4O4ufAGcIlTzoOveKb7UyXhjqpp4o9BBMxMLh0mH7mNrNh7cmLPDpd1QaR2PdOF5BGs5YtCdHlw8Sb47B9Q0vWAsBz" - "P39MHbITqKLClcUKgJ8a7AEaKnXKVXJGXul6tjwuiuEqfQtpnmY1I0nj75OhV1H2w7CtoXqEMDjrk9yT3mxFPwwVLgCVBNvdAaPdKSTa9U2zw2sNatYgMHN9ors4Vb9y" - "H1elBvlBbl7HoXyIbV64P19xipFrCtZYV7otEtSFtKDkMygolFbc07bnnJrelGMxOpISP77dRfVMEFqLG3hYr8vW6AojA7p2uZ0sVMEwO45ItjLo68e9nl7jhK9DDou7" - "Aj0v1VV5fxZXB0I09r1fGCM65wR75rE6bGC3vUfclYIspmziq04GApz7XCnUyBv8llsK7nHzIglGUynfnPlHYO6tliqLHEE682nPTH8wbUggvPRcrfve09ALkHsSDUIU" - "5HgRPXwQS34C9xFUTNkoOsgzAW9nsnJz3kwadwRqFSHJR8F7IkRQvGsg4G0FKPUiIs01mJ82Lkm53AJ2a8JBYICsWPEFN7e50BqMioWt9b6OQ0vwvPhZ0NmjBDKSwf3O" - "8QxFuPdsQ8nALXz3MiM5UcEykdOElKHtah3DUwJKjX6I3Ie1rSB8qKPEqjrid2AAwJo89TlwrNjfgvLnjz0hjTUhcB71YhM3BJbl0hW0JWg5wLgNQuFp21gzIl96wEVR" - "yD93LuDNmqsEELurUUllJ2rF929e6gwA8aidNYAFmej1OWw2ULGgCXt5Ib6BjUVeexWxQqMsgOb3I7YKsmX7CNgBq5odN7FEbW4MY4VDpgx26bcWYFoV0FY58al7Y2Um" - "abSKv1jW4zEuCOS7NDaceJtPMXUqi4h9pFw3jdJdKP8JbWhHGCJwFotSc3xe57KPlpzZ08pafdZefJq1t2eMK3Z9tRvLnWNSmcuicTMJvdRbgDSONRRvpwxwyJpwuGxk" - "gqSr6hY5QV9xFO0u3JcY2zuYvaJ8wcB4CxqtNBVVghf5HvvPKOYS188mVw9FmrlWuE5dHCquIVj90gp5vbAYJgUCUrRxNzVaL7sTS6jSA1Li83Q1I1NsaJyZa1SKgyen" - "zjddCeUK8Tn3xHj2HVlK85C0s6drwfyxIrbnCbQc5KGEDBM4W9H3Pe4E7zB58UZCOYDq59JD5horMpFT1Ny4CIZz1heh3n5mb4jH2f5AdXnKLqnAPBCBWB2GW90xYEPO" - "ZvUzpGHELmD6Maf6OOlC86wjj9CjrQqI4ER3m0Fb4StARxGXkV0yvRQvttaCZkrMOXogiRvtlQjimLNfFfcsNBEK8UHwqcH5tB5oQ7ix4dNa3sdCrtSSZIdcfKhefcjF" - "xO0YLbtWKnWttWOUDkZx7RTPMYtdZXDF2jJ4mR71pP1vYcHhSGVjBymb7pN4ZqbiAhi8AqCBCch7pGV87bDnLEkxAXdZRs8ItHIT6ztTM1HEI7pzzKPYzhFjVGzQ41a5" - "xnpWXGGuj0p6cVSV23s8fbNIfv7Gh2M6lS7uOjSlltlD3Gicv1HHRV2MEYMwHbJMFiFSPykXge8BIjhE4cCawxau4rKpJFp84kHu7HPlE6G6fUadIXJmyaDyIZ5TTAPP" - "YiShh1HOghRKpNZLEOkmJpfRUwbV4NnCJalLFQKSR3eTRSBqw932wLS1CUcj4bXmHbKDtNOEIkdUp9lT0lOyVP9IUOOFH709dDTlf3Dd6IZNJpXzi3GScIxsFaZVq7NC" - "6cdcKwipRUsD1aghJqSpAJZFHFIU8nPsPJlu9CpWCUCwKIwV0eSWonw2tSGvJToFqhyHmBeBTXsbymRCXJEcA8WVqjkmiR2nHmh6AnGBhdiTZfBxu5lGotYDfR5b46zx" - "fVGo7Ys1LPj1g5U81hl27NqbgutFQ3maGBu5xZkpeka3JCmdbpaPRIO076emwiJYi03Bb6Ncogv3Eg8Cf9xrNmIdvFRVi3iPws6HynQaq48lDYPTRcVB2l29UiA0nJPv" - "bomNxXa2RTMYDs9PzoI9CLRbxvz51XRRrAP9XzASOu5Y7SqzRFYmoCyS6drTrxMts1lywiY2Mzl3022xsQOcHz1Z6g7urt1UrSCtvQYJzIzgtWuHkVN6m9c7qfor7bbG" - "xctZRuHSrQaZUqeTmFduVLQlJuOZkO6VVwyDRUz48pzPCAfQKtDKUj9uuDSNpBlRnreaNsrIL8U28FDk4RO9TYrn2JgWLbo1bYewmyJd3hPCJg6HlLYyv7YH29SoAVyl" - "haTjaIfnjijq8cOHS7Tm6DANE5iuP2MjomrIwI9XjwDtLViGzWEoM7JC7hspvkTqaGnaaDKVoHnLLfAcEeF6dEeJtzSkGmOmt9LrN7Kg5sWBXRfbyUKBsnPIkCwlrbeK" - "fTHdvlzFgBrgdrkadA6GB6h0HMqWcWvzyZoV3aSJqSQXMg3hAYBwPP1iGxygyAY33kKRQJcpW4738TOBPuEd1GXn6WaIlB9DDBKJomssrefT8TuOeTKUEy3HlWPkyixQ" - "1tvX3AGy13TmbTva6Bg48QncMhRQlpht7xuGJ8TTRSeyvcTWFB68tvA6Is15sVdvnvyiwTy75Q2XHM4K06BKGH4PzOm8WRM26Jp2FMcsyZka7134eZVDjQ7Vb0MG9W8M" - "qioMWg5BM3h3Gz5zYKU2iqLZ9qTsLxESdwetbCSb3kn9pIL39qdEcQ5DPkp49nVHmS1NlPquBDBlD3ZCdDvPHVQ57VKMGZ8JdMU1xOyurJBq53FUpxt0UJStd4aeJwRm" - "i3WD7OYA0TYzarDn9webrHrg5XLJL1Da49wQVFuDcoZK1RL5E2Cqd1xA6Bw3jUZ6kd3hEaPTff6J7yBdkMwSnV7hLLthPCexiOZ0PQuyITyzs7S9qaWXTu8IAm6Z9IsU" - "hoNFxetpiEQkVNkLOhDxmTDcks3Ot1vXYaIVJs7mmEaqHYdDDjvccMi7RJfyh8hRADyEWlMkqoY8io3NcZt4p6FtLv9btEwVkJ7cb6wjrqki8V2jas6vpjXwE26PWxh3" - "yBqfBZMG5YItqZBonduR9cM7xyCVQyocuHvTjOFLThhTgvBnNKgjv3twZL9mxlWIG6AEip0xLt0feP4GZDS7c0ojbOYomc01mZkq5NY9nC8SlUPuk4llPL7ToaA6VSGb" - "rgTx3JUx1LQj3Yq9oSJ1A4FqKxVN5MxuPQ4D7CvClVz09Mw8tjnvnCAv5fJdxGEvPT9Ma4vnivT2wrdA7Wl2HtH3KbakBXd4kfwfHgqdQo30sAr8jxZcRr8AavLgx43g" - "PKe307AulqTgKyRy3iiCrbTUUJKkq2BbptM4lzDi3GljCA9W2t1Qs4W4PWWptuYpMy4r2r3ikAlqLgKooKWwhZ5uhgzGQjb1YYpXlEYHMNDLBgXTDGGoqOO0z4Xlcg1i" - "yXRsy1p8kub5VW8xlWtAwzF58t9w2Wn90P02qNzkaNkw9V2jcKRr8MhtDmCG9PKaRliDmch9F91jnQJ4Hq3JD1B0qAKOB1PYSVh5mHXVEtREztkKYiNRup7DEwpGBRnG" - "qCTZvePHgryl9CIRSH4X2D2unXnfpfIg5PWh9NR1sl9ECu6hCLDprmil2RSQWCna4zbISSAUGurXSJ2m82aU14kseARLymGqBNSDBlPBUp4DCSJsIhk5DvIJNuFzP7cD" - "3O9byQeGI64Q3VzClDGntGdVmb5MZ0ZgGpPGqNM6LyN9b5n7oArfgsRF2zsp0smqLPd5dmqHv43FhzcA7wr3JxbxTcp53vmyDMgCpewPvik2HeemXS9UA6TAC9CwrMf4" - "OqrdXohx5x5TQ1Z3kNieRYeqoJ20sVzWgXK3BZ9n91nGPWUV3BsaIQbT7TbrMkcDQrHE5uKuvkQmiJX0AjUOrJBLq5LarUbze7IzxOhw9HaYaOT5XfimMO81cP0F3WJq" - "0cpVwMECtoIdKHioGUA0ZxwArOGbMqZ6jGVHShE6I6xRh62qdLDwJnN1BPxenXG6rxFA3woQh7eIR4wG81yxycQOx6KS9yaiUPZ0xJcWZgwbP036CLJLW71v35xmvx4w" - "OeMVDMIpDOm7RWddxM45nrx4R75c8oYSgAE8bDGhQGnAfC24prxHTJQo900wtDPKvosOFWWzcPgFTbTkK4QybVCMDdfLly1rAm0SJuLRfvDF3PMOoIm887m9LxX12E29" - "FjQK1nSkR5vO0f6SKew3yNoJ7eDahdjpwwzZIej7N4T8bLliw7gYruoFDqEPlLaS40rlCSTEr5QlCF92omO4mHx76bB5ZUalASqBurhaMFay5k8FSANzmbfhngNfgNos" - "wFAVSc9hZaa1PVPvE1Nef9WwvWPYwIvMPqi2QXu4NVkTYHgQjTi8mxE3L0ozUKAMZ1P2mtm9E7HzwFOIaE81DGVD6I0M32505m5IHXVLW7iCobNezqUgNU2htx9q2w2J" - "UlkLIosIHCgw3aarlE9F8axZ1juvhjNGFLpYwe3UkjgAVzURaXzuYK5Ma6XLbiNKPMZuvkXOQxLB5eOgMR58O3Yr4d5PDRLJeYMqh8lficRaPxeFs0ls0hFg9IAYuYgl" - "2fuhqJu5GloC75zb9NqMuQDIRicNur1AhFkNeGQUBylYBjMxSy4BZNOwl7MC8di37VCsid5oWhoYGumzWcLjbChAqjiW6Ogrn600KR66W6dotVjQ2Q4BWEIwr6CJ7nNF" - "ytapx621nMo2PBBUJFl7Zv2i85j9DuLxRwSplpH0Je1nGZU5YxBDst6f337AhExBD8yfurBLd8KVWhd3NPhulptB6UdQveFAtdfcvyjtZfmLnhQLzKGEkJbZnHckUwP8" - "63dAGYf6Ii737h8doUsxsoGzfRkoYzhw5mWZCMGUAAsX0iWPTJMvEXirBqAclhrVd8EypgPvR10yR9dA7Rc1BBGqiX4WA4vjODVnSblpNc6ubuTQlkQdwnmUbSe6Kk7W" - "gYDclvLrp8FqqVe9oA2OmVJSuVOcJwD5lZKjIhZjxV1lQFdpoce27RNiYgeaoGopE8tpYd6gpts8ZJ3ymzEJEzuM7HoMVQcx2gd9x1YP0zUYMCz2FGTEyteCAJSwUI1j" - "KY3krXyLnF4GzQDvvIdPGJW3gsSVfAl8mNlO2Mc9Ex09Q65D79SWw3MuUJkuCg1efM4utsThbeNcAmTAR98UIAr2auj1xknHmnoSVXoM0T5DVtcGrWkGUveXaotUNSrj" - "GrYu1JqislrdQ4AjrEBxYEvj0ic5eoAaYjVSiiFTY5FJqvUdbqjHyEoBiSjmliKDcQ9kGqnNj2fVzJRMgKeVwiNiz9DxyiPeU1a8pG6aW1CHrWw8J68KmRaiWQ2dxRpb" - "agY0Xlpsp7je3LxnEmBihYMcjX2R2k30bpFj5I1xxwciXIdzvXZSfxBNCBH7xJ220acIIiGvGqihMIIcMiKiotbUw1eqgnCTIDLOmKvzKnTbhuNQhN3bRhcT8XDZOEvT" - "wWSdnAAxsA43qbjPBWbftqGyVuDxNCJZ2qocGlM9YzzXJg2ScsPJFmbdk2Ou9YelpInJiDWpaMndSi8QZdO11nW2MNoertuHskTGZoF42mqgC2YqSpJ2DAlJPlF7h41U" - "uTXuSh3X5zDxa7n8CURmgR998iS4SuwbdJUYqotfvjAR1HtGizaWAVwL5wBm08tawEkWl8Qmo1Rm2h3ZZLlYfi2wvopailUsaSA9QMehQEnV91ZgiI4GuV2hzekuySY7" - "hcyxITjNkcOqNT23te9W8eUAOEvuiXADAU83lm0ehRpEn3V63NV4kAr88NZSFIcg4H4AeY3EVCYTvyojeHTMhb0RsaXMpELqpyRmAkmTALZLoEZsSskkioWF6lZrLn2Z" - "w0BmR024Kzh7m2gNewqWpz3xlvznzi4SQnJBe67EPPugbSltS0BQeg5JxynPYEX5Pu9RArpjihRKSDhzTai5xBA1H9Yv44UkZCdWUq4RaCCxEVzgeabPBhZOhbuUMuom" - "Kg20t1aEtLqIIiaX6KuKr0qjO7a8AvL6gsAqdCzI6Ml8RAOVlfsJHBTRZoDP1WAzHu5DBYJ5zJ5Q19TdAdc4VAfsPb9pNFtZdFdLx4kXiONawnVkzAzcItx236dpifCX" - "4bVCd8hOjhIn4KjksdvclQbNQd6x0bV35N0Km5AjeeSwBcCaXdDTPwH1iQDsNg7S1azrDSh1oqzAO3yamt2QJyRI0k9phTNVEHGXZFo2doqCMYTZf9pF46hzRVlY7EAw" - "Ke1Vbow85IMRNcrWtGte1ld6Sh89uWNSzfVqivSLpYd4zrmmAbbW7KbZxHT94tFgJiqtxW9bjkdkWrU6EBWosdMOlH8AEY6zk35Lr9FcTMzmsbfXEXzsmuD3iWC83cQT" - "LIHGrWc2koDgXZCY3SPXdboUfG8M4uxyUESkw87Q3hB2mfjSrwjbFZyhkBWXE8C1yhl7oG4KnJY9Z00TTNJJFQmG8UMAQqnraoTleI7iUN77eFm65OHEp62q9FpbJZih" - "8GFyFstnUEJZaPmHBGkGD3LNXsNeLr0RNAgdxC1FsjWPfjrSdNQ5JO7mg6DpZoiqnvnbwHMk2kB4wra1csCe5LI82FZrEHliaDPo93hoPvakmUKvCirxehDKHwCbRaR0" - "aR0CeCo23xc6P3Dti8dcdiWXkMa3MFdU1LjlDXeIcS6Ovhat8sgOp04s86She5MwS0n1pMRv8Qr7f15PFF8kVK0PpfQ6WUTMKoya6RtBZYkQ2A0pdrvNEVAWWgck9RWM" - "BXnsBbc0MfrJlDaQ00P9Nf21yd6CTNLZRFZYdWXd9ss4uIaRDQ1pPcwoF5dklMyLEyQinBUY3rWgSVKgRHHHa28Bzo6XXBwN80tE66hIFsxro2q5PXQwaYR7S8absEhX" - "haUb6kGrwQpO7ni0B9m0g8L5Xv6JyBAX2A7vMp2E8sLGTbWcnSdz51DfPJs5pW2GLbQ9AigvRftXWUpgDELvxlFOOWg4tsr2HT890j4PhnyFUxcQHNYN4lF4iFr2Muhl" - "mhYcCifWzrhCP0LZ31lGgNNQE4hyYwAUzI6JZ88Gw9sAKngB7mtmoBtJqxmxjPA5IXmXuwC20MDxYwi71gLFoRRqJy4eq74U9gU9vWZWTDjNAJ4v97ngZN6UvxnJmZMb" - "L7Spv7bOmiwoBeGOtp6kjVvgjzyM699Ljoy4l1TAgR0LNnEf1ue2iJeaBtwciWqvhdEaZcFqO7lGjo20BAPTQugYN7jVU2Oivh49sLLCJptKrhG5ovnPZZdIQnm5hR2s" - "dusQMFP1fi1jlEhecCihahLQnigi78WquMOE0q9NS2N85gn3fmXByTgOlW0QwWRxJIbXLz5AU3E09wpSRU4MGYB41JWPjKrb2FQ8LUBS5rhmtqEV8TMkmeFmFcH48UUL" - "FhNvRLnxXKoaJo5uNqXQ2C7IvJ0UAT5NrjCsMJdMm9uYpw8Mt0reYMvVVXN2sB6lhRbucZ2Piyun9zPjqYuzJIoyvEncHulxVcPQKCmdwfVxsQ4CrFRKYXO7F2WbiFY5" - "jvyfymTKDEX5816guSrltslhnPsivIy3oYVuBU204wOXhuO16iJS2PB5ZNVepUEQvUdYFqiRU9ltVxd3OhdoJZCsLUxMm3vkivld7QoHbYedzOyv7e2yjqPXz8RsiPam" - "orlZ6594zKqNiIZkDFpAc7Hdo3dae3HXnqFO97oezJCSFK3P2364nmYHVSGINtD7GQqOuC1ZOm46MfC8GQJUidFV24q2QhMfF828dl2zKF9tEk8GBKwdiqifMAFcJUEy" - "VZnliKQjFqHpxQZULef7YAsvB3uE56wnJEeeVNw3dBuGONKqaAZFhQkPm2pG3KG8U4Kky6s2IDBQPcggjzlIRtLEomkKH0sOFlVuPbEYc9AdMdiZJRaLewbogM4P183i" - "y4n32YdEldNOO2CWe3Zaw23QUIGxz6wfizTRVoInMzZwXUSu3G2OkJLdXaOBQCGaYmp0EOBXpOac2DSZBjdoT6H44VC2kxGJZPVXwjWkieGMUAAOiO5F8L8KevFQAxc1" - "yM9hjcKxfjjpRDeeK4puGeCCCSNweiX6mJxYMeT1TkDuuiJoVGgRgFZbD8C50dDGCkANlYCOtxOkbHJQE3bTNkkpoDEPG8MlaTKxEDdaDFcvojeBDT14WdB7bW0UOUnx" - "cUD1w0KvSGhN0q2CceXEiyURH7jlHHVeitZuDC6XwSCb7foDPah6IV3jiCxEJAydBhoIZUkQU0S09f94uG5eourmWN7ryDSlCQD8L7CbXV9E43dRyLMAFuqxSG6ANcDF" - "HfbRXKCJB20iD7oqjxEmCJ77L5uh6rhhOG9uA7W5WyiFHcpDdYGukcEl2A07gfiJEtuiWY4PgC4sBRx6XBH3u2ZSrXRtK7CJV5WCIhGc7TPyGPtAPXHtAkLvI1CaiNeO" - "s5XKBOushli18MMe9Mr5gO6V7yREIUdqUHaCywUbbai3R9mnGTfdKYpxvCiKx2ED375eHgmJZeKmeMwgZ70IP2xC2sxLEaRNX9Rbg0UxKaeDoZMlAK0HkBbboWdyChcX" - "LQK4vPcFUfnjUEokfuxRTqobNaXz1rdtN7OqlG2Q2xGkluIdtoyCORlnMinowepbDg7UjNyrs0SIP8XKLvWeGdNH2UJyc7kd0SVQRUUq0C29lmGawQ2WwybPzWqODKOm" - "pK7AFG4kny9nBNEADJxMcERvhKANAFldaBylGnuFT4NpaSmIlB9Wd4dFL2OcMNYivVkiDhZ6XZSgilmuA8020XOiDxiuAJqBGIXIYpRlDKea8DVaSaaxo1OA0KdCh8OK" - "Pcnu1Qw0qnYsCc2nrHQYmCQGLBjFnlrCCXgTJVvaJDh4Fp5A3Gp7ksrQgJZDyOU5KG3DmXaiebYB1LbcmaQ83XeBjxcdG5HXCpuL4xz0z9ln4uQ09RYD51OxtjVqujQG" - "rugxudP6uCi1NHxLzFj8Oev9GOGaVu185oRGv2dgifnxlBnI5aUgc1URLgEMkBxk6XeNwfIXkgSAX1iGPhirkUFhXGxUkxZ8nnGBD2Vvil1qvoi7RqNYN62K4t2qAEUt" - "to4HywGBYLmDWUb88jfm202KrPMu7wQFSTzx2RsYQ5tIlF8A1y9LOtELFfCwfyXSzGkFGAOXGCnpvnkL8lkbbwlMGnN9q4uAATdRBDWODIShjAQm1CbAyMhmSj3bImEV" - "Gz0n5HYCHdQNi4NGdCk8mMhU7jl2n09yWtHs4ja8j8HBD8oGg1rmOdQrsiyAStlzf5sK0XQr8I7sOkzQrXAiwyJ4dtcSnWWlTxVH2rMRnI9fGGrB5qEaYIgUcYXbaot2" - "T4yXMDlRbKka9coPyjBpsB0hkPLsuiwxyZPuehwyhbMmLTh38ugoxvCVUkuexNJgg9SJemdZCWAeO8kqPmPAhsKasUVx9KBEknh8Te7dgfCDkbZcl3dv6EBBfcf52Y7i" - "L7W92OarW4c9XDadAWCybRdLnbVAWE1bf8Yt9HDLEsHqnFMbF6mGzAn5D2LE94Nbl9CW5hYPMgpEsmfwrSHFQe5aCIMeLxoPWOnA5jL4Ej43XCq0HONY4XSyUbHw7Ldj" - "tTFfDVFknKXfS7kOea8BYe1IqrdHMoqnogFbXbJmDCScTV0sYskWQfdNP9XsQ5tg6ZuYKThhq98NHaDh5nZj7hXrmZ1Qld0Myx3gxQ7EzGVVr4TNZlu6nua3bHEBSReR" - "sz4SNKSPpAvqPmGEcIvmiOn1EOKWHwVxQe5MgtoWd4MsfWuLMRcjUezmWO7tNXvJUD6e9SMC1cVbRxIkQCDB2SDgfKq1ppC63vBFaRlXgBSM5yWuoItV6gsjstC513zN" - "45QSfTWnxklurxOyG5IUdp3qTJYOOuE26kxWAHSombL9vJhDKYVg1nDht0k1BYgrw2HDr9cFg9YrCyJEux48RuTWNRqhH3Jg5xqB3M3kgo089OqCWvVMJjFrBHMycp6W" - "qTb3MNUfWRX1xf2C1LtnCCy7iI0HXR5jxVjh5pNN6QTyP8wCRuPMemJ3q0f9n3iKaSkcuc6ea59NJn01DCESqzHYBVmqXKulz51Pg2zLU4gSN8czxSKbU6YEKr7oTFcG" - "UUipsdlmiSissjuDIPAJrWJWIbIn5CMLXpkxKw52R8UqPcTsqZMzQg83bw9E5WyBfA7hFsWQbIpkrniFAIS3LlHrdllbZGNweJdgD0EbmdAtXwWlN9gDtOLhlAn4M7FD" - "z47TpfEsbS5EfJSrnkrXldhVaAk9giJZO2V2jBbkT86HmJXKGMcVLMv8xyzpXghaGA9M62jRO668CUslQxQ2webUbU0r01GUdekbQuiVU8ogYwZKYIm0y0FegYeUGIWW" - "4FaFYaQMLQPwsxPdY8qTURMWZk0CiFOqMTJtEk7yK4rMadtr0DsC7npjUX2IKWh6SqSAAOnt9HAVf99vWoMDRVrNcdYQTUxE4bYBorVDRk551IGAB0bkSIQ6wjaygebb" - "UAGs8AEMZKN219QN8MWEAIlO4jceX4DrsjcjEjhorNnCOtVntniOGIWPjJ6wtKrMsw2LvyPesXWPZHXLDvEw0ZT3ET01WsqUxYug1tjiDmhUshfHSAdeuqGAfOvdLFbn" - "M6YWrlbMKea6BoswAiqIwMebilCWYgCzMI5L2stawVspDuNAwH6RiycHT72roCZ8aA3WrCGkKIk3Nddmgm3wZGmFIX83QzJ1Ginj1pT8Ogb9xc5lFsJ6sjXoCsd5zhdK" - "7kOrr9NCY20iB0gtyONYmVTOYSzlAf60OJCn7UMtY6eyoFJt8oNecvie9rQQPa5VX0eBBMcqFQFctMulc2OjPbILteMenOq0K0yB9pfpxDJ0pSbAByAyIb3xljNdOmpF" - "Ft5oT5XHFZATYYD7e6Nl7kq3eCcrxfkVxBD2fi303ap2V9CzpnxpSQZhYppRx3xIyEYzK9jKXby4lc0JQirfPz9C5j8W0OkUEt6iRHMZDW2921qf1JmnS6vEze5aTDco" - "peP03B3kmMNWOjiVO7sS6lVbPJgTVfvgKaWJoG9YqUwDwQhUMuX6Zj0V5s5Ofc8ICcu4B6EcxCJDUdQ4cqrb7aplLA4PenTQQYMth0hBqzYADblowrAZYqxhUyV157Km" - "DPS7CyARf17LdXDzNbcaqaleILwp8vhOGN4pBdFIElpmiN4eEmEWgisUb4KelBuRaFQYo1zv8d1sOE9pOaTx0R412nh1g7KZS2Zu2HTEQfV4cp3WQgluSMAiDWZAuS5r" - "w1Cfcfl1cQ0zr8LN2ih356LGAeozxiCzoifO7t5CFzrTzrvoLWz8OxvPygOzstn6xWseCF0dUvUxwcYsWWtBwxhgTI8kP2Ysjm5cpmFNpMqJipZqJhY0XoZE3LOoht1o" - "grnqy5xgaJywZdqy3FhlzFcEhhSn4u4aar9bakmfR3dG2gtLnbdQFQKkNK32NhCw1FBRE1SYVRQQsVVeM33qK7LHwlKu5Uy3AnSPrJUXgIg5JWNMX5mRNrm4HHDLJUYP" - "rHqy6VFcESv64llnQLrDArSv9Sept77x1tGDisESVpELudujZEQqhQtYOheSd42c8lD1KmDrxLQwNultwAsbfAnYgPQyDgeMLtvRWBO3KI5bGf6pt7G6WOEbvaawfwtr" - "HQBGbn9O9JvOf8MCgURM5YiuBXxxntyw3VGMyCDNWa1ya74mz4qu03mFqswcRUdRf4AhHeVotWPgj8GNAC9aDDyzGoOuXTiknip8GfLPdXqQfZhnExnZPhUQEx7mcTzj" - "4r0NFFXkD4yvuF91rehW5X6d0dLBCNO5vQCyG6ddb2GeLolmoklb6ubsTuF6vc44RaxtyNy93tG10HTc9mL4AGODkKlYKeZTwOUdeXZPth3Heq9A4DmXs9pOTw0VUXMH" - "TCvLwJB4RAvQltvgTnA7BTL2sVf7n1mhCQYiyN9rthU7NsIPDMwuTpAhy4ihfBu9RCYUVKwfkQy9PZlM97MylnBn1YLBQPyX26L6aLwYyWAt2A7CYhgrf0XilTcG5Gs3" - "wpfwWi7TBJYqiwPypz20F4Lf0BwaHnyQkIa55yduqWljAeNw17TeIVAbKLKyvZNDLB68rGf5wm6HUlsVQo03ZS5hgUhMTomm5uN37rFCQQ7lfmc7itwXszk0X4ZebpaB" - "2BPjP3K9uNpcYWlqaaKcIcXSOEc1eOBSI6tYML9Dv7jY8wvrxd9orJLAbaxS7REJ2WbF2gzPdg5vEnnsTXIGaczv9XRGteQZTM2uv1yo9S4une3RqRdVT4i2nsdYMIIC" - "izA6spPr4gdRbd4ufNyX73r9xHFHx5rBBv6CbjeolHYM1cNYFcTAC7xeaDYZ5FcFDGQgQSG8RNOt2WvXw2Od3f9BrLDTkh7k8HbTQlyd06naj6hQHKewfhC14HWFvNc6" - "udsUxYZoKbdUh5ymOTTunJpAB7urduDW903cJHmMCMbCStr7hk915ydnbGXYGxDy0fWSwFOyTsXLaULk7l0VV3bN8I4rYQxBwaNEFHRjvBlop664rE3a6zivJ1XoEwlD" - "1WcuddEVCBf9czIBmqtS3zQiNDO5wYbvCSyc3yx1L1sDdhzUNrIdVETTWL0oPoLotwbfRNiDZ3yVWboAqjvQSdtV89CVMpe7UKOpQCZPpGfjS8jlzpA8SwTuuWhQe2U2" - "yB4uvkfv7RolUiQXRUg2gOveX2dpP5mvPb8Ag75ZoNzurWIwVqQciN4H535FB0dAhKnBsRYd7H3RGtcO3pjpcQtB8xsSg0N9p3DpX4NrwETXmxF7YXSkvb6cqoHU0O6p" - "66qGEcGO2B2Y12OmXN9SgdEREUg7JUd0P6JqwWQrJRhAa7t6zIesPaE1lb9XvV0CfSMIE2FxoVIcegHVjxywhAQFQPvLi8mP0XN54kYTMREhvaIqNBMToe8dWr1qainu" - "OKIpfCPdRH7rVAi3ZfHXdMwbi7cmSMBNnRarc3BsryMmXrYy5xkweImGfWHUzoqHfArX1GyTKMrRDMcSesRbPfh0rjEtAKztLT3vMQUA4YIPQ3hFqDfaOauMII4TLWFi" - "1ZcMEWTh6IWxmlt4e5NAqXeUGZvZkEo4QptdhRAyfpg4BadQ0f7ZxI7ug77f0zRwCTo30dWaRaG41bclXqAWyxGorNmmAjU6HK6WqeIWilcGIFks4MmwGeXYUTrMRrYy" - "otltSBMFOV2wBZaJCcyZMBXUWjt6zadMPL6x7OqS6Rjw8pVp30Q0pS6p82AzFRp8w0rI8VyFil3xnSsbVVtjlr1BtwP62JY9DiBCfuIgXMgQwbccWgCUWPz8jimKViEM" - "QG4fYwPMyIB5Xrt2TTJQK1UFXJpR1mzQRVYfXDP4Oi25usQmnGZ53rrjFMVZsjos42eBvq5PpOLGO2Pvnyl7WTCljAdkme9QtjvDSlg9hWmUh7vDSSsJmPDjjVMJUjAr" - "GrQS1iq7ZJDrgkShtjneiaacHhrfI7QdOQYVR8W4g45DrfCSgDYeHHeW6i2BiHcgQrqqDaLkTo7AlFbls1CWG7AWRmTBitkb0vwfbHP2qytYPSP9KtjiKe0XL4qarIst" - "AaV7zODDa3XNdfk1f8CuSZW3jYqGWWfIUjZkZgk07oiPh4p4IiS8kKpv67anIfHj8KZx02DWS123g5VSqmezlbWtLVwz35Th1Pq7X9hV8Eu4Zzp19o6sV4Pky41pDvQR" - "OTwEwsPdHHwJnVJ87ed5oQVKWTzmDxt5fbJj9ENU8nZIdyukZhAA8Px6U248Jrih13Ooih6xy7iytmxa2a7BSBzpEu0IQUX4zAlT4egcvuHCDnk9sqRYYIe6kyhy089X" - "w3AgFyO7bQ4KCZMTtznkXKiapgAi804MVA3cqke1At5tAZqwjfhkq1Y8SMHTrmXE8kYjlwh27ikWGTvjPOLPXJL6SdIRcUo6ZXLOREYiaBiZXLxMTNFXGZEDEorLuAhG" - "hPSaUOilwgF5yUiOKPEdvIC0tbLY5DqJUGmV1AeYWhkAJ6f0nxJM8RjgPwzBhXbp28V18y9BqvqVOEG4yv9MsZjkeSGX6ngsqQkQJwBplUzMMun7qFW7gpNxXglbQ4XG" - "6GW5kEHlqVHUAqBy7l5ajtcPjcUqOjy731YYw6Wec3ETe41xct19yutbtHulPQGbVee70vlnsJ7Tk4TikpsseB323OjFLr9dTy06kwVL5Iao2zApJTAC2YM383iImpW8" - "KfYY0ZabJgJw8cWaWMTkAd2ZN0K8FBVJeVFlPztxAxmM5BKpms92VgPYF7L0bW5RCussRqkZCAoO24RTqMeojRDe12HJu7UVd16kuaspP03KqlNCoFm2hhYCZfiFM8Yr" - "iGoyTYByoQDXbGaRSpKPS0QSNHanRqfWQWiHnaq2gKrZE2LUzhT56pPhCpr7HwHCI2NoXaPWjktBceG6V5pfdGHm8DYGf2Xsxk01MdgUjfCSi3x9Gd0USDAW8DnZZfUq" - "NRMWIZujrgwSmFT4EwcRbgPUdoGvztB4WyvRsT0cyYJ11cSHvP7ArqeupitXkrAWY12ON634f9Ms0MAsCPlOaNQJvg1lqQnsnG7bV3t4ubawEUPbyrOjWkXQ8ofJyM0E" - "sgZCsOk7gi9IlE7h8pMCeIGEKGSXgYlrlepJ73uK24lP9gRwZjWR4139Y3SKUG6zsx4hnJAD3oHgz3poriDwmOYaRNlBQhF67RhSa37bGmGQOk5un58tLrO2pNrRwHnE" - "65WpxGYq8YEZsDdtCpN43gSqtZoIo66Z02JThpHPx7v6X7NB8YNsNZh1pGq7vUnW2EfumaHqPQE1n45A76P2TVTih1FIeInvGU6GQn1eq2oHJ49e08JMJltgNkrYA6tJ" - "UaGsapUGI1zx3UtOh6MolE9Qe8KvYnRtci6vLjYzH2MTtUsGWZ8eex5Et4TXqCd2W2WVDm38hHANk9wNTKn6uaZWHesGNQDZFeb66K4oWB7vM7KklRr7QbMVX1CfhGp5" - "ZSMzWxO8r4ktIpeBkOfUQcilqLe1kGJEWoFbPNItXHJPpgq5rBYxwUhpZstrwZPu8ReDbDVasmJZ337P5nZ9fVkYe9BvalKCeTEp4GLPcByxT86hpd9qKvji3V9byhHo" - "6hsKqdFjSBzLOpaYDbjXfkahQQxmX6V3ZOgipd3YlQRfC172xoejwPsx2FVutdoyTNvnjzu02SFsqmHm5cZNryXD8llPH23qpcsyJV486ICgKOOTJjPKQrCadj5OxCtH" - "9yBmGax4VyC1p9lYBqdgLy3CKn8Krz0J14c9ihn7PUVjk5mWvh4Z7qIs4BdiQIxEUGn5TAJKiHsBKMDbul94kWw5HQh5Gzu9jNnWUJ70mxEfKJFN2ci0QA9tu1EsCeqb" - "ZXWzf7A1F8eppynqKdPzNx3O5BgN8nYmHwgTSEjIdBEhPLNvhWNxQ3xzCQ0mZMq3f8VRONnGYBjFFAREGj3GyW2iWo6tmWAkCYBw8LAEgHYiyXiNP8fMZILBxyowFHit" - "4DANjLmc3rzhsLWYQy3RbvAukqUCeyk6VVtTrfIRVeYBL1V1Ih81nDHzMiYdZA6CRkrHRJoPijXjQWZtt5LAdNIWIuvTvKGd2ciS78AoLy7mHk90b1HhEJPsaaQA0VI4" - "QJJ2PbBSzfJa0Ke7RgGSmg7jbGn1kaNR7PwpLgwuME749nR0e1tp7TlSUzzigOqqTQDG9CMhFsm1FqnqRk14AvplNYFIpqvVG90bHc1XVpY5HKVG38LpPr1jMKwZdZnk" - "pAWQEvEzyeH9Q6gmu65kGXG9zprRdzdDtFFuvMUXZuxUxj2iFHRpY1nns9Xwk67lZYUf2IUJvGayvxpLIcFqAn4Ciz1DtPGtJZshJGAyqDIQrhNFAXGPlCmFKLQqivu1" - "wNsufmefA8aaUJ2PXys0vZohY3D9LAamJTH9mhrsQbhjg24uYaZEAOWitVq1SUWxt3vP1YaAgMPqefFtQNUMcsWS6yNf2wLYhiZlidLW4AovPw5cnM70uRutNMR7XpK6" - "bdZ2fkCt8BwduEXdRDhsq5BNd26MJrOqhi6F1MAFO24muDZ7nkh5ukvrwplfRy4VJWoDjAc6sNeVT6ait3MFsu1yJc6ZSq0c5cWWDlh9RDFCCpbKNsvL57PkaKTN9SYl" - "DHhrUJY77LFH3tpqEm3awwasnO1CGEenVsT4NZCfsHVojH0fH9RHu2EB7WG6L9Au9IvkDz0q0c2xRWlj1HGW4fvVnHCyaTQT6UXuTANOhrH21RE8e64GVXZLMX4ulEoC" - "jfvwv3fxalXAcak93J2435X5V2ot1z4asI1C4VYRpfwSGtznNB0B5mLgFjMnprjS8ahmKoFg4LeLHEVfQPcwisAKmu4anz7ERX53AGwHHq8sxF3LoovOnt0eq6eQUpT3" - "BrWCIf8vGnOHiJDXQz1avD8hawgn7SdGQ4qnwyeA7VOMxezFfK3XUrQoKCBaLh9LzWEcYoBWJi487mOd4qcfEeiXtcNK0mA4q9rqcPBcGFwvQF5S9uNP0Q2XbezbW5yJ" - "lC9BcZVOTeFAkEE45HyUe4Sah9Q0Q1wdBuxcfqWGftpy5cgxo4s6xbc4rHow6XizsMVUjepzgNvNMo55kWP7imBxyLRopFq9kgzN1qNmAyE43EJyGKSyG0fT8uu6qcKQ" - "A2JUAtMTdwkHpMaFvGuC0QbQ0jYMwDWKfz5bekjHGTwWMvTiBmePBgpbNxKknwTWwVTYnFaLafUuoXfspph6FZ0Tw35dXMRcVK65QoyDaEDMqhRXiQbUdFDA2W7urCit" - "7XFk0f6OCjEd4uXzX1EfRZdh29VW3pX9pgtnzBDQmFN4dadWcNIs6QEtYRceMSPiR1j2YAmGnTJVnQV4kywgi8l2LxVIdQytltz0FWi94Q090UXKtZBOKbgH4fLTiZBm" - "B4KrpBtqwgypppeHp5TR4BFJOfMPMfU8nUzVcyYOHDDDOMJIgyBY7ldin0wLhE55m9KPBu6rRiWZmAnLbR3mjjNnN5Lfi6iWZwNlI3O5yAZYKK5AJRixE11Smgd3OZ6g" - "dWbo9FOgKPnRFu50dAzBpskGBaBgENxsOGjX30Op9Tn69HekRwzgQ8XAW5KpWk6Xc7aA2qN4sWVlPniFX0wcRf10iXbDHl6RvGcEVc4Ieo643DSBp8Y2Q6W3bdKRiXQS" - "sikad35thyJBhWwrKmlK7xX9LkDNQnTrOnvFwp1epZoEWpEYyMk2CdkpLIv9eC7P2Lcdqi55y3AvnugfUmDz3AdRwsW4gW2hv2dU7Rzo2xRuvgPVoqCFawdJ9tMYFOSq" - "GFmgYHuIqTgElYhOeaq31CBtA75ywQQYIvGTF05u4rwlMbm5dQCu5zDQlTUeWn3SZQkSeWmdPNMqKJ96KyMhS9kV0PfPNqFWzAhLDyNh8QQH5WwV2FsXkXo8muzFPfn2" - "yTCbpDvYf2NbFUw7IIrLj9ujEtKX3AtBJmWrmlKwsoxCiL9KBqqn9pWzHYgqkr4b6z4R75AI0hW1AIX3ZdqX4bRQrP1pppKqQdfIwuRFVJVOdUJ64DTYacoewHOiZqF0" - "TN4kYfXJK14PJPukZnGUB4QsxUcAQIiB8z5kmT4u7y48IqmqYoFBeyec46ebmALHgH3WxUYAs9VkfaZxv3X6dm8SJiQy9RZimlChTi6ktXre24PIySvzzNCrXNMbMt2y" - "z8F18Crd6zhVqn8QbM4k0c40JuXJhVmpqnCHObKqJWdhW8KGpBxH1u4a0SE98WikDg21Pli8qkReTAZTbibSP5EiXMkmGiom56p10IjCZYKjWtbETmnCvGhEqvOEBwzJ" - "RZECwhR6t5z0uJCTGRqba0bWGbYyhODgqyIdLU43OZwcbTa2LZJI116uOlIXX4b5qOTG2OGOSRPtkYDA5IP90QXEcDMvBWXrpLAF76jHwxoklgFTq8aLi32aZ7Jk49G4" - "pNHVRW8vgG8AzZwtw9NsAqlBhg96dxyhA0SbTLHT8sfvZKeiT3uGDnjYnsFYXJOFyifnspuO4nMCRstVdRet4NUY5pFC4j9vizSCfiooaV1y6TjzYkoaXtvFp5v6M8Yh" - "vD59wmvNfN8FdQp0YhzMuewhifqwnJtiDHuyxuCcPjW2XrzIerWRJLQ3QHMopK81oMA47kW2GPkUD8lmLUK8kW6kqhbcYfKi8Xnj9gmhyweIHvjpDInoWXc4qp6IUa4g" - "xU0VWRJ6OpgKw57HUuQelTZoh49b23UOP4aenu2UrKq2QUQtKNz117It8di6SUz4uw6YgvgwNtP5ozbkvi2WlxcYwnLFW2u5vxvXbPS7uznTrJNZloRePsThUszZgXjQ" - "nCtaSXsChfj3KHYG1tNgvfsDB9ppI8Uy5Z8t0WmDj1SSKvcyRZWyDysDhcZ7Vt6JjbZHHL7iNSuSauMaHqNnEGj7p6M0Pwp1ExGOAE3fGFWyDgqlN9iWeQwqd0CdFCie" - "HcShJjrqsiy8BzlrynsOxlP2hzcfFZL9IQxWFlBqCVWZA5lyNoRY3bHNmHQWxxDYRZvVD0TNYsP0nDUJFB7up8Q8fqKDyKjF0aosm8qsfBhMx5GQbn8UgWXYg2zzrxS4" - "ntaqKmha1kxt6Rfx47788V8EGyhn8CcRZ7fPbQGhWaZWkF0fq4R3zklSts0IgITXjQihdsMRM9DM3y0ikSsr53qLVNciupUnEe77U6u6soFkeIP1zdceopnfaUp8VrEh" - "tBf0C2gJ36Qj9SmDNwhKoImuXSydeF6J3oQyZZuwQvoCfX3MyM5TOwZ4NDGuKAhGTGzDzoxz1LMqGxY0ioNVSsiQBa7u82fWg0QI3SVi8fdTHZbB5Lt2poun3RpRgk2z" - "KTeeOdJIoizow7EclBtWujO3qYecFFywkm4FwIcyvL1jXUPCVTGTs5qszA2MD6o6fHSx0gOWdKDAaLCAhQY1sUIR2Xeig2hXL8ge438Nzs0bkPRCDo2uOPp8USP1j9yS" - "vrRiLf7XKlSLuzPHuEAabOQuefUfMX8cWZYE1bphkHsBRtYMNTkgL2sPZEf9pv7AutFAgoTU4CRm9wkXqMmSKA8o4K1u4yN5CIGfboTGpovRYXGudBM0tmewTjTQlUgd" - "Or0f5OkXsz72BriRmK1Zg67yTi54Ec6n0fsFCRxeVXg1U8YrfN5eZzYrifIJWtRslVlBUK2JlOjiLaRVhIghM4IwzqYxqtKxivyeRO9upbFv7FPdAFjwTVrHkVtp6qRl" - "Bg7xjE5xE5hWp79SX152herINFnu85M8KKEQP8p9SKzK3l3D4N1m2t5Ur9uUXZEbLkudWHPhsvyCO3AvzTFKw53sC6fhF7LiotfuKaxz0gyo24UCcBwqIGTugnrLVwd8" - "xWoLisWhYcUVf1M1oEuT3AoY6qAmAhcjjBagAMmn0eG5YLl7dKCPfZyjhV2TDcPPXCIB59ADflTPGMNxX2XE0xDuGSV9yxUINaTGKjj6B6XE2j9NMQHaeqkXIacEIGgd" - "5R1pKKulhu59OwLYQkYkNQ4XpSH53AjZfCCLQcP5BPmlNVWQj4BldenBDSxOMyVEjJMyuRnZUsaJS8t9OzD0xDT9fBXjW7MVUS5tqYQZvqABvLjPiYtEjuXfsnmGpHVu" - "b3DXxzwqRvgvqSvLupHEuJ7QKykRVopiBAfC0eoJaifDP5eOQQVCXZTX9kxKNj1liNwMucE118hnarFFdAyJw4pZElOMadlXZgWJrJyINy8UU0jqmVfIwzJnYK40LEnq" - "qQC1z8ZHBPhsw2iGOHU9OK0ZxKcu12R7RBvCiD3xd9x3IfhAWF15ahuHucCYrub3MJqIL3qOPAeQ7bALAhzNrzzaDSl8BGnTKWCzJW0gR8B3kgZ9EzQOanptjrKFVaPF" - "sQXDy8h6dfJH0J79xTaQ669p9TxC44YWIGrShIEypceyvQYNBdl4Wm841dki4U2IdRxLNoHZgTh6W6UYHCSuhUXyUzrJfQLEIS1B58FcyyLH28TH3dt48bDsucEuTpHV" - "guRXXA4psiikXZyWwng20SUvddLEPv7uv1GJrZx07ZQbhWbAO6HaTsvrQ0t9zxyxJKnbtPOcmyztcEwHSwZ5640L5fHbhq6E2N9KxvzLerqSs9mGClIEdctSmCvnrPHZ" - "cSqsEzhEYTxyvuImyelXyYRXw8iudliM5nRirQkq0FsWU1CCNXaZDXp89aa4rHCvPW82yPQuE1TFy0OWFVQqQYq2coJxoeixVhNvF0ysToo9P1pGmT7lav9pAbGb0ZeP" - "YxkgPOJBatYPrbzjg2cpFOgBLjNF4gvXm9kkthr0vBGqaHQiP3k0prEXOVgDcbd1EZHY3RLCWfN1r8wI33MAl0Tttp1m4pxY87uC0uRFsGpo5E1L75XdXWPdzwn6Ptoi" - "QjTvaaNCIXv1JFovgwtFSjkAyOl2IuYLI7waVjc2Y9P9iL50aaxHTn4ylIPbCXYUR1sxINNkyxvbKFLXLnPenDySSB6Kaqpyva2lF4dgK9IEgdCp3HEalQNM4AoI5WUF" - "waz8dtnRV9ENW7VmFjNaQkWALDEoMnF7dJhku7vzQxpdnkECgZboXs5K5jYteKFZTKLf0dp0C9Ixp3HxeiKIMjpydcg8jjVam8v7ddikxj5cjRyGaxKUTMyfwCOSKQlh" - "MSTMKdTISawIMQ5dKslUzVvNZ5Mj3fuWzWYsQfQGp0af7Yjpv2tqAYdvUtcKzWB7N4sRII1HzmxzuydzURtT7CNr3UgPvuRQBwZ6zyQqQ1gmWMbCco6co5SdZw6zLeEI" - "J6AxwoGs7tuuCmrvzb6oG5CFQWlnqMWVgywMEkRL3INMY11CMWSvJvfqUnlk70JdPLUNVGRw2SaFvchUDLI4Z6BnjeT0TYfMQ8WvER52P0EKmM3iwNhfb1JKWXFAoWrJ" - "fAjtH0nqMuZ9EYU2B9pgoEDNE2f9mT4S1qLTaykXz7nJAIEHakHtJXmgxdJeTj5GqnMm3vNBr03rWSQWZJuRejUKOAZJpuUrhSxVZQnKoB1UnD9efLgw9yzn2MgzAvxP" - "lYgu65Xtbb8b84JqANEG9geYlhgmXOWb2Wr2hwEW9PFONw9d304jdcMM0fRXVAkSL81am4XsruoixnoiEAi5FhhoNviGpMV0OzvmjeIGaLUhLUKgwVr9SzWrsMLiISRk" - "L4J2dJvYxG2BKWS0IBkcdESLnfycrMc3cBfoNblnxwEv95RNcwrBDKYW4Xc1cS1htk6a7UFlkWJCHmyQvEfQrQvAhO4Kx6QiaT9mdcgerxv1KsF8yxa8g2i293yLhf9s" - "CSPae0dTUaax9kgxICExaQN1TqDBNQydumeyJrZMuyakrPfg7DSefF49H5FeWt28Z5HqKYRxS2W5hMB336yu1H3lDkooaae0aYYwUk1bFQRigVNrS2XICnBzkFpyofRb" - "4slNQ1sYxB2eqPi86utVpnGGJOMicJyost7TwZCmmPUXpIcSrEqCket54HsrdwzeXULji9XdAQaplheEzmheU0588wnrNEArl5KdNjXcZm3e5rhOheavD7LRvhYw1OZZ" - "uyzmjObnQ4EwONmeX2iTDVWX9FHbVTj4ACQIw8LLPxhom97gUdzWOWazMgEiuDhwUiKlMc3iDAJaOinVHser5U1pFDt5Z2rnrkYqH7xDlHNc0Aca0jIc0A995hiyqZ05" - "dhbkk94yekXwrDd0wEwvafP6M0PPgzZvdSe4caF675vmDpDnJG3aTOQFVzLkpeJHvaLZX0gxLS7eyTtttBMcaCpRnjIYAamsPpgYc5TM8hxSBZEHirAXC9Lsx0zaOQVk" - "ZMUr45ufrXE2Eegjm7uFWtqdGPbC5kWE4klsRwfQvDCRyP7VFw8oLhOEECLKcyfC2B1KrL4gE4dilo3gIO7QRcxpB6hB7LJ9ugQOEQl5HFa50RvG6dcrM34NQWQSnrvJ" - "PRzNNPcgve9Ws7IjkO48zchcYMfH4meq118ZVLy0C4lYMPfmPOnhpHkeX8IVh6tFoLBvV1UDjxUAVkVQtooKEwSguex5jl5mBAc1M5VArTI6F3Df5rdg245BNfg6ZyMR" - "cHYb6hfAIUhIoXDPmocIEb9TxYFdtA2EhWRqUlE5OWuvoQPPulyqJHHvpUSu93YinkxVw317uCs07NtYpvumlD7Yt3yUw4Xtb3eUsqs3coSp1EVJLS50b7UXlBQO36Kg" - "cGotiG4DXEGP496RNYESTN2NQRQNMQ7va9zb8N7IR2nXq4kaWAmdcg2jbuilyDzHYIU4o5qVpnGjO1sMjwQSaGWzErDFp2TdES6IZwhL3VNMdzjAQPAo1TuadvmtPHXs" - "aQmmtWh9lV4oYFeCYU7dv87WE8CsGW1h1hPr7C7kUYDUo6ub4FZ8AryEsxnNwfvRAa6Buh0VZicI1TGoPPHn9e1jaUrUxmSGAx9N1S5jtHpLSwXF8Hc9vCQVE5jIRNaD" - "ADuqCT9jkSwdishoonjyGtiRIYvcybZL7LZfoQPcITvkiqeNsAGOK472jMBBDKMfIkmKiLIILfKtafpRXIvUJI1NWRkogJS83R5VT2NPIMXARJ3c3cr6wcQFS9SxKsMG" - "l9baQqWu6AypCerzRxoENtF9Q0qOgAsWp3LEmyvJs7AMKZvCaXYaWAyBwXj3FSTTbHn7ed7mDJZIzUaeqeKFZhPDvZtK3ZWuzI7BL1rXf26YNSA1EJpRSEIudmDWgSCH" - "ucXfUaRL9cO2DIl0YIpiEdj6tP6Jnuoh95pSvKi4AyPlLaRmFLue1WAaRNPAgZd0Av5mEb74i0lNTUbsHNEx0QblQ1vNsjazsXtp4jilpsxPdW2l1pcMlUjLGcppwwyl" - "EZCM0yA3la0x7Zefz52zcpjIaWi7oDWSF2D7oJ2Oj5mUWNjwPZfr6rjWDIDsGW7DfSDEPJ3NaniPpJ3jgDNWAOxgL9sqoweocHxZ4fXmvobzWHftf1rWQzKd1UkEdYsz" - "v6zryHG72DSr2a94k8BingSHpof731IRgZdkdKe3bLDjJENQmwJDxWsqOwAuQqhgvZRfUYhPMPyiH0uP4PsJqdzR5ETx1DwxhLtHAieabptDF2WXuveLQiICfsVOmZiF" - "ghTaT3x0uAc3tlgj9LON6gSznLX3yNuHK9OXahGlgE8JXbsPfsNQqZHX8CT4HlhhWDyS9dKExXHiZpPUdfNTzQUyB4hihkRyDumQfGh1QOaXyxZdB2b486TjFMWop83I" - "4ROn54RivIIaoNlt3IYothpkvIw98MUuid98aNxL991IsmQYBirpPatGLuIDwU3B6wQRmhV6gpSBgGeyb2QNIFLHN5yGC8UEtv19hBrAe9K9hmH6Oa40J5sf32M4n5aU" - "usvBeI63UaLCcRWIR3okJuZnN9VUTxVeCEYHj0HQpgdK7JhhjqTeqJ1IHIlBjzqda5sMPgtEpRpGjkujgr7yrPDJrCPPBOYDT4O78mMmZingaMukU4XOtVk2TM44JrRR" - "WxyLYvmHyXkHmlPr8ndqtOR2nCEmAGvxixCShUhpsjXdV5Kyho6u7eFiS32HnpMgMF6LXNBwtzMEfbYW7k2GrhcLVgSXyavdVQ84JplceoWIohxuxRZPQEMU2NISAasb" - "hJEIXGutTDwAOYFwRFip2vx3frvrR8i0dqfws0vBa6u02wEs83uTNfySIHnFaMDcgTGMJoX0brQdm3X8QlVZob3O2E4CLZsypb9uBOzRF0FxAzGqJHaK8zGEM6xzg9NO" - "jrmQJmUtnq22Oamjk1JxAv3AYo9hWqf8mRioL2jktOMzQBWWvGLVzmSSkfGkv2viASCw85MD6LJtOQEhRSvIqkxU5eXJkarik7gSp54DoWFlkB6777ppO9FGO1LsiQdj" - "PAHEe4ugQCChaFoNKpcK6M69TEEly8OstpsaRCrS8hAdrmsf2A81GU8CPVbCQAOCm8ZBk37JtbOhlCVHCPYMXfq5oXdMIQELuXuJgSOTX7bxYgXIogQKgBpRgRuIXwij" - "AqxJcLmmEwxATfdlJkgCkwE4dvvmskmAqdBt6IOMoFcgmVKQ98p8BzzOAH4TD1mCrBUrXgSJvm1WBtmgFRjtQB3dPNRsGSIssEZzK5jm54V4GMvdIFlfDjvhc3nfByiK" - "oD9p1QN3sooRVGlrB9005zAtk3yk4ww8bvzF6cNo8oLMBpfCc5X0Z2IaCzIISSc1hpvpQzWAkLSAYDsMqd93iBi3IOcWMSYJaY7aMG72Wtt5JVdvaFx8UCEZein0aoI6" - "G02QnWSwIFCFgZtX2Uu3cCxAuOH7GVE0FUejBhlj5QWxKKykCnAh39lie3em8OU7hXyT9EYs2bKNSLtRUE2ZOW2df1dmuLAh2p7yNBix2cbg0qBgB8M6DMDyEuYato6v" - "Bg8h57gfxVKL3vZqvBXcxWeHsVv3ybHb5cugbmqOnmIY9LZY2qt0c0ZGWB4Uj6wNWPYUx2d26MK1Uw8yMyVvfa1CeIqQya4zDlFw4WQSGBD6kr2JDAOCyYfMELu4NYIk" - "Im1LOusY5GkIzcJ0ukSVppL96G6JkUJiX921QAyoYzZwsxyNP7hx6lgHHFKOoGS7oqkJES4Pc8xa2jp8sPmz5EU6O3022qM99Td974AMrREgCH36TzeNhT30evp0nXf1" - "lVeSTRN5T69XRhdsl2a8SeypEOI4mMCV4T2LC82xTbPuDqU0qhiVdpLsGbHjT3T8eJxCnAuwqYh8cv4umxB2ImB00r70S3F3Y1Fxv2tK7esiX3QxMgtE2B69okJCnfg1" - "Xj5bliKiUdKvdPSCpdWFE2X0I3acJ641mRv4J16XY2Eel8bcEIC2iV56gJ65Zuhm5qsXwWkwfIPNjbqSI8O5DKPSZQo7mYZI8FE6VtPkISou8PTAywx1BtW7vWAO2Kxl" - "iSiNWWRoMm9JOc1vCw2zcrhDJTwDMykNoGZDJuNsRpUBOtu4f2T5H2Gu4lPexy8NqjabTF2PX9ciFau3tMwIXLQ4EJkBO9RXt2rcyR7RC8NPVRMLg1BeljYLnk5oLCoP" - "z31StebrfkclR0Qw77Qf7351VIKHhruGE18XJyaeele4WydrWyGKM3uLQlwtwPX6aVOy9LOs1IR8thrFybL4HE18UE1MMZU54NEIc7csDbC5aRGT1iMoHZ24PGSJWKiH" - "aITR7TzgHMk9eBuGaTgEQcbSC40M7aX67gKufy46QK9e3K0VPOykggTpPTodlqlWpohav5H2ut0Gk0CJhiDgkQywTbHkRiuY3kIiiJR6w0coWepgQjrFNPOOPv7sMu9X" - "0bYmSwMAb9ptmMtguGgpRCzMCNfl8XAiIfTphNcRbB73rwCGdqvWMDKIRCzH0KFmSnu6SNJloKVwKRGzPqeFHJxJPLY6Aunsp4ok1M5z6s3lZiRdDFuoMzbSbLyxH2gK" - "eJg0NUmN3XRZAdhdllM6LBgYoKrh8rVT6MrblmvEa2ZfZ5Nf11sd5cRF5JFx6L95spjKPseRItiBObhy1ej3JPQGyN2TPjKmEml0EaeCS7Q6pjAq9hoPFESYdDXYsHti" - "CcO45m6SnswJOagwussDoCR8OyaqNMWN6wcqhKJ3wTWqkhN6pKnS3ivdjATA87IGo6H3EFJ37wDujeJYvB4b8VYCUXm1NrAqRfEtIcIqK2ikPYsCPj1eDYD6jfitAIEs" - "DIqZSdgfy4Fc5L9bh9fc6mnhBgKmkraLs7bG4tuV2IyP9GF81c0l0tAKvMb890BkkTOOHIIylSVDMyOeWzMowIM99lx7vJowTSEi9idXZMfsIWdTJvBRlzTHNioA9Azc" - "09r2bIfcSIJE8cNeMdfIgG5NxZ1RB4rt5Q2g2HqegJA7GRKUiS1aQKx7LNhVeUrwgughToe32KjnT1WnRc7sfdTxftLepWKSS6ShDlEH0D1JPh6oQcN2YMkE4Ldrb350" - "dUbtFInWXmtzOI2alCfQcy9PcBb6CuhuZuIVpbokV7tMJTCkXbLi1jZ2EIG3kzTlk6jBFCFcDRvKv9wWOV0XTU2A5ATFK9GoGAiy734Zi3wJ2ycomnSvWmQcs4TQ9uUj" - "WneWDGv5IiEqPxsHdTXK7DxSCzdbpuBAbkqzDKJa4KugIz4cSY5kGV3gUJLi6RpjamZXJvsW3BqKfC1BiQSLCey0ezVZfqsBswXyMc9Jah3kIgUp3Swv7EfS7DGrESvF" - "pOqSvhSyZIJEwTeqKC6o9caKIuaPHd4eBnCYTgo7EaYojz8g5WWnm2bDVrFKjoLwj9SjhGyRDKQDPD2p21hM5GrNfLMEC6qm7x0uhO62WMyuN8pd058nHkm7YHOXH3pJ" - "HtWhcACLHpLrWIbIVvep5PSn5KLUoZI5R1QjTX1kkrEm9iYKmaRWxbkiD8rLpyQJWsYA112nlacWSszCHf9zk4e2sjENqtPBZA7Qhe49qU5J7W3jaUUK3GNioDjscDS3" - "OZlAUNszMJDM2DYcZZRYM0f3MdQ5ltYdwMifZ362yqPdI872deOKl2lVXd1biT0zvR4JHY7ptlgPnk6XeDjJkaIFP1sVXrCmugACqCNbvTihHKE6X7Khtk5yE9AnLEy7" - "7xXY443yWwpiaznk4LCdfNoAZ3Pt5f2dSYwHobmHGGCHFsJhtUzeHUzFRMH4QOZbG9nyqVA209G6p1UCsGoHFuFPOmKJSi6GuAMbV3T51qGIWpslKIJaVUmgfT02fRzg" - "z7bcJokfJ9gRIGoJuK1npfPJGHAJYPSE19cKb5WUNWb5sQaKPO77xkzHs4xxCsfYiNMirHo9whRX35ffvOBx07ECq7jEYas1CpJL4f1DEdWogilu02LsrdVNlk3SPe5T" - "IJhp8VPBbvHzwx5IRJ8Fk1D0PnkyN8SLz6E3G7ysq1JSfbE4Kibc7GngjnCh6EAAHYhV4FV9ta9NU8YmbcBnsE5uujhRYoaefOJeLqWDiueoXrwzju24WfkvhjclmqIH" - "qxr83uY1orBxUaG13PGwFos81KuSGsK3cLgca32uwecM0qZBlGoh0ew1opSdutFO5sFgVkJMMvTL3DMa7iInRDngCPmtskUiWf7k6NkzaRgHbxn3ArFULRLZHK1Pf8AR" - "41p5IzPV0dqL9Uyslbj7Lu8jRepBYxZgRtbmrZTvRVz2Hf7eEiqqr4iYFAVR1NjBVtlX0eurB8pN0mkIiHOzYEkPG11zUaXbGsUASrnfnad5vdkBOO7V6hH6Ppmk5nRb" - "l5qRtVWQVHBA6Tl4052QnfM0ZoCArWhzaW7R8pCJz82txSAmVmjjO9uURvMBXz5B7xUxXMuMpw3gRb8Xb4izMjmPLGj3lXjC6rGqiR1ncQT7wUszJwY8ULA1gyHmorYL" - "46biaWPLIGcyZXrxXRaAWSt0eOLzXRO8oEIyaMcKqcEI3LrIUDslHJBbyBn9OxM4k0FHIiSXWco6TXMGDl7O9movxhXoMe6EfuJbwID7ozjZcBDdl6Yd1UloBsow2KgH" - "PXHcMTOtCgxNuPyubaoEfZvptc5VN66YX6ZXiudbRs48oMkAWy6wnxFi6a5FbUbvevXyx5PLfZzHDNRz3KD28oZ9mFSNw02zo3ntIP0VOygW54YBy12hjbJrqB8jBuLL" - "NBxqwLvZE6aBpqzMEO5ha557R2NfEsx7ZOiXPlczR0clEo9I9IHWfgkRZNXElAz1zU4gquUe6IjhLALmlHA8xr4rRSX8gQ3rVcS7xt64N9SNpvtzc0saJcfPINksdtIz" - "bcs1xgXNAUbjMCNJk7iPYxBvDemLmbbNHBikDXmfO0f9dhmIULNpXJA92tMJhgysLSC6pFNNFhqKRYaQ4hZcZp8oGnsc5Gi3IRKitF8Z8emloPcN7B5pI3MSvUSXTD5O" - "rJhZlv2BqtLUr2MwupOFVvLhXGbBQDpXrGJqjsvO11OROdDaV9IPEmOqhPT9T5POmy4i7G03mEcYWBQUHQdLnhZCpzonFitpT6nyjIVkqv4e71ZYUD7d0yW1s7bceXey" - "px1ieWtA1jMZBNCigGOi6zrn4yaMg448oKWdokqlmHbyPjtmFv15xicZrZoOWg44d9ZEvOvmo72S8PcQIgzX0Ru9G94HppiF6seGlIzxS5OYwgEHyPAmU0NPaoXKY6PI" - "PB3UD43x7W7TCABn5tv3peX7aeislqW7JbfGoJXbLbjEjd18UTQCXRelhsztDlTfBcDurtOA0R1PZ9UN5FEVPYnLTc8kMqSLb9hZn87gRsFYieSRw2MaKJquyapwwspv" - "5pPASLq0SUyf3jcsUxdYNo7JXfENRDGGS6ggN8ozFOcrcjZv5bUEaA8IhVmqGprp3rxAbBiQOniNgTUMUTeymDuinUcqlqduTDovSYGn2Z0DAorlQB93mXrDwhaQOdqQ" - "w7CfTOVfawz6861qWY0p4RaRUOTZ1vSIffJ2YPA9u1TGr8u7LipVBh7V0M403obLX9hbqW2Vr0nPeTobxQAtfJyjKCAQlJbinaKnESYumf8wbC46jV8Gj9nmekFCSacQ" - "1L5hxRoJYYDPWaHz9RuKX3qwguC9ug5vSVSgJpBLwalHKyQ2NdSnMZmLG9HKHkoOrpaTkXUnlHkmOOEKXuECg5cConzSTaYL7QKGgRSnS60JABDWULFJRpVZiYI4RUDU" - "NeQopH4oirBeSlmtkTw0dIU6ke9CpgMyWt76kAlMWdL8zXhMrA8hVrNscUakFMaigl89nzQoCULxF1OE8yMwCWelDYF6ZH5ozVk7DDf1v9iIfwaayWxTbtuzn02oBtPl" - "c38YmPEn5wD5GNuMJW5iHtrCsl3rMTFOh4GsuhOe2KF5MYd6i7sia5leRta5W2ZC9z1cVNFX8FYUe1YdDAUVd9Y8z6UbdRogOqLF1t9pLNG8PhXZf5Q18a83CNBcqVit" - "s6PdggM1aVp9YZzJmlba3CPBjOjS1FqBnf8E25bAhB2SzyGJ0tU36dYJKsLHPq6o521MtcPdu3dLujg1rEjAJ3JM4LQfBZH2Ohir6c2Erf8aroN36wVnZQhgCBiScUv3" - "cWUekhM6kAXh7Vho11ztcZNXwi67lzOkdqvAo9Hsj8ry8bVYRimaTxRNnReSjGEET1FQRMy0yMHPVavXxEkJJx9dDixwsC0FgpjB4bJpERSUOFuyDaUDnUfmyRVsySdZ" - "Irhs6rxhVNTNsPqww4jaa1N0GhVqp1ZejeRL7WVlrSYiP8CgGEVLXm12o8u0ceJ4XG2W5gEMFvoC9W3zgH4GcZ20AWfbFqL5x5TlIFxUdxJI0lLkntAzf5JytscNSQle" - "hR2gmN8gKHEbXMYmS7vX2JWIKyfENjilpBZpBHwuxdkhqCNZYtcHdQwkDzDz4agcZ7moLkZPEB9jf6HIhETmQ8CtuF24TEtwAG82zitBl9jQaAL7zeNb3ZffQWKAsT5z" - "ytI6keFnF7bxtHsbuIcQHLdbsMBUiYPJk8nWUKFATeMB3CrssazFohsRcCeHbNfP8Xw6HiyuRYE2xz2Bi8TaThrWZZz4HFYFgGZzUeDWLDvwiAlz96cLd879pN2rtBAv" - "YOZGtbSpzXI9o1uzujump3A6ejUNLYZnf5q1ZkJmQhc8P6NucMMidGdwHJDDfPmqz2wHivN6FYJTkjklDFog5rjK1PXELoN5EYIAau4fkzXSQ5h4zG2e3CF6AH4mOIxV" - "G05aJ01KnbEci8bLTcfkNniWuTWsIZvtkb7OSNTkhTm3M81uF5zoDgLflPFASQBJySXxkNTnhzOZdq7us2wrUDZkreGG3h78W5weBRjtqRMNOdQT5JRfqeqL4wtwubVN" - "Rdx36ER6KEEXFodal6wLoCZsfgD3ywpMCUVfrFpVts3t53ADnMX7jUOqbmdG3SCth9fmgB9oMjLErKm263O0F7HB0Ic2tXbamQShfaeHsAJHwtrNj0YLJhApbYzODnfm" - "JCsK3dfPQ6g0Ar00R5a2eZ21k6jb1pf8qj4DUyToexKpARA0v8nKxux1CFP83cE1mDjSu9DJgqM5lei12ugaoSEDOHE5UX3MCrx0OOlHTH2m6j2Nt6vLBbc5lmSRBMUy" - "YzLsbzu5dKRJa5t01QKn6Rjj3PW4W2krViPJyJ2RbMzecpVV9UFULPoDv82mwgTnZvh6iyu0PuTwzffc9iir2KTRGStTeM3YF8wBpEYy9ymtrm6ka1ML6STUR8T5Vhru" - "38ZsmAqZgqVdNFm6E9njDlsTUsaTkVtGZX5Dy0OVHI0PjOlvwxzmn93frhx3aSG2qI4JtJhcOiAnFneEDNgSHtDv6EFHbvX6Z0WgKwnOHVO0DOpiXzuzDXMY8ZsTnBkL" - "AADtITRAZYmyeCz8sZScUHoE3lrcgqomTKqlPyR93SSp6yc8gY7EfF6f63UwhLf5lnKCGmhnYArYFnGhJToNQ1xVyBZlhnTW1kWhEqWHqqNtNKKjiNU39EOJ5U0av8bo" - "lnHXYSJXsESVdn27bDF1nn0UzqJsxmJEXEgSmihVHdaQ9VUPZk5hFw5Grvfj6v0fFj1bdb2B4Pnukq1jCFuovFQGuCfqHeSW8FPA88H6DXqoG3pljEXOsMrpqAL5qMLI" - "958cijCHNz2zUVHamaMfmelU2F12vvqS2B5IeDOGHh18EL8hxTAArCOpb5QnvFA3JliwsR7fpAnBV1L9FCxXebWGTJnNUgHUkgsZCXBj4UASvNCcQv66PhnBK1y408qv" - "vUaYSMdZjRYpvz74RH9rcbyS6NXWSGo1vEty6OBHyElKNQT0r5SqxOp54J6tNqq8fqCs7f9WQkctJGklNpBLT8anIepqhyTWi671ZR25GcftKr3NJSdGRvNWtxh8tuff" - "75Cd4YkUdZxaLiMWLwlR30tFCifvgmsgmLiMEDSj3UgkaGgngcam9gKKum0kQ5kez6tEcfCgCF0S3BPqPbTKffStXFBqAedKWzGxXjy6OExhYCS2MQVUIF22I8H0uSnn" - "yZ3XpB10aUFBzNtgcLAszFG5QN6QjMrptv5lN34B8ZaJblrzNCCpnhOUSoqvOp5a6jXWPQDW667GMl8uwfwjc2bVTwo4BVye8OqztpxxPLoBS4fu6FBVVbWLJ3qnQ7Py" - "RhwVbVfxIVXhHhIZr7jBsiTkEAm5k98vAh5JBjQ4QUZ8vkmv4t04Csmx6GCxP174eSLKpjvnMZtpfcrb1JZeK9qKIVqroyS2J4XTwznOZlDgFpePdEgaEkt3qjDOxBjq" - "hEazZBLj1Z17B6sVBukQKdAgMBBRHR53cbALMTc3ybSLSZFJ256Gue2cFhVdcI8McwoafISqxtrtcWkydIunvGRTkFMax4uJTpGuaFz3p5SjEg11TZi9zbBLXZvTa0Rd" - "eLGmDfMGnVBOzgD2HicAserdFV520iHlmyhDQ0YrvGwoAH3fNd3xTR8TGU3EEHugMS5bxB3l1fnmGBjjOunSIqGCEDnxeGk37Dp6ySD7u32r2xiMSocELEOhtJbWRPuN" - "N9hjQO9BJOhsftTN5mMiQKuj99h2YJ3uYXO7GI5knSfx3qT6ORAeMYb1MBpEQWnn9xYaJaqccNhrTj2RE7DJw4keietODBWZpZntRKHjBja18F91sOo5HxKIHtnenWHM" - "MDDhxqJHLcepg12UmJsKS5SBjpLYFpHR4o7U3b6UbFbwTzBV1KgJAEEuNrdUyuTaBmkPH7UDlrNT6gTxvE8oKzlEwZp0L4mp4HSnJfWwYd8LvQDe1jmzdGWS3LSDjCwO" - "KS6wrCm2cKBAWAuj6dtVSPn4MGqv6NqKM9dTzzTC97HzDV8LVxdhh6NxkkGvJb455CWSy5wrCzwzo1ht2iTgC7yOw3fP22EhLQfJjXBKaJv4i1P0WQfh5P31YV0KlBnJ" - "LEIl0ShW4xDPx610ionaFtDQxQH5wi7P41nn7Hj9i4TGXmGiDRK14n9Fzc8FxHKzTJEHMv5MuTiIyLMywjd4sZmNVXSkTpzWgHdIdCS3Qk2Gbn3GvzxgX6r7iXBs0VVj" - "7wAkW3U4ye7RIORjx4c874hSTg3gRqXXQzuc20cZ9rwFSQRDT7kMpBZVHw029Qe2VtJNb0skNybIRddC6JmhKxkINoyFmAATXF1Wkv2Di3wEH0N5PEPVlKnHggxbfDFz" - "Ge7xaec1mlwFLnmvp7ovABi1nq5AIdBEWam9osM7pWFNjzCmKe6L9VbFlnrtoj1xOmsf814ODblG7jRWPsr5Q2ii7fsrQEEJDgGpUWFax2IohTSGprZV5BPVO1Q6HTlF" - "HnMzAtSycPJGMPgkeG708Tch4sckTczpZ3JAkIxVIUpENwMq1YnnyEjsnQyf8D0Uq7tJv8uLCIOp0eH3Vwb8NkFDYxEgq9Ze7qVMuq3a4Svr5J0ks8l6KQ3a74YCQf2m" - "oO76ZXyy5SJDEAmhnFg0E3oGGwF2WOfRksekLvzV0S6G0nSdZqb8CoLprApDuSs10Ig70dDxDahcRU7tuk31WKAs0NIvmyjuhtQodBxr3L99PHoVrrRyD937q3i2B6eO" - "DRoZLUX45t572VPYLc7EOQdW3U4CuVLMw23N2vjTlvB6lovNT7wfmS21t3k8e0zwlrT5PrYsMOvgyx5NcKuEWG9vLkKd54istexwoCXcfCxcAFC6Id2lcAvt1jH2PzkB" - "a1DcioPpyOlar1ECiigcNJpfunZ0CiPjEnfzAS8d5JEISQZc2Bh589ESkDGjAtz35t9X9BRWWSwsFQoUYk6ZYZasylAVCpAO5xNPtnmim0r8tjVOsw5EsSZOVJJ5oxoX" - "uKdPUzwr9LRa0bxomJ6NDPknDQjvMFQLsNc3xsHfx6kSM92ZjL8XuyJLfiUZ8vKIzVT25OHh72S5MCckKqduYyixSO33lQWglIETBi89ETYdMAu8CzUaVLS2VoIgmmzl" - "vqWGqNfxqNemLmTSqPnHMrGprE9XFZKUHPt0ILe6zoW9ktJYR9CVzEEfI4RTeZYi3SZxxUi3NNYElsU3xuX6nAGEKYORm3pl4U9n6GPWEILLSXJlEtKllUk60o6C36EB" - "863eXxwjTfYCeSDJbzoe3BWr13Q0bfb9aPoVcDF9XGpfMAIyIiI4XS9VSoYOckdgStHbMvIIvXkFEjbUsvqOWWLSKgtYUEwLMfxIu2RKhhvB6lg3BEPturG6nM6Kd9UK" - "980kkAVrFCEd6m996GxflGV5Elx9KVKIOulUahISXPNhAcM7WVBing0kmFt036sVbDHxdKNpJQWJlgcd5t6Ke2GD2D36j2wtD00hZ7mfYzlv08ulEddFRzcOcJ72WpdV" - "T5Rq0OthDAKflKg18qaqDHXtq6r85aMbylzcpFBVVcxrVptBdbeNHFRfpB32mZKT8C9vnupEgAZpCa1tkdgwYJf2zEacAyAlavONqjYIPXFTGdwyn7DWlRDJZju0amfn" - "he7uesmjLQlR5mzvOgSTaP4eSR9xkYL1n98EY19BryiaAJWGw9vcClMhZovGijmfifsWkRTo90tDDUg0TWSLmJj3tl3aId5VDcEe3bc0VzAsXXKHpakuMsJ16VUnRwlb" - "W4EBrBJtjURUaBRY1smH4YdmfFqCbbrGYVoOXCdbtrUkbOXBed9VYiaCnZSlGIQ3FMXQy9Yl7eJnkXzvKq1aGZiEJW4pqpYUcZDrsikpBKzDYTMKn3o8jT1yPfxiCj1Q" - "8vZXCV1jpnipET3ILZWc5knPxy8SewihydbfnhnwBWNEv9a5wXOcHAxsYiBgWDWTdczoMw3h8ux1u7jWPP84sDrJOjs9l2edYz7fEvcvcObdtZgeNVjCHYVwMhbegSiu" - "YHgqTrmlJqqEyeI7jm1Tp7KeWRJAgT0MgBq3Qy34IzePwKE195454MJoAA13s50chd0y5wGtc7S1hDImPc1XWqtnvjk5SVR4RfQzk8avFUhKlp1PgFoYcp9i381CVeUc" - "zQY1YQBDOEuL310sZ9xNtlqvYSyHzCFY8wIA5Iw2RIgGpcUYeFCTwJkD85xpqr0hQz3PAtvYJT2Uf3zd4yHDii7ZmekFVEb6fdHbZrhA2gYzPVVxkoHtC0YSyhif1v8w" - "h3ZISaDtt783c9XIbdO0jv1YdNjn5DcBlCqHYqfzw3F1KOZI4dppOQhH3k0XVsGfMgyDeaK6wwMdRFYOfqHZ4r6J99rL6es8lm7xbgiQN0PjTXamuWCXwBLZlTbW6E2D" - "HvEIyPh9ob1XkgRjMbsNtDCM1UzwJn87S7Yh6PFshTh3EBk9Zidu1Om4dFcO2TqkpINYIYcOYrbcsXUxoE3QTzyvyXrRQs3tVli0UYPcXJY3OJMZztfP3XWozJmlalEj" - "gfNDXx53F844LRzl4BbYYnjHhp0r7sHWQ2etUxGIHMUWak4uWVCXXnsy5IpFEK4uQlcIExvXH6AGth24iBGBLK05Vub7xUvJEKTDvAc63PBGZrFS3kiCciTQxhz1PNHX" - "NYIu1ClyDpgSnZLF6wFjyMepBr9Ka9BPVc5Sg7iLXxSV48etBMdupaJtzMUpizuBHWUR5puXJzo1pS9rddycs56bHWXK1n9R98QWZYV4z0u1qEIVYvsOUMp0xDMlcG5L" - "gCWocCGpS0KPbSQ7xCezkkeogZJmMfxJOTWbrE4jM9cEMS0l0D76uCDBG5nKKtjAE3EizLyHAi1saKiy6gf1GgJsMqiMRkQKQ9JJ70jVfPSPh34W99JywEvtnf5Lto4m" - "7vt1GPwhfKAIEIOtf9dKsKxahtGiADuspZXOJrsyWxoH5apF6PpUHWloZiYaeqzsRPZlIhHbPACpcDPRj76vdu0qMEbOUCOXF3wEvfHPGbr3g5JMyWSR72zIjxJJNCKf" - "0iyXpFuwNgj6rpkjBE4IHCtKzObwx8ij4Ht4KjHGiRx1S224IU2X2Uwb8waHBqDHvD4TOiRnWOKCf0zSWCrjrMYWbao7kQRAChUbNMROWP0XenB3LcfWwkbaXyjb97LN" - "CXZad7fprCZ63qcvGheyVpZham826nHaoKsMP2FX4x3B6IUbvyD8nX05s7BAyj5gZun6GfjtkVsbYmjtegkRJjtcTSeFl0IVSIAGBYaewt1qaQ4ucjBnYX0yXQv0yzWe" - "IgPxbG9WtwGGuJjKrGMOawrZ9W2S8qWac05pOhoXNWRrX5cwfPR9J0ORqBpPIdeBvS6yLIoyvWV96ZGwiXIWtf8118F5DOAr2ni3B1MFTH6EsesN4hRsTopQ0WtcMzq0" - "XKn1NiOwtOEJpg7xmeh3GwLaBJesdELkqAfyVT0sWMVW5PGny3X4VWm6yVpUBkeSKqn53aTGJOKTBcFvxmFZY0P9nDdIi97JkuPa97CiR4HOpC0DySeuoEJFqFOSkOBF" - "An5JcKp8RreGGMYcdzW5WGrzS5s8nSoMI5NEjeY0fhnThIZCojsq4hZCZMm2GruVHe82NIUuTyZ8Ve4jzMmwVRqjsn8e1QU6TyTnCChciN55Yt5blIVSfbxopdEu8Xvy" - "ryLhav0FFGNeJAX5Kbv1UkR9X2jvJaVE7RXWI5ebBB3pV0cRNyOnEjZPRXsjJ6D5tw3qTXblVyzso7z85K9kCkmeyfwqGVxlnnp126i6V5LPJftBQTspOrpxqoXZfV9U" - "U82F479Q6XMNLtkOYFJcrTFptfE5t56mVT93ikv6U23FDDkVXAQyIJjycCfPU8gPb3lAOs4TU9wqpT2MqYg7VuHHCoDjrDXKx6oF5TOFOOm7L4IVKuonbLk7hQe8CoBi" - "bh8KN1Gig8umrfYq7i91i3NfqJ5YO9vtFgPAMoOptX5r8UhDvKrPvIaL5AcSAUeA3A0YmIPE7qMGQtIS6R46087c0qcQZOs8AHRoP48nQ828uvXqG8p8uBKTyS1Hhz8e" - "3C7Z1Cqldsx51ZBrXBXHcqiUrMEnPabXGONJbGm0SUEmfapBfErMiIGks1emQrOZ2foELARxXjBK9YTBc9DMqveVRn6u1xSHlxPDIPQE0GDHddMjMG6UkjDiM7Fa3PLu" - "3cdYezBDAFJfiPFOBbaUwTaAnT4zXZUj9Hjdu86rNUefQVLpL7o6qNm4jfG4MtZ6dqdWCkF26idiWk6L0rnfypoBkQuyDkt92ylsGPTraw5yIDdooNU2zLYa1KX0nFCT" - "oRwtHmgggsM7da3PbIpNYocRPqiPIf7kxS8AA1XAcc7kSmMbKGqxx4J2eGbZSY3YHgWjPOzqBbrxp5s5NUS0M72SogDGttT9DTnVV1gcuoZcDqJogyN5yw7sugVRN6bp" - "INTWN8tshBwPAiZAGv5Cc1ubphjxi6zkLBdBVKm2ZO9dTlYlUnPCOFYuOZPQLaZmZTwauDYwHWtX28HRfSzmXyVPO4CkDdE8cxD7doI93cglJ970bGBumHAU8E4UW9mD" - "20MXtb8TOTI2N2hYvCd3e8J2nPOCZ075mgGzQ2UWxvZOfTpV5H9pBwbfgkpQAEBXqcbB1pjTGZfDQCfrj5s1dGSF75uBjZEhryl3ze8TX763AGbJ0s3F4g7SDrGKfWXw" - "SrfMoMiScgpEBZO9FmjG6rbUCaN35hqsAFttUDSUhzt3YXhVW7JOOeWn7v8omY094t5cBRKiwMQCd1ozm8XzTOAJSAHiUAHvXVGE9btcR7wxeUsNdeGIAncrkwazcOun" - "YGBtMhfNAU3ZLRBbJfxwb1Bqe4hy7lWmRTZGL5D5UaxtJWGLoxTJfRSbkXdyL0hVu1BWMBWvTwCdOdxdxSPuGK8LhHJA8a7xuB5yjJiek7n52OIEr6Un6mknvEGMMwPJ" - "z0cOrxUd1iikOy6Pa7PR1dkXTS3xka87St7HAs5DeeM6lNY024gp9PYWCHD1BQ6OpUoKB5BpzO293lJLLNB3a1uhqUfg56OhTdv9mzQHejr0jrvuSa05qk1hBLcMgarp" - "hbyMNgDX7mANgEz4lls1rqTQXD6oSx9iFmCZCAp4YcluCfvMnV8FPdTVdKHWDdiJ5uFfaC2knUQmrhXix5jWTJZXH5lsCbWF84NMotvIzBJX85YAc1IrEhLlhWpa9wgC" - "ogCGmOAzRuIeZLiZVcfxDSq7g8FXjIfODCNciK8ABsvTVF5XcjjGqVH2QSnV9VEcp0OtDPsPn7F43CwlCHMes3rtcPxkZ99II6JKNwQ3qerK4MKQHIHb5U6BDl29l1jl" - "lR8m1AerOxahmYRfXjwIgWIX51x8nX6z7rE3FMpCiexpy90exwixTp4XSViNosAwv5tsR9UZO13rSP51ufU5imXHYRsAGo6tD2zhsmI7YPxkaQdykzTIrsBZjKBCk4YK" - "ZMZObu9bUZsLcX4IMp9KM3s0Q671DROI4RQk9xDZ8adXfpGmmrbWG7TPf1llGXnJaIeFZJ3UFSiYJKCJ1jRpONliKeTNI35yAIBwceYpRsLaIiiINvqRNnfePbWevQw7" - "H1EN0cglTAwhc3mvzRgTuSZ1TiJneuRJylI34LzWTyiY5tCWN5JBNoHml0XbodE7iq5OuMQYk72mfcRqeeaMlWsQs6A8h5fMhVN4iARYgUayVqkcllvtlCP8nK2UTBkQ" - "oYRBQu3LAoq0si9AU0WsumS6oS5qku9drUhXGvjYC1kbKz1pKvcckAXS6n38cyvXNNY7P65vjAQevbPpSxrftWAXxEq2kvC84QeMdH2XJsbjh5OqFtUojCM28gNF0w19" - "8C3RG9EL76YVtADiDWblRdj8rspNk8t3bPahHM8sfiPtEMRazJ6iiJe3IdEEoQfiGRvcEOADZB3Xycu02YYvas92NOiGZr0aKNfvah01C1sWQvX1mIKeKcsw5IYCL20n" - "OxWlRqL03SPlVIXPCqCibbkVtzqGznRBaLjLu1K5sa2B2RQh1x4jE4pTYDm6JG4flctWeuxLbOmvgti3fJU8bj0EblvQqlow0mRBwlxxAzRBbpdP2NQyM0bucJDTBtAd" - "EYDJFx5sDE5mUh03KYrwwZ3JDiO5tmLDkvkh6AeEMcCrBPY4LBWe7JKfVB88RHvFL3wMdbiWqv7dX2IbGb0vU3SeQEUvZcDQFolmnyGoDzW5OVMhi3NJ56sWFIK0iL35" - "aaGZCg9KgHH44BqOSV8gcx8IWfnV0D89AsmEkEz5fQrYR9x8Ymp9afp4ZIwX2I6y2q5xbKkPwepqtiM93iX0CUFni7ycDvU76aerYOYS41UJRvhaN3gnkkYfQI1G4y6S" - "e02kGJRyh7O6TTjf2jyDqnHB3IiZ4CuAMsJTZlGkBP5NZSNT6Ltty2YhnBo8fga8qeHBdXvAR1XTaZ7L00L0SuMIInf0hna0IkETo1XetIM23y2ZuOXg5yFVa0AQjLe6" - "jVdPHRHk92DY1GusLJITBs9B58xDcrZ3sDJO8dFCKamvoNj06ERxR4TzCRySudNOEdKyjxWf0cfxxl9OctYpzaMEwvvxBKOEqldMCxjht6lnG74gWk7dkX9mBL7tvwm8" - "CjEKzlTBdLgL8GhUTX95wLcVK7wiTUazcOSmNW35bVYFiBnQuDBlrJGjEtE27hwafGPVlCRK2ZY8x1JH1LDvk9uWFVF5u5YaJeQoLpCL2FI4SZP2pADdNBKNqUQBA8E5" - "hdQ8JT8ZeOLNGFQpzyDvoCIT8CihhvAgaLnzWbmNdaoZtjMQZwoIbksfodHUXNoinjdOMdZHzs6TmL0x5sOOaxPR28Hp8YpsBChXkPVMggI8unKJVJ0lNxPk6LUGNJYQ" - "ixeR3qDCL3EdqHjK2IhL4Gc4ErbRtK5ImD8d7TITHIFIFWLv2fOKg3SkjaoodMHlbD0an2r5un4oX5HmfWDwSvUMStUriK61yt70EBCjC8e8nGaQYPav5ndPjY4DUNam" - "9BJbzZ2Vfeh4IBsJf2pwrm6LGwyLNpmed6IAddEQYHlZwlg2G0UgxjFtJGGpJhAUiSSnf8CzE61uXzbGuXP3g6HC0ApbKggjcRbFhFEjNkdamo3w5ySWo1FQ2cXzYvwv" - "mCHuBReBBDPVmV0eKbObuRXFQgaBwc8tAjtd7WDNcKIYuvtmAmQtPInx261sfNPNr4xbQ2T4ZJuiBIpErOAjWkLQfCYSF90EVU3BGOt9m1UDxsppz3uNPHB6mjEK15x1" - "1Mh67pSBAED8z5Jc86HK2Y5zVzHa2WMgMA0Q0MY7k3yAG8BecJ7gLri5uizxV4JUP3Z5ffKLEHDYYJcVh5Xu1Ngpgf9sGZhYPHL47995GnxjAJIFTKTT2n1NJbDF6fK2" - "HO1soH2yE5WUoeHq3CqumoGvB6xJwiXjOltr5RrUWQVAFpPjoANNF1XIFsFSyRQ3fOIN1dvxyozzwsj64eXoJSnMcwc92fCeMZjC8lnQz5mvpBv3AtBWUtHcBQEucgra" - "A7f0qTmYAK3HCwMkjl4CNEqR7m3H2Ai7fxRicNwCCKuHi75O67mPzvDxSTYZViG0G6MTyUuYg31EdxpEtOUVtnKMQSaU91iT0WagNpQ6ePb18wvoasWohmxa2KjU6vLU" - "mXETP5eRMtev7MCOC0GC9iUqpC7I6PO9mMKd9uUx7JINSiqSIV0OkMBFGq0t1FaYeYZ2gyWAGyGbdXjABhQLUAfl1H2Q4zWeYoiacOfFemB4q24ZFQKLCXtwlCFePcCq" - "UnGu0rMdl1ixStZxjYzgS8rziGQBSeBqai2I97gZfyK4Vi0m9g6WOPjVji6J54m7ZlSWJhKO7wkL6XKWJ2NmEgbvFwesrBV2nO3oxkSDigmILLVE7F4u8rPaieNIuLu0" - "JDcgZIHYM8wWXiVjpcr78OvuA514njH62CKsfQJj6gqvpHotAL3NPPFeYIoHNuUNM9M0GOLt1YtFiFe5alrKYtCxilcwA1mzQ9cw8Y1trYRXXeoW36faLf5crZmNTU6Q" - "BFwB11V2JTlUVM1h1aobE7SvRFUD4WsOmm2CTUUFR2VpjnfTMKx5DGMXDq1fxftbSpB0rrqfP5hBvVVmVX3WHN3wy4LSEjWZZgmNCkhIXBT7WSY7DMatzQy0VlfATHTK" - "ajAs56tQFOqxlcE0CrDCyimjpD1V2ni2arxHfvEnTcQSKCPVAnOr3HtiHAqhZ4M2O07KNEqycu2OKyHdjqqfQpx6Rms2REufMKkZVtMNeOqvlmoosqwpHQQqzILw6tPL" - "KSIbVL8pt74rJCFdPtvoofGRbOb1viRJAmcPlDRqK7l1fxQATpM43hLLE7zTkzn6wuHjojKYqBJPb1aRQBWNCgkPhXkn3NHmU1sklEQyyvrvDYmNoIvlJ4WOIpD9aOBH" - "7UAQKjznFEZQTwlm0plvmn8U0aC8R1e00PqDutERmgyiBQjViT3JaSN3S4ee8Wec4OErbWwxDllkg8AxLQSHy1xNwuozeGNgs5SPjsy9tY9oPbKF23kdFf38NARgD073" - "Ni20ebKrwS5P2x3ZiDBatmOQYcFis4RLESzP7RsyQy0yZxGjTUbOVCuPCbbKv6xyZrK20drbpxJNptBhAgtuXiKmVH12fZc4j5WRw7ZCoAEd7WxCoLft0mF7iexxBoHw" - "1m4oNzc3GKpeVgtfBkaAF2dQHAnK0ANcvvCAZ6EKhOUZOUa0fbwyDkD5ebuBmqas7Emf7sbnw1GWemgAoc1LtTN9eRjrmoYMer9bt9ooHXuFNgf3ilqqRkoxdRw2bGQN" - "kpcppdpQJ8hqPj5UindN2VMnSRj083g2EWXiUI3gfRh4BjDAg2UAOd260K1tv8c8RK1AYGNNNqi46stWXVezo7JKM7EbDxlApZHCv2iKXnOInqVw94HqTcM9RF9VdKkH" - "0NeKKHBupDp06QLRUfhrzIcNH23z6NgZ2RosX36JhhVki221Oiq2V7RRrqeS4RmaLR0EhvNyXziCdVhn6xZqCiI25TghneXL37ShY8VaABMAL7CAdV2RjotwNA3KSVaP" - "ypCtX0swSmritB8JrpY20jB0fDmCspd5hb2jvQ7ezQgQXh3SVbQRplCIGyIfqRMVVK4gP0FLdy0OquaWwRw7dPAEIsGHScKl3NLMpOxr9l8N5qcnusKIpdr7f7A2csaM" - "v9tW9jLPetRSgU5IvIztR4v3mSTJnEACDRGgiuqxB50pT00Xyr8M1LQ4OxNoBHbnDMsnCq5blcudGG2BMa4WWiI42CUzLlHEfV4A3hUA6ycEp0AimhOWWMArIYtYJmCr" - "EyUcmXKRYmM20Yp5Q12MoqeoRnF2wbN8jolJcbuFQPjAYoCUQICtzLkHtZ4SPZ45zopnOrrMcGLN3XwALVvzQtUHxSGmAR5tNLcUCK7siEr0DyIiGnk8sg78xEz8QrY9" - "LnVNgg2chPB3GgsroAfKGOxKZzQGUreeAzjhJTpMOXubRUzuSC8g5CLpSBwaW5gQW6jK30FZpj8GSeRZnTFvkOG0XhLKzSQcYK0xy8mIeNXtqqlwmIJjwsZgH2jBFxeM" - "7gcpguK2dAjuTqPHES2TuWr9qbqcuOfWtAHm9WCSbZdYr45xAmlQoR2WG0PSzkPmuqnZPYkJDbaE7l6WXFyWPSp8ifoMaboGJZKtOG18kUYtWisHTqqtUm60oH8pNzow" - "ecqVy7j7TbSwGcxF8d6Qb1DolS9d1rUAzsTkeBZckmwF53GhanArFfh6Beu0Maa03JMhr0wZ2yKjDGQC0aFXmhXQpeun5dXERRVEpm4GlKVyUUSzUiPxEM7krylQ0qOx" - "SN5MFuusezHsakPHpJEWOQDWpraeR1McGkNIRp362CfyR4gCgkZKZTETmSRSknmY3rdUAXOkWEYbGZgGrWNQsdGQrUBwKnYqaUSBlUa2xE1is95H95eUeyX18J6JUdEO" - "QkWjlNe5rpJpaEnXDfqnzKjAjD41YvgTNBhh6OFp2gzSxp3WTModvlaLnUTSWs0nYSs9yYOCH4wRGlbm06YyxW72nnqUjQTyweMl9bO59mLjCbwpUEj89JPTlDpOmhxj" - "kvgXib8Ds0QSfXRP00A2vSRVPvuwuKrUly10uE0kIWZU8I79cYfmOfxvaJcqsgqBUnof7SEGF7qpRjqnEqSZvC9aj7qgL41ha3S5Xtfnc31KQTLOCRiOzMUzN5nPTZBV" - "FuJi0vyi3Tr2V91Ln9rs6qAxaANylvS4XvWzuW43Jf5VqlePq5jrgtrpj0xlwrS1tgGlUn8S9ZPLDXidPRJ7vO4GpPz8l3UtdaFzyQt14crsMV8BZRORtCIzwASaitss" - "trhuzemnfzQroo9hhfNCpETpRq0ztuBd4IsXo0ig9YVQmAcNWfWx2xFYpbRta1uAcA8Hpv85T73ccoF5Bh1ks2qUOVIQAQ2we30eUThACqT0KED4YL8srHKTLCvjK8NN" - "sA3vJB1a27PMdUrwWxnnuJenMBmkGVyWackl5H5pvVUCJ77gvc3B8bLo0rd7E9pUc6K82ltxpyVrjQiffCuUdpoKM1QhgAe0qjUn58X3Bu8tH6bQUZCt2LziMmkBxYfP" - "q5DZfy6cBhfztr32lIuJQLJEMCoPcGZxZltBFvspylhF3cp2IpmwUY8xSY9f7pJCvB7OXGbz7VHJslrkdwq2MF73ecshUVnVQGvDo4Mq5LbaQtfxdEgH2CbeuP2bK0fW" - "fhdqDiZg5hU2DqTBH1msD6SFaeK4Fmsbo0VWGR6erx43nYymKhJRj70WIcoe5HTgQRIU049xHCmZsJ9fxQBT0DePEyKWaugpWXEJmeFtb2qyYLVl9hOCsgFBYrbDkVpn" - "ePTavCKiCu7NSqGzrhvnRHAtzfyCdUafKAm2yiMWQrKRuOEwdQWcEiWLRffzyPV6wFi14swEzbyw3cb5HHbrEOo0FsxPEjJUu7gEFaFVCouXodp7yenAoAS06kqRdciY" - "D7vzmNNiSGsPl2BhTJQGmpJC8jC7gFdcAXzo1OYyjtzYtH835mSRHml8d3YnEnsib16gzIpI1WKGJaMmqgIJ4w6UG5gV4FlngQyFsCajyp9qmL0iGYa8xW2CuOgRRXUg" - "Sm9645NmY3NGM2q9u6HKLBIVKYb7TUMRaUOXqnXURPzTUy8wz9IjYclZFh26I82rIbIo0FpUJynoVpXEiLKUScJJiNtUFVRCyOE2DAUPWgQ013A4RcIbcbA5J6fIaXVL" - "gahqrdDNUX5VSLNeQJakc1xlCogWkqd4ROiSesuHFgexWdZCn2NItk3uqxa2DdR4lAZEaT6PMFsaA0206YgI604Cwz6SsrIdqzjHlmW86I7LhqFSAE36pJUQJzGYXzoe" - "dpZwyHMATQgSonvONx2gVH87ifeAvDmtIIUr0jixaB2LHbJZ9GaVD7F5a6dkb1vndmzesNwrKU9uaaBzzJVJQCvKfRGwHQP2hiw09fbCr0hGmt7OrqCbZxbtMXMXiYTc" - "AsbYHIc6Ct3n1fduK5pB5Aa1wKPoU6sNe6DM9qxgQn05Y2UjsVTuo1KCWVOz2qccHOnyNzOyyAJmvQe8OPKYEhZjJbkarTYAnHN439tg6H3dzAgpXYSKb1mpuZ46dM2a" - "Hp2QCe6wtvOCnjJN0iHtBkhmbivCVU5ki3r0h4NxHx79GYzhztGD3tTLXIRVUv7unXFDTzik1MAICX7HR0GtjsECso10pqpFEXrGM2XHVKfyoRmLxTgftF8RBDb0w3YK" - "Ut19orlyPmZAKIEzs2Co2rgWx4KKIBL74YFby3OiYBjkZ3NBS17NoQZwZPzRsiSt3DtIFoUoyT1eBIZBPVjCVo5SMBDtZfHivSdNRBXaPZdR4NqQFcKxFTd73m0qTwUm" - "ibvSPTG2qLhRKHSPxLIhcSb67cEDX8Iw9jlxOk9L1Sjl7dBmVXKaBrxGCBAhffLyUdg0kGUpzOAPCNobQXfdybYYVd7xqu6itIDburLPXeNY49787H4ZpaHcPCqFL8JE" - "9iosWeknyQq1zDmxte9Try9xDliTJ95MRCCJcPLhE90czVDdGM7Ceix3kqkrrMoM16IVotiOb3299uEV2qioyNTN5XUhhiLK6gWsv6n23StSaggynPpLPo0e3PJcGLyi" - "ufRCsGGOH9H4XjH5ZLj2llZjWUEBxok7CmLV8VylrSw6UdPMztvqzYdQa2QhEepRGcJepQ98GejBrjmNVqn9RcOTQb0B0qF6jfXNkHqK2GcboQe2vMO56hgdtDSo3DsR" - "6jQJOTwqNFqJrxEsZZC9L8Fo96cxr4bXyrTtRn6joblxORZwtCn3vwRQvM5oyDZfdtxr04EzvKtftxQUgM3TRAReZi4ucmQ0fJF9zIKA6oefNleXySOn3FDtZSXuVpsM" - "Gxa27zETEiwSF0mEVtmafHq44Xeo3PgHu2mbBYz4laEYgNe04f72MGyvhXH8sRyLEGvSZCsmtg0iVW3iRoGvlxnnNVhpyDydTtwy7ifAoo7B6eI6YhKGe5ju9kQlCjSx" - "sdaJXCxLjcf66vNL2wevmXYghY05Cl3f60yn6ihEZJkj19SPqUB2olYScF7EbEy1ZifogTamkyeDovLrm21UMBi1phJpe96hnjiHyNGgKSje4K3G1AJvzzMCn37CPeHj" - "jxD0EPKOuTNg8bonMXfZByezGKruOsNDHKFBI6rvOaczDIf8FiKvTnwktnhMAcj50nOOt5IFK8olEoYwwBnzyOIoxiyCkuhXqj4GDtvXOmXeKGmjjJ7oGWNe55lPedxd" - "38c3Kq1emQwygQuMhLYxBMSsbhaVm9yv06duKAs3A7aPjPjYieKhtisNw3JFyzrKOq04u7kI0Ve34XCUU5WHcMlYk82llTYULeC5YG0MAzcJICQLX66h2f6R1j6CDij0" - "uRWArg4Gm89UXxhLKDy3oUUK5X7jInO3Z4l2Gbx0MhvuFktHGm7jRvuJwYTae3vAYbBraBMMUEY9SPmlZZHpu1Hkf6HBF8cE3rCqerUrPnUCA1H0Ie0sQBlJ9Y9y4Pur" - "U4vxcJto6zQTAeAkDsnufCwNyuHc6kOtnzlq859s3NeljjwTBe1PfATvogKY0UyE1CIysCT54JJY57DR5PhawPJq3iOC5y7Qpk6zeJyGlztXpVgFb5SJ9r5QtJHnbkE9" - "vP1fMKzemdVLSjA82ZFtGfS4DCe4YTIY9xmtSQZeHDSeV2sd2C7QwP3xkCF8FIDwVmFfzEEcgl7hA3hkPuXvT5mbQ0kbSJ9d8HUxkTeQiE37gpHRiV1mwYphX0vzxSF9" - "bMuPmGcDUEVYWavPMaAtjdAIndRn1T5Ex8jBIGSDxVU926YfZAEDAaFCQ7tbJ8RTjFfDmzzsmwNnJSDe3UinmcDznRnUkHLapuKp1GPRC1NsRxft37ujX3u8CcQVDBn4" - "qskD4n1JwOfH9yTFTF3jGzA0I77TV1JRSyCX4J3q1FRxTgdpo3hur2oLHYOuhxvDR1ZMqr8qHYkFSW9X1fUdCY1BuuPMMCPfupO1ZBNaQYsuSjKbXpJTBjhaZBqIj0MX" - "Ijetz3M0NlSZ8VO1ODDi0tn28gdptfhhiP1zLVlJNElz8gc38GvF0q9j5lCxiUWaX17qoPGHVZH6oxiTa9nN49UXvDsiaSJgbCZoZnzZpBvsXKhGemNBXCwdiuMxSL5x" - "FXVet4TQYEED9i9ptgCuXtdcBbcpRbe05HLew1I1pEcDVC5Wc3IQHgURrdYuvbKTqhR905apXUzBc79qrn8Z5Le8ASVlysA9uQqLFwVU9vHBtps2N9SwTNQph7l4WNSy" - "CtD3fW3nzP7vsgUb2SlTXeu9UXQHkjtEqBso1BfZwTpo20xWByqF9sJ568vl4i7qj6Y8oTsq5RHhTY9sscDllyl0O5Ef2lkbk1iEQRarLwCyhO39DSnRQlYVdNo1iGBu" - "Qq12tnScBaE7yEnfH1udHEjoGQrNaBqRUUIjH3VtgxIJQMWrWbdIYPpCm5gDzpsIQgBSdfHj7BwGf7tXQh841mKGFDguDUMP2OnJkkeqdOVt8pGk5Zw9SY4USccOimwb" - "EIauwjisqr1FRDeLOTGqHKkzbpu5e4rcMnoUgcHgfXZd0iIMStxWm42Rc4eM5kzBbj2bRtSeDa0cw6WfBfLswJtkdhUUdAKUCksZ5IXUAMZPYeXfZ5iwKKeetB0nRSW6" - "R64Bif5JUwdCTXslEIQc1WdIlGrhxGwPsHQhfmr3dfAtcMBA1e4bXqH6EfOGhC7YjjxCysadI7e5SUSHKlYmtqjHd3Ji1usEKZIYwEoIgu1Q8sx109yina3BWqkZrAtX" - "vJfE5NP3A8lT4MxzSanGbGpk0WekmDEqTY0A5hWrYdIbmlBsHoLHN5OyXEnRRr5tOhHYfXzQDjmp7zw9hTuyqxPPOwvwVwoCEF54pAOt9D3DEesQSfSOmxoxDJpZ4O68" - "C423Pk1TdJp4clgCrfV5Url4LxlHhXLJNCEzPd6usSoqHssNHMmEclNdu0wyrCzFzN9T7Dz42g6hO1afu84mvAks23zf5fFhZSRl0comoHZGJcbmQ1Kkc4tEuCUO5oWR" - "JfohwnmXGcPoj98AUNwRLUzsyD05z49QVY5pwmAsws82G3rkmWHd2g3JLrbFmX1oeVOEnx3lb16ywoNPqp2t2LfhVqq1mUq3MT9QYOoaCn6wVgzGbAGTAFLYerq0KF4N" - "6uLwMZEmrLPZF8yGBlE0N3rk4Uc6Imcq6gjBsyyn4ufRlrIwHrVXFY5omNg0y0TMhavkHSiu2Ku7oSmqt4CZTXt3amxhg3EHIvE419M7DMmxdxEWEHwvULkrxyJxJaPc" - "Myzo7I3MlgaHxmdjdygiUWzIE2ewa7hhweJomeZTumRWRK3I38fkGPUP5dSWwYWMatKFbZWhDAyiCY1bXkLNKzRmj4zusnMcjmzcItQpZx3Rpsn9J7l5o7tA4XuQzie0" - "O6IrM65C0x37fFSJpKVdgkArwhRLly17zOhTLtytb8gzGWeOoc7C9ErR3aPOMqsYaZzn5V4MfmfmcwzozRzh5NcRUvCYie5naS53ruUvEUVV3MqkQzCmYH2XIpuMT7gc" - "rCIuJISrcbmzH4OKr3073eT15Vz9VcyYhebWh7jbgzXDjbaNKyUzi6fGao7NZDTYSuIdCE3Nr8YHKXtmWTcFNkv9t85hzhHA9nXJ1snpPBMepwFn9b1VYNVGi0itNo2g" - "CE9EiDoApbNlVvS8KO8SCWWn0QwPNQtBwZgCjAqHNFETzshr16njxIKvYC5mgsUZduPMIJlFOWaab1yKZH2pj5Um41Gg956uZKwH3ZayWPWE5cv7QSIosGQRDEijeljL" - "nBSVMxTvOfMLXrjIe2bPiyqgjwsfFedwEEyiHWEYsSwks2p0jADqfrLGbe8w3o1TCI4CnKB46PmDeAFY3brHIt4QpGG1Sc8ia1znFxU5XqXygbUQjeWsKsXG7LTrL0Jk" - "5tOgiMLO6xrgJb5BuGsslBeVp21jBhOJdco2BGNKYEkHKwPekPn7rlfVzmSRMSPVdvXNfvrmxjoM6CFETNtI2zeTuX0J1m5F7FjrP3M9cVhcFMg3xQNddmOegl6UX8p5" - "xdSBSHVSqircHhbkhckd8Cy6VExxrKnMjXP7kkeX4Z7OLbousZXVGnu2IbR8E4pklhwcZHIaDO6OUFyHD05UZUjzCKoXEu81OleXVZTgAkYVJyzr1O0DUQblYA1ij2AM" - "zNPY5GncsBKfMBsvHxuzeBVurtLrvfeAYryuTRdwSgD9MW61AXC746rSILF7aMRnJJ8ho5qzZZbBVRx5IcF2GpdpSQqDrGBQUTAarAluIwoPCYyb6aJrbbtjpe6UmH0t" - "RXdfLoFkYu6OD7jynmGCPb4A24g8rqpRXlFUVcxcN8dm37bctHhHcIrFY6UKrZqABFaQHPQzReOkHiABOxXba8x1mKyDQPWUYe3iPFXuAIejPAW0wM7MiRkODFsr0nnP" - "wQZn6YrNNLlmq04y9x9aBZNIhGX1XlhQ96aYObh1DFipCcaQznB9qgE12YVHl8jcvRZjSCMQdEgBArhLP6UomJUr6n8rMXGVK8O7zSNQzgWzCg6KYolPgREmmCATOlmx" - "x1GkzazPBvuviO3PGEoANE3OJOia2hpmBZoL8gPVZ4XASSosAfw0f4lMWoB7nwY7t1v3uqix9Rp6gOxsXP7aK12vnCZRg2COeazeg7MUgfRJVMdTyIOja2gD1RVKH8EQ" - "YM4MevSXTpPzbRHRgys9P1Ni5xNlNJYq3Ic8MV1HygpfMHkwy9Q99fLm8zVJqencwvSsXjKLhJ0yZjgWILAMuv4ihuVQ9OzPJeENydu46bS6xqyl7n6gGQJSP6VSN9jS" - "mUULi0gMnWyBT3xCHHabqpeSqPIVCR25TcdMdHWyGBor3WJfhMw3HN05uPiuo6aWvYdpolxfVFpaE5gT3pDVz8KQKd6TrS5FQ4ljd3mbSWVVGCMjq84Y1BZ6qbg0zDIo" - "oNfZdOWQ2MGzosleFO68dQ5x2EuauEQcM7i3OQbIKrgmpQQQLgAu30QeZeX7M1IBV9P7ceLVcKaSfjUQEpMqYGGP7hhmewFfZOXuDfHMoEpXykoPXDnbiiatbz5JqPKt" - "ZHAuMXT64VqUMCYob50Vg53nRdOt5ciUrjR6pqqDBCOgEwc51fr3R0SaHo2wYBYxzHBz5PxAbr4OqUHjyN1JmOd3ZqZWbK1dTBrtgTXHDEQMFohndqQRYtS5QzYfvmVd" - "S0fh4h1hwIlf88RGxeEdO41lQOVP35P0ybM6kmRJUUMFbvv15d7yptFKIVNrJ7litI0tWvDVBPeEJmwbCPwADuK5P6cz2WEcCI48R8liEQwjBKCeo5aC89UzSh8CrtAI" - "WpraFWZ0okBBwyZ9dQUXXtdbqUIBiKiL436vFmE59aIUegtPeKTJZVHeLocXeA8VFPVhdNvKknFJdU6jzk9WQJDCjhusVcJcQrHmd16vLgpkT0D6WKIEPldIxmBrPzrH" - "b3OFB7ZmLnDsp1WMXVrNXivh11woH0AIQAfvlOvu3hbqZRWG2UDitie36JPFqtMFDRnJMkyB59RTeUBAS1iqi8Zun1KU728EZgjqJcxnX5nKXOfDWo0YOukYEou2f32A" - "vC2fKPqSgHgf4WuKmRjn0cENkZBsxQrJusN7BH37Q4dVUGijIS5I3UihZhIuNAEhlrcYAm1IXfHN1RccBpd8AQ5hBytUvJxQsmfkGILlB6K1V5J590IARfXajP9h3zew" - "FHqV8XUwo61wQSp3qB0ULBhf3ajIOix6N05cfSOOb8HhkBbmmiQ3pqs0J9tE5Ionr5O0lXlR6Af3PcqnSHi2kJB5XADd8BpjyZmjjQx73nrnI95Tx7OgFXjPRoMByXQx" - "BRBrhkVBwFY06gco2b1pbPJVIJBP00yetN896LETdAIE0AXHPLeW9AIHx4HoVd3chMLfP4dS4Lip8qVRiPKKsTYCleDmx2T5IbFLeJnsaw5Yr220opVdcPkg7M5ALBVm" - "iLxBwLXddkP8wb5utSSItKqBFofOCkTZzoRhO1fWA9BvBt0UCoZlS79xCD8wSjMjDSUW3pVXO0zBbBchXsvUzLu8yv2m8izrfkNwprxpBrjFQFsaQXhEmVPPTFJZazzf" - "awzGKqjHXguwC73xg8Y5Lf4wFPqMmTnuxvMBHSVR7AgyMjrqeIpO4GcCHedPWC52RCKMSJWwfngsC2hO7FnsCopF0PL1taPNkROuBg1tD42WZT5G32mSbNfyioI2xTE4" - "ErSQ2jcDwHkSwwFdLuYc4CIHO6zuBHBA4mc6cMOHuobBY8nJwhL3J526hzx9gzUNTU7vgUdW5zUNNAjcxkN3jF9XgnBShzeU8zg1ZLdGfLrsNjL8Z27vbMG3KphyxzCX" - "kgnNFB7hptipG721qwytqDsx7pMi10UFPWoWKMqfpM8wgpb16TXfvy2HH7VDwjNESzaQkmwjeu3NCdVnNajrGW1rIX0H0KhV4PgKG9eptvrzQ2pbwPzD6XBEh7MD8SlF" - "Ptv0aIezSdAPIckcKaFj5LjHdWZx86WFuVcKYsIajlazquF9rCeCC1bfZbKrjMeiJnp8RHheNUy0NnNNB3ClGekyFWtQCzYUCo96xB1o21u5bP7CFoqHevARgjApes4d" - "gM9WgZuHOD7qNWfYux7GorTTotOhg9hWrvRfbHrcD0Niu7iVvWgHVn3v26Rvu6B05jUGTXxfcxGLpyC2ZSKS8lQFpD7T6gmH2z9ysbbptfAdiwUZe6T3dtCaTSI8YKJE" - "HabEWVTYta5yji9Hqm8oWAT7f5W8T8HOZ80FEZWixkSVeYSKBJbEBTQEueR30G6dsLOqDLIDWqkq5BqCyr84hT0S2h5scY56j8LsWtQXHZ25BmtefeHZaUaq5jUQLXvs" - "2DGzQQpwQRiosJtRBFjOiu5wFbfcDo5mZtDboDYanJYOgyaVAKb9Sh7whEt0Ss4FVBHaUxiE9wHc2TO8Z7L1TZRUqDeUBEegIzikCHqJNEgpNuSIHD6LwQmkKPrO9M47" - "MHXdCMxuSajkGt37YGlS9WuKtv7p8gFOnImv3GXzmyleLo9fe1IiTaiGGhKU1NWNLN3pOeUzC9E63iwsHkQEPWM0CIOYyjgE3GTR6mHknc24lo0a0p8gjtDHedk8vZ5p" - "4BNHCX7Ea7paxayYDZbIUx2hA3TUXkxYivIJVCFF7drV2XMmAYSa5H88JsLA6YRZCsltAS9iXYdaonAlN4WGaz1E0m4afC06JsV0uOzfiVKsSCFSf2IXyQQlWTEqnrSu" - "nqlMMuSx8Lq4GDVWAsTATI8316L7lKV5RW9CJB1GvVM5vwrHq5L20WHs8Bvpx3wXQkK8FFwF5L8iXG1MOfS3tbXGBld8EfzF8kB9E6kk4Ehzml15oDnDQceTWxOkjKIo" - "OD83EnQKmuHvul57p1bsT8n61enTKYucBytlzBmi7mRsQ1PbjZDMvg9TjaFTMcnvKBOjq9sv6o50FXmrhQIdHkTUSxAZ0YszWEhxawDWetiT2Hnpl2DkG6AiEC23N8bc" - "YPCV4jdGnVKm7lkHpkU9gzNqNACF57y8vlsWRRZtop18ymQwxNUB0TfFo8IX01YjYWWGGPdZ4d9aR3V3FL43a40XWTf5EGYetVxENlvgmptILWp4WSShdJHFuhtSBqFZ" - "NG9LL5E6eaTcJUy5CetUFwF1ZLBIaM7NqnC1HzDyfbUE6YwZoE3SotSaIwjVfTtcQibn62LodQtVfqkwtnbiTP4ZDDUXrsFakLKKdQtDyl97KJbcgIy7AzatcJbOtv6k" - "TaB3RWYoEa0hVJ9vFbXjTNOWZfQYQffbHvSsIk5wvD7aPJH4GQjII2sB4nMTnPcG8u59HuzITQZkXdOqXOiWK5JGHUVcezyr1pCNQSfCSzjixkbb0q93KYjevgU4URQx" - "40sWaAZ0zItTAhIXMp9I1ZBXXyCiIMbZB8yW62xOXEXeHdoab1J4W98a6TxabU3oYCZcHbpFPPgUYlOOpsfXtLTbmbny7JOm5WAdn9oNB0PslpU6PZ0afwFVUw6QnBAI" - "7ugkUPAg05Q6k6Eo2vKbUceHyTctDxTb8jCAqjnQAjcsdjFdyIrXfxRuPoYlW3hbKNY7owubNosFBnkFTREPWDSaLrUc8mq9XdqRYxkMLAMSur6aal6yCK6BeeNPHdeo" - "KK8CoLNDyioVAhp2hsL5bfjBHKDQCN3ItoK3TD7ALfZlQgF2cQLnvkQdaHqVwMtwkGpIOEUZw0zcDmOVrLcYx5UmptfwkMCbQxrpFlhju1Nry2Ci9bKuRoMeuOeneV1O" - "oKsrFPXQiUcpuhOhNhlvHbnvgzvwxk4tt9TvBzw0fL9vXbDnq9uM6YtKeTW7rRrrs23cwjPJzz3vEhZxC6NkdM71Rc4nOwbrpDdxGwkZ3jSY3Jylo9Lbf0pviXcuJ3g4" - "1rzmp6gP6NEyp2rscQFE4nCr6CaQVIgjhVlsQOFOtjsvQb34oUDIBwGxDtXLPNh0ErUUoIAKxVmcJTLD2XKtme1h38QFqWEfMASip8mWjoSpwhWUT1P89iEoSeNCA6iU" - "X1Onx5LE43j9HoPlZKvYoFA0vErGTMaiNik4pmxurReM6dZIH0THe7boPk4nyih1Jss45PZ0dE5iabinKnwZjVWbyNMFWfrfYPqSbH0No1zeh1uaS2lnCiEAlyGRftQs" - "NPPSFAhPBLFeSpJYiqKeQzE1opvSoiocKki7YY3QrKCddLObN6lO4W7s1mIRF1GYJcrEY9GchljXqlJznDmZ0udaNJCUfAUcynENT8KplC4dLGUPskVd4T9MC4Y5wJ9r" - "ogZz0tPeCqnkC1gEXGSJQsMoqeI5Aa2Z0rsczLr1acHWNjahd6hwmRLcmnKycJAQlqsPUdrylLZaFMcYa6YXUh6kqYG5GEHXd7UAGwNrnm1zeHAUvcHrYOSotYDpV9dC" - "32BgvAPXdjs2G9apgOjZYC8ykWYZtXWuh0YwTpd6HBgqgeCD4MmveJqyLybFQFzdKjl4h8Gzp2m7jpmEgC3TsEEycTWwGdQf8zXEjZ3GKRLzfjrmGlOUmQuMGHmedZxB" - "pPOeCLVaVkzqnz1jP40lEjHzAqhKqKXw4qcv2l5vMAvJ0MJ7PhySWs21rQO6kZcg0uGVSa3Ykl9OQmdiUmO40n5UEx3Gc2WgfVb5nsGae7nkzpFLgrRgo6rZ0ER3bY0R" - "zCjgQx5cJTsZyuw5HasdBKjq6W3t8EYIuDNs9VWoTillo1u8qQca5hztc0oTGv5mTAcxp5mHHn2WKMK8S21OPPBaZRYFMfHj2gfU8omqVLxDzmRPWg3xZLzEqsh1QMGg" - "XQBYlP6BYpl3Yf5Y0cyBJyaz59m8OfL7tySHrJULklZkil5tMAHXWU5H1XMFmC3PvMy1qpRxDZVRvO0oHnIw58Y9pwiLInYnbOCU9400wjr6DeN5mgqzIbUqAkGfHEx5" - "Sy1qz1ZdPIyevxJyRFBqEC6Bi72ICXJh8anlXX253KJv3DQ4QTzBSqSvtmU6VYpduhhXET9x949txeo0IAXcZnHirY5ngkCIygciuFRoTTUHkEKS9kf4fS2nEyaDG09J" - "6K1r7QZCXpoVR8MttHqCPPN03dmvQSBeAMhmwIEDPM38iIqN3qC0hkCMZDrZhDiX7j5fvhoJ9kLjUPIMcu8tKpnJKB3Lv8STTSUdIULglxuDVNl0vg6m8IFUZbHXtl2D" - "J1DFEvqE73HIWFZoeJzVggpWmrDCMuhACkbnGqNGL0lUL3TUvoGBs5gVG57E2LyQZUnRPZYAo0RiOtjR1mR7CLvPphvjOVwkKNj6vibfQOhEjBG5nbkATzHomZocpL5Y" - "cHBZpNeFHh7nP48ahKBzQn49I6HJEaSPWfcfsTX6VF6lKTwQ1Z4dIKLl6lFmwwRMoaOHz0W9QaKuYNOxzD0ntW9xn8mN5sosSFZ7cMdalkIbxS12A02OteW0I7u6ZKGC" - "tmDYAk6d7juw7JdNfJiXpGQYEUkDK1373amxtzWmEu9ftz433ZtFUknfzTVuACgAenpfcVRx6oyLJoclkAk0LLKgE9tyrXjJZzZJWw70YXxn8t3D0jPx88aLYhEA8TfL" - "joAZvqcxBQ8e03pbggZFLqZAIw5ewXQhBgUDgjqsWtKELUeO9pvq6XJJhsZp2eiCahKpZKqxpU0sYBSciW1Co3eCmVLX7NvQYgNZ8epoj4OplLPqYO2oBUXdxTLXpZeG" - "gpcw1f0gsVUgreThXaZjFYcEaF3cTlqRPwayG7d2niO7l4fJ9tYLBPoOhlu0hpwwNMFf5nCccgVa10DCxFUXtG5D6jhBr5DYZcJRwZf3hdI8sFnbmQXigy1pdsmh2sUU" - "R3V09UrxqUwqO31GT7v6wCoRAqmAeNS7WeL8sYztP51JuwOewATVSNNUYU92xVpWgi1gc7zcUSnheJ4lOdTM41Q6Cl47aiTE4WhmbuW1Fgwr7p8SDOYJefMG7qFX9EBZ" - "N4L7tra75NO5qNNAj1l5RqUHvLwfP67vKkAmWdqEQOV2yofT13Hrw3bJrqB9Sh17AJD3jvSOst3sKj4UNkSJRVGJLky6t6XJyPfu9SgCqcybD8GH3A4tcotpKmUyeWaV" - "3Yb49gsjlVWNr118iAFYAtu8LuYIpv0tCDlNmoGnaOr7MYg6AhACoQGhC61W5fh0Rl8EmUYeQwr5n6FsRGANqCUlTzABzvhJeZU8R4yPzK1EKYpsNbuB0OAfjWQ4QfXo" - "3PYVwMTyxFiMTEPF7JExpQ0A0kiHocZCzBZNee4Wbcosp0V9cqlB5X2Mizv87MVqN5qeAlNv8bF3tkUTT9ny25jiWz3piarNZZriHt2Jjwr6drhDmCyAMtOzfTpWgbIo" - "4O2QBp55VldQdVAIfZaruZfYsfT0m3klv0ijQQrSXfmEonDPr9LSi5ex1k0Pt2I42sW7tO3PfHI2ess8NhKTFwaeKbhhJH4cjYWuxETfAT1hcFDp9gFVNv61hZIpPC7B" - "6Re48nr4bugJvEqjP8MgmDwCuP4wPv8whciQ8xnbDANV9atiIp6nILVVPHy6iy8aeNJ5cU8vGHKEfglaW1PF3BhNBoDtbhi2ygT7nRWncGwlpXnrPsnwBuEk1XC1d9wE" - "XSkcpTLsb3jUCod6ahdBtMBWg30E0TmP7daELf3o8SWrOXUHoCad3y9q1El10fCCDfzw7uamJ00EXaLHDNaIOckLowZUttmM1TXTyrk8V330gmOIs3pS6zF2fMgbXfdk" - "Rd7m3ocqHWKxCINsXgZlt7a5LsoulsUktOELiWnNvLJ86qo3BmYk0Ok0gGqnU4WHvVMsiCU3gEmxOxenUWlHM6JSzrk6iW0PYDjEwQvMlT4Msom4GSLDQV2oxpbvWpIg" - "xLQjt8WS6pKhDMdSMXMYproxprmKdxHdsTooFhDuIHtNZQCbqhSvAZLZ7YaVo0JMWVZHMi76eAAjPI6h1BJOmMcuywnhIlMPcSScLtu5NvwQI854m3BUSmnxwlTPrknt" - "AVoDe7cEI45aGbgaXW7ZPPSJ0lYc2OvyaTKJob2OBh8zdnA3ERIBgRXC4cNImoSvxzMBiO7BDf5zroBqceOXxtaEc7y4D7GrekVAEwIjH7DMkg7AXdBrAXKdr97HIj"; -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.h b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.h deleted file mode 100644 index faaabae2d3..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessLargeText.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#pragma once - -namespace TestImpact -{ - // Large text blob in the form of a string literal so the app does not require the Az FileIO and Application environment - extern const char* const LongText; -} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessMain.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessMain.cpp deleted file mode 100644 index 5c46ee525e..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/Source/TestImpactTestProcessMain.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include "TestImpactTestProcess.h" - -int main(int argc, char* argv[]) -{ - TestImpact::TestProcess process(argc, argv); - return process.MainFunc(); -} diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/testimpactframework_testprocess_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/testimpactframework_testprocess_files.cmake deleted file mode 100644 index a06f1fa706..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestProcess/Code/testimpactframework_testprocess_files.cmake +++ /dev/null @@ -1,18 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -set(FILES - Source/TestImpactTestProcessMain.cpp - Source/TestImpactTestProcess.cpp - Source/TestImpactTestProcess.h - Source/TestImpactTestProcessLargeText.cpp - Source/TestImpactTestProcessLargeText.h -) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/CMakeLists.txt deleted file mode 100644 index 8298bb7123..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/CMakeLists.txt deleted file mode 100644 index 7748581284..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -################################################################################ -# Tests -################################################################################ - -ly_add_target( - NAME TestImpact.TestTargetA.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} - NAMESPACE AZ - FILES_CMAKE - testimpactframework_testtargeta_tests_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - Tests - BUILD_DEPENDENCIES - PRIVATE - AZ::AzTestShared - AZ::AzTest -) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/Tests/TestImpactTestTargetA.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/Tests/TestImpactTestTargetA.cpp deleted file mode 100644 index 9598b88b98..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/Tests/TestImpactTestTargetA.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -namespace UnitTest -{ - class TestFixture - : public AllocatorsTestFixture - { - }; - - TEST(TestCase, Test1_WillPass) - { - SUCCEED(); - } - - TEST(TestCase, Test2_WillPass) - { - SUCCEED(); - } - - TEST(TestCase, Test3_WillPass) - { - SUCCEED(); - } - - TEST(TestCase, Test4_WillPass) - { - SUCCEED(); - } - - TEST(TestCase, Test5_WillPass) - { - SUCCEED(); - } - - TEST(TestCase, Test6_WillPass) - { - SUCCEED(); - } - - TEST(TestCase, Test7_WillFail) - { - FAIL(); - } - - TEST_F(TestFixture, Test1_WillPass) - { - SUCCEED(); - } - - TEST_F(TestFixture, Test2_WillPass) - { - SUCCEED(); - } - - TEST_F(TestFixture, Test3_WillPass) - { - SUCCEED(); - } -} // namespace UnitTest - -AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/testimpactframework_testtargeta_tests_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/testimpactframework_testtargeta_tests_files.cmake deleted file mode 100644 index 32c8746c9d..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetA/Code/testimpactframework_testtargeta_tests_files.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -set(FILES - Tests/TestImpactTestTargetA.cpp -) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/CMakeLists.txt deleted file mode 100644 index 8298bb7123..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/CMakeLists.txt deleted file mode 100644 index 57b55fb9f4..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -################################################################################ -# Tests -################################################################################ - -ly_add_target( - NAME TestImpact.TestTargetB.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} - NAMESPACE AZ - FILES_CMAKE - testimpactframework_testtargetb_tests_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - Tests - BUILD_DEPENDENCIES - PRIVATE - AZ::AzCore - AZ::AzTestShared - AZ::AzTest -) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/Tests/TestImpactTestTargetB.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/Tests/TestImpactTestTargetB.cpp deleted file mode 100644 index be58b4fa17..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/Tests/TestImpactTestTargetB.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include - -namespace UnitTest -{ - class TestFixture - : public AllocatorsTestFixture - { - }; - - class TestFixtureWithParams - : public AllocatorsTestFixture - , public ::testing::WithParamInterface> - { - }; - - TEST(TestCase, Test1_WillPass) - { - SUCCEED(); - } - - TEST(TestCase, Test2_WillPass) - { - SUCCEED(); - } - - TEST(TestCase, Test3_WillPass) - { - SUCCEED(); - } - - TEST_F(TestFixture, Test1_WillPass) - { - SUCCEED(); - } - - TEST_P(TestFixtureWithParams, Test1_WillPass) - { - SUCCEED(); - } - - TEST_P(TestFixtureWithParams, Test2_WillPass) - { - SUCCEED(); - } - - INSTANTIATE_TEST_CASE_P( - PermutationA, - TestFixtureWithParams, - ::testing::Combine( - ::testing::Values(1, 2, 4), - ::testing::Values(3, 5, 7), - ::testing::Values(-0.0f, 0.0f, 1.0f) - )); - - INSTANTIATE_TEST_CASE_P( - , - TestFixtureWithParams, - ::testing::Combine( - ::testing::Values(8, 16, 32), - ::testing::Values(9, 13, 17), - ::testing::Values(-10.0f, 0.05f, 10.0f) - )); -} // namespace UnitTest - -AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/testimpactframework_testtargetb_tests_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/testimpactframework_testtargetb_tests_files.cmake deleted file mode 100644 index 949074a5af..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetB/Code/testimpactframework_testtargetb_tests_files.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -set(FILES - Tests/TestImpactTestTargetB.cpp -) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/CMakeLists.txt deleted file mode 100644 index 8298bb7123..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/CMakeLists.txt deleted file mode 100644 index 47862f80df..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -################################################################################ -# Tests -################################################################################ - -ly_add_target( - NAME TestImpact.TestTargetC.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} - NAMESPACE AZ - FILES_CMAKE - testimpactframework_testtargetc_tests_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - Tests - BUILD_DEPENDENCIES - PRIVATE - AZ::AzCore - AZ::AzTestShared - AZ::AzTest -) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/Tests/TestImpactTestTargetC.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/Tests/TestImpactTestTargetC.cpp deleted file mode 100644 index 48f0ffe9dc..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/Tests/TestImpactTestTargetC.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include - -namespace UnitTest -{ - class TestFixture - : public AllocatorsTestFixture - { - }; - - template - class TestFixtureWithTypes - : public AllocatorsTestFixture - { - }; - - using TestTypes = testing::Types; - TYPED_TEST_CASE(TestFixtureWithTypes, TestTypes); - - TEST_F(TestFixture, Test1_WillPass) - { - SUCCEED(); - } - - TEST_F(TestFixture, Test2_WillPass) - { - SUCCEED(); - } - - TYPED_TEST(TestFixtureWithTypes, Test1_WillPass) - { - SUCCEED(); - } - - TYPED_TEST(TestFixtureWithTypes, Test2_WillPass) - { - SUCCEED(); - } - - TYPED_TEST(TestFixtureWithTypes, Test3_WillPass) - { - SUCCEED(); - } - - TYPED_TEST(TestFixtureWithTypes, Test4_WillPass) - { - SUCCEED(); - } -} // namespace UnitTest - -AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/testimpactframework_testtargetc_tests_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/testimpactframework_testtargetc_tests_files.cmake deleted file mode 100644 index fa5b4f6e9a..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetC/Code/testimpactframework_testtargetc_tests_files.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -set(FILES - Tests/TestImpactTestTargetC.cpp -) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/CMakeLists.txt deleted file mode 100644 index 8298bb7123..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -add_subdirectory(Code) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/CMakeLists.txt deleted file mode 100644 index bf821ac0d6..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -################################################################################ -# Tests -################################################################################ - -ly_add_target( - NAME TestImpact.TestTargetD.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} - NAMESPACE AZ - FILES_CMAKE - testimpactframework_testtargetd_tests_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - Tests - BUILD_DEPENDENCIES - PRIVATE - AZ::AzCore - AZ::AzTestShared - AZ::AzTest -) \ No newline at end of file diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/Tests/TestImpactTestTargetD.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/Tests/TestImpactTestTargetD.cpp deleted file mode 100644 index bd27b51fd9..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/Tests/TestImpactTestTargetD.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include - -namespace UnitTest -{ - class TestFixture1 - : public AllocatorsTestFixture - { - }; - - class DISABLED_TestFixture2 - : public AllocatorsTestFixture - { - }; - - template - class TestFixtureWithTypes1 - : public AllocatorsTestFixture - { - }; - - template - class DISABLED_TestFixtureWithTypes2 - : public AllocatorsTestFixture - { - }; - - class TestFixtureWithParams1 - : public AllocatorsTestFixture - , public ::testing::WithParamInterface> - { - }; - - class DISABLED_TestFixtureWithParams2 - : public AllocatorsTestFixture - , public ::testing::WithParamInterface> - { - }; - - using TestTypes = testing::Types; - TYPED_TEST_CASE(TestFixtureWithTypes1, TestTypes); - TYPED_TEST_CASE(DISABLED_TestFixtureWithTypes2, TestTypes); - - TEST(TestCase, Test1_WillPass) - { - SUCCEED(); - } - - TEST(TestCase, DISABLED_Test2_WillPass) - { - SUCCEED(); - } - - TEST(TestCase, Test3_WillPass) - { - SUCCEED(); - } - - TEST(TestCase, Test4_WillPass) - { - SUCCEED(); - } - - TEST(TestCase, Test5_WillPass) - { - SUCCEED(); - } - - TEST_F(TestFixture1, Test1_WillPass) - { - SUCCEED(); - } - - TEST_F(TestFixture1, Test2_WillPass) - { - SUCCEED(); - } - - TEST_F(DISABLED_TestFixture2, Test1_WillPass) - { - SUCCEED(); - } - - TEST_F(DISABLED_TestFixture2, Test2_WillPass) - { - SUCCEED(); - } - - TEST_P(TestFixtureWithParams1, Test1_WillPass) - { - SUCCEED(); - } - - TEST_P(TestFixtureWithParams1, DISABLED_Test2_WillPass) - { - SUCCEED(); - } - - TEST_P(DISABLED_TestFixtureWithParams2, Test1_WillPass) - { - SUCCEED(); - } - - TEST_P(DISABLED_TestFixtureWithParams2, DISABLED_Test2_WillPass) - { - SUCCEED(); - } - - INSTANTIATE_TEST_CASE_P( - PermutationA, - TestFixtureWithParams1, - ::testing::Combine( - ::testing::Values(1, 2, 4), - ::testing::Values(3, 5, 7), - ::testing::Values(-0.0f, 0.0f, 1.0f) - )); - - INSTANTIATE_TEST_CASE_P( - , - TestFixtureWithParams1, - ::testing::Combine( - ::testing::Values(8, 16, 32), - ::testing::Values(9, 13, 17), - ::testing::Values(-10.0f, 0.05f, 10.0f) - )); - - INSTANTIATE_TEST_CASE_P( - PermutationA, - DISABLED_TestFixtureWithParams2, - ::testing::Combine( - ::testing::Values(1, 2, 4), - ::testing::Values(3, 5, 7), - ::testing::Values(-0.0f, 0.0f, 1.0f) - )); - - INSTANTIATE_TEST_CASE_P( - , - DISABLED_TestFixtureWithParams2, - ::testing::Combine( - ::testing::Values(8, 16, 32), - ::testing::Values(9, 13, 17), - ::testing::Values(-10.0f, 0.05f, 10.0f) - )); - - TYPED_TEST(TestFixtureWithTypes1, Test1_WillPass) - { - SUCCEED(); - } - - TYPED_TEST(TestFixtureWithTypes1, DISABLED_Test2_WillPass) - { - SUCCEED(); - } - - TYPED_TEST(TestFixtureWithTypes1, Test3_WillPass) - { - SUCCEED(); - } - - TYPED_TEST(DISABLED_TestFixtureWithTypes2, Test1_WillPass) - { - SUCCEED(); - } - - TYPED_TEST(DISABLED_TestFixtureWithTypes2, DISABLED_Test2_WillPass) - { - SUCCEED(); - } - - TYPED_TEST(DISABLED_TestFixtureWithTypes2, Test3_WillPass) - { - SUCCEED(); - } -} // namespace UnitTest - -AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/testimpactframework_testtargetd_tests_files.cmake b/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/testimpactframework_testtargetd_tests_files.cmake deleted file mode 100644 index ebb5a422c7..0000000000 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Tests/TestTargetD/Code/testimpactframework_testtargetd_tests_files.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -set(FILES - Tests/TestImpactTestTargetD.cpp -) From 5a53c074c2fc938ff84600dbbadd2fbb003af563 Mon Sep 17 00:00:00 2001 From: igarri Date: Thu, 10 Jun 2021 15:14:35 +0100 Subject: [PATCH 129/233] Fixing Header Data in AssetBrowserTableModel --- .../AssetBrowser/AssetBrowserTableModel.cpp | 11 +- .../AssetBrowser/AssetBrowserTableModel.h | 3 + .../AzAssetBrowser/AzAssetBrowserWindow.ui | 424 +++++++++--------- 3 files changed, 222 insertions(+), 216 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index 60f15970b1..d22486d481 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -46,16 +46,7 @@ namespace AzToolsFramework { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { - auto columnRole = aznumeric_cast(role); - switch (columnRole) - { - case AssetBrowserEntry::Column::Name: - return tr("Name"); - case AssetBrowserEntry::Column::Path: - return tr("Path"); - default: - return QString::number(section); - } + return tr(AssetBrowserEntry::m_columnNames[section]); } return QSortFilterProxyModel::headerData(section, orientation, role); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index 6b64ceaf92..e438bf6271 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -38,8 +38,10 @@ namespace AzToolsFramework QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + public Q_SLOTS: void UpdateTableModelMaps(); + protected: int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override; @@ -48,6 +50,7 @@ namespace AzToolsFramework private: AssetBrowserEntry* GetAssetEntry(QModelIndex index) const; int BuildTableModelMap(const QAbstractItemModel* model, const QModelIndex& parent = QModelIndex(), int row = 0); + private: QPointer m_filterModel; QMap m_indexMap; diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui index d9e42ef906..37eb1521f3 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui @@ -1,211 +1,223 @@ - AzAssetBrowserWindowClass - - - - 0 - 0 - 691 - 554 - - - - Asset Browser - - - - 0 - - - - - - 1 - 1 - - - - true - - - - - 0 - 0 - 671 - 534 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - 0 - 0 - - - - - - - - Switch View - - - - - - - - - - 0 - 0 - - - - Qt::Horizontal - - - false - - - - - 0 - 0 - - - - vertical-align: top - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed - - - QAbstractItemView::DropOnly - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - QAbstractItemView::ScrollPerPixel - - - false - - - true - - - - - - - - 1 - 0 - - - - QAbstractItemView::DragOnly - - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - - - + AzAssetBrowserWindowClass + + + + 0 + 0 + 691 + 554 + + + + Asset Browser + + + + 0 + + + + + + 1 + 1 + + + + true + + + + + 0 + 0 + 671 + 534 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + + + + + Switch View + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + false + + + + + 0 + 0 + + + + vertical-align: top + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed + + + false + + + QAbstractItemView::DragOnly + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + QAbstractItemView::ScrollPerPixel + + + false + + + true + + + false + + + true + + + false + + + + + + + + 1 + 0 + + + + QAbstractItemView::DragOnly + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + - - - AzToolsFramework::AssetBrowser::SearchWidget - QWidget -
AzToolsFramework/AssetBrowser/Search/SearchWidget.h
- 1 -
- - AzToolsFramework::AssetBrowser::AssetBrowserTreeView - QTreeView -
AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h
-
- - AzToolsFramework::AssetBrowser::PreviewerFrame - QFrame -
AzToolsFramework/AssetBrowser/Previewer/PreviewerFrame.h
- 1 -
- - AzToolsFramework::AssetBrowser::AssetBrowserTableView - QTableView -
AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h
-
-
- - +
+
+
+ + + AzToolsFramework::AssetBrowser::SearchWidget + QWidget +
AzToolsFramework/AssetBrowser/Search/SearchWidget.h
+ 1 +
+ + AzToolsFramework::AssetBrowser::AssetBrowserTreeView + QTreeView +
AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h
+
+ + AzToolsFramework::AssetBrowser::PreviewerFrame + QFrame +
AzToolsFramework/AssetBrowser/Previewer/PreviewerFrame.h
+ 1 +
+ + AzToolsFramework::AssetBrowser::AssetBrowserTableView + QTableView +
AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h
+
+
+ +
From 1cef72ab8d19d0f8533ed793f6600447ad415662 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 10 Jun 2021 17:32:16 +0100 Subject: [PATCH 130/233] Fix uncommented tests --- .../TestImpactFramework/Runtime/Code/CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/CMakeLists.txt b/Code/Tools/TestImpactFramework/Runtime/Code/CMakeLists.txt index bcfeeacb33..e89dfe60d7 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/CMakeLists.txt +++ b/Code/Tools/TestImpactFramework/Runtime/Code/CMakeLists.txt @@ -12,12 +12,6 @@ ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME}) ly_get_list_relative_pal_filename(common_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/Common) -add_subdirectory(Tests/TestProcess) -add_subdirectory(Tests/TestTargetA) -add_subdirectory(Tests/TestTargetB) -add_subdirectory(Tests/TestTargetC) -add_subdirectory(Tests/TestTargetD) - ly_add_target( NAME TestImpact.Runtime.Static STATIC NAMESPACE AZ @@ -41,6 +35,12 @@ ly_add_target( ################################################################################ # Disabled: SPEC-7246 +#add_subdirectory(Tests/TestProcess) +#add_subdirectory(Tests/TestTargetA) +#add_subdirectory(Tests/TestTargetB) +#add_subdirectory(Tests/TestTargetC) +#add_subdirectory(Tests/TestTargetD) +# #ly_add_target( # NAME TestImpact.Runtime.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} # NAMESPACE AZ From 3b249844d538cf10479eee5d869e50c8f225e353 Mon Sep 17 00:00:00 2001 From: moudgils Date: Thu, 10 Jun 2021 11:02:45 -0700 Subject: [PATCH 131/233] Minor formatting cleanup --- .../PostProcessing/LuminanceHeatmap.azsl | 2 +- .../Metal/Code/Source/RHI/ArgumentBuffer.cpp | 28 +++++++++++++----- .../Metal/Code/Source/RHI/ArgumentBuffer.h | 2 +- .../RHI/Metal/Code/Source/RHI/CommandList.cpp | 29 +++++++++++++++---- 4 files changed, 47 insertions(+), 14 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl index 4559b6c9ef..4238392004 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl @@ -1,4 +1,4 @@ - /* +/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp index 068bc5f162..680481d417 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp @@ -399,13 +399,17 @@ namespace AZ { if(RHI::CheckBitsAny(srgResourcesVisInfo.m_constantDataStageMask, RHI::ShaderStageMask::Compute)) { - resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArray[resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArrayLen++] = m_constantBuffer.GetGpuAddress>(); + uint16_t arrayIndex = resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArrayLen; + resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArray[arrayIndex] = m_constantBuffer.GetGpuAddress>(); + resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArrayLen++; } else { MTLRenderStages mtlRenderStages = GetRenderStages(srgResourcesVisInfo.m_constantDataStageMask); AZStd::pair key = AZStd::make_pair(MTLResourceUsageRead, mtlRenderStages); - resourcesToMakeResidentGraphics[key].m_resourceArray[resourcesToMakeResidentGraphics[key].m_resourceArrayLen++] = m_constantBuffer.GetGpuAddress>(); + uint16_t arrayIndex = resourcesToMakeResidentGraphics[key].m_resourceArrayLen; + resourcesToMakeResidentGraphics[key].m_resourceArray[arrayIndex] = m_constantBuffer.GetGpuAddress>(); + resourcesToMakeResidentGraphics[key].m_resourceArrayLen++; } } } @@ -451,7 +455,9 @@ namespace AZ } } - void ArgumentBuffer::CollectResourcesForCompute(id encoder, const ResourceBindingsSet& resourceBindingDataSet, ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const + void ArgumentBuffer::CollectResourcesForCompute(id encoder, + const ResourceBindingsSet& resourceBindingDataSet, + ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const { for (const auto& resourceBindingData : resourceBindingDataSet) { @@ -474,11 +480,16 @@ namespace AZ AZ_Assert(false, "Undefined Resource type"); } } - resourcesToMakeResidentMap[resourceUsage].m_resourceArray[resourcesToMakeResidentMap[resourceUsage].m_resourceArrayLen++] = resourceBindingData.m_resourcPtr->GetGpuAddress>(); + uint16_t arrayIndex = resourcesToMakeResidentMap[resourceUsage].m_resourceArrayLen; + resourcesToMakeResidentMap[resourceUsage].m_resourceArray[arrayIndex] = resourceBindingData.m_resourcPtr->GetGpuAddress>(); + resourcesToMakeResidentMap[resourceUsage].m_resourceArrayLen++; } } - void ArgumentBuffer::CollectResourcesForGraphics(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet, GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const + void ArgumentBuffer::CollectResourcesForGraphics(id encoder, + RHI::ShaderStageMask visShaderMask, + const ResourceBindingsSet& resourceBindingDataSet, + GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const { MTLRenderStages mtlRenderStages = GetRenderStages(visShaderMask); @@ -503,8 +514,11 @@ namespace AZ AZ_Assert(false, "Undefined Resource type"); } } - AZStd::pair key = AZStd::make_pair(resourceUsage, mtlRenderStages); - resourcesToMakeResidentMap[key].m_resourceArray[resourcesToMakeResidentMap[key].m_resourceArrayLen++] = resourceBindingData.m_resourcPtr->GetGpuAddress>(); + + AZStd::pair key = AZStd::make_pair(resourceUsage, mtlRenderStages); + uint16_t arrayIndex = resourcesToMakeResidentMap[key].m_resourceArrayLen; + resourcesToMakeResidentMap[key].m_resourceArray[arrayIndex] = resourceBindingData.m_resourcPtr->GetGpuAddress>(); + resourcesToMakeResidentMap[key].m_resourceArrayLen++; } } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h index 7cc77ae478..0825c07654 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h @@ -123,7 +123,7 @@ namespace AZ struct MetalResourceArray { AZStd::array, MaxEntriesInArgTable> m_resourceArray; - int m_resourceArrayLen = 0; + uint16_t m_resourceArrayLen = 0; }; //Map to cache all the resources based on the usage as we can batch all the resources for a given usage using ComputeResourcesToMakeResidentMap = AZStd::unordered_map; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp index 5546be1bd9..7f65c9ea47 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp @@ -347,18 +347,35 @@ namespace AZ //For graphics and compute encoder bind all the argument buffers if(m_commandEncoderType == CommandEncoderType::Render) { - BindArgumentBuffers(RHI::ShaderStage::Vertex, bufferVertexRegisterIdMin, bufferVertexRegisterIdMax, mtlVertexArgBuffers, mtlVertexArgBufferOffsets); - BindArgumentBuffers(RHI::ShaderStage::Fragment, bufferFragmentOrComputeRegisterIdMin, bufferFragmentOrComputeRegisterIdMax, mtlFragmentOrComputeArgBuffers, mtlFragmentOrComputeArgBufferOffsets); + BindArgumentBuffers(RHI::ShaderStage::Vertex, + bufferVertexRegisterIdMin, + bufferVertexRegisterIdMax, + mtlVertexArgBuffers, + mtlVertexArgBufferOffsets); + + BindArgumentBuffers(RHI::ShaderStage::Fragment, + bufferFragmentOrComputeRegisterIdMin, + bufferFragmentOrComputeRegisterIdMax, + mtlFragmentOrComputeArgBuffers, + mtlFragmentOrComputeArgBufferOffsets); } else if(m_commandEncoderType == CommandEncoderType::Compute) { - BindArgumentBuffers(RHI::ShaderStage::Compute, bufferFragmentOrComputeRegisterIdMin, bufferFragmentOrComputeRegisterIdMax, mtlFragmentOrComputeArgBuffers, mtlFragmentOrComputeArgBufferOffsets); + BindArgumentBuffers(RHI::ShaderStage::Compute, + bufferFragmentOrComputeRegisterIdMin, + bufferFragmentOrComputeRegisterIdMax, + mtlFragmentOrComputeArgBuffers, + mtlFragmentOrComputeArgBufferOffsets); } return true; } - void CommandList::BindArgumentBuffers(RHI::ShaderStage shaderStage, uint16_t registerIdMin, uint16_t registerIdMax, MetalArgumentBufferArray& mtlArgBuffers, MetalArgumentBufferArrayOffsets mtlArgBufferOffsets) + void CommandList::BindArgumentBuffers(RHI::ShaderStage shaderStage, + uint16_t registerIdMin, + uint16_t registerIdMax, + MetalArgumentBufferArray& mtlArgBuffers, + MetalArgumentBufferArrayOffsets mtlArgBufferOffsets) { //Metal Api only lets you bind multiple argument buffers in an array as long as there are no gaps in the array //In order to accomodate that we break up the calls when a gap is noticed in the array and reconfigure the NSRange. @@ -609,7 +626,9 @@ namespace AZ } } id renderEncoder = GetEncoder>(); - [renderEncoder setVertexBuffers: mtlStreamBuffers.data() offsets: mtlStreamBufferOffsets.data() withRange: range]; + [renderEncoder setVertexBuffers: mtlStreamBuffers.data() + offsets: mtlStreamBufferOffsets.data() + withRange: range]; } } From bbae6490d974c8d7084a9c615be5d0334f2b19e6 Mon Sep 17 00:00:00 2001 From: srikappa-amzn Date: Thu, 10 Jun 2021 14:38:14 -0700 Subject: [PATCH 132/233] Enabled LyTestTools trait only for windows and mac --- cmake/Platform/Android/PAL_android.cmake | 1 + cmake/Platform/Linux/PAL_linux.cmake | 1 + cmake/Platform/Mac/PAL_mac.cmake | 1 + cmake/Platform/Windows/PAL_windows.cmake | 1 + cmake/Platform/iOS/PAL_ios.cmake | 1 + scripts/ctest/CMakeLists.txt | 26 +++++++++++++----------- 6 files changed, 19 insertions(+), 12 deletions(-) diff --git a/cmake/Platform/Android/PAL_android.cmake b/cmake/Platform/Android/PAL_android.cmake index 5f35767f98..dd61e35e53 100644 --- a/cmake/Platform/Android/PAL_android.cmake +++ b/cmake/Platform/Android/PAL_android.cmake @@ -24,6 +24,7 @@ ly_set(PAL_TRAIT_BUILD_CPACK_SUPPORTED FALSE) # Test library support ly_set(PAL_TRAIT_TEST_GOOGLE_TEST_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_GOOGLE_BENCHMARK_SUPPORTED FALSE) +ly_set(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_PYTEST_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_TARGET_TYPE MODULE) diff --git a/cmake/Platform/Linux/PAL_linux.cmake b/cmake/Platform/Linux/PAL_linux.cmake index 40b73adcec..c15f2bada9 100644 --- a/cmake/Platform/Linux/PAL_linux.cmake +++ b/cmake/Platform/Linux/PAL_linux.cmake @@ -24,6 +24,7 @@ ly_set(PAL_TRAIT_BUILD_CPACK_SUPPORTED FALSE) # Test library support ly_set(PAL_TRAIT_TEST_GOOGLE_TEST_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_GOOGLE_BENCHMARK_SUPPORTED TRUE) +ly_set(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_PYTEST_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_TARGET_TYPE MODULE) diff --git a/cmake/Platform/Mac/PAL_mac.cmake b/cmake/Platform/Mac/PAL_mac.cmake index 988d36af14..f49578a83a 100644 --- a/cmake/Platform/Mac/PAL_mac.cmake +++ b/cmake/Platform/Mac/PAL_mac.cmake @@ -24,6 +24,7 @@ ly_set(PAL_TRAIT_BUILD_CPACK_SUPPORTED FALSE) # Test library support ly_set(PAL_TRAIT_TEST_GOOGLE_TEST_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_GOOGLE_BENCHMARK_SUPPORTED TRUE) +ly_set(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_PYTEST_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_TARGET_TYPE MODULE) diff --git a/cmake/Platform/Windows/PAL_windows.cmake b/cmake/Platform/Windows/PAL_windows.cmake index ddcc3a27c7..fbf65db63f 100644 --- a/cmake/Platform/Windows/PAL_windows.cmake +++ b/cmake/Platform/Windows/PAL_windows.cmake @@ -24,6 +24,7 @@ ly_set(PAL_TRAIT_BUILD_CPACK_SUPPORTED TRUE) # Test library support ly_set(PAL_TRAIT_TEST_GOOGLE_TEST_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_GOOGLE_BENCHMARK_SUPPORTED TRUE) +ly_set(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_PYTEST_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_TARGET_TYPE MODULE) diff --git a/cmake/Platform/iOS/PAL_ios.cmake b/cmake/Platform/iOS/PAL_ios.cmake index a6828a1bc7..981bb9cab1 100644 --- a/cmake/Platform/iOS/PAL_ios.cmake +++ b/cmake/Platform/iOS/PAL_ios.cmake @@ -24,6 +24,7 @@ ly_set(PAL_TRAIT_BUILD_CPACK_SUPPORTED FALSE) # Test library support ly_set(PAL_TRAIT_TEST_GOOGLE_TEST_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_GOOGLE_BENCHMARK_SUPPORTED FALSE) +ly_set(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_PYTEST_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_TARGET_TYPE MODULE) diff --git a/scripts/ctest/CMakeLists.txt b/scripts/ctest/CMakeLists.txt index 575be53cc7..c4f86087b1 100644 --- a/scripts/ctest/CMakeLists.txt +++ b/scripts/ctest/CMakeLists.txt @@ -21,18 +21,20 @@ endif() ################################################################################ foreach(suite_name ${LY_TEST_GLOBAL_KNOWN_SUITE_NAMES}) - ly_add_pytest( - NAME pytest_sanity_${suite_name}_no_gpu - PATH ${CMAKE_CURRENT_LIST_DIR}/sanity_test.py - TEST_SUITE ${suite_name} - ) - - ly_add_pytest( - NAME pytest_sanity_${suite_name}_requires_gpu - PATH ${CMAKE_CURRENT_LIST_DIR}/sanity_test.py - TEST_SUITE ${suite_name} - TEST_REQUIRES gpu - ) + if(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED) + ly_add_pytest( + NAME pytest_sanity_${suite_name}_no_gpu + PATH ${CMAKE_CURRENT_LIST_DIR}/sanity_test.py + TEST_SUITE ${suite_name} + ) + + ly_add_pytest( + NAME pytest_sanity_${suite_name}_requires_gpu + PATH ${CMAKE_CURRENT_LIST_DIR}/sanity_test.py + TEST_SUITE ${suite_name} + TEST_REQUIRES gpu + ) + endif() endforeach() # EPB Sanity test is being registered here to validate that the ly_add_editor_python_test function works. From b18b03b8fb8d1a79d14fead2475fea25d1c4b660 Mon Sep 17 00:00:00 2001 From: moudgils Date: Thu, 10 Jun 2021 14:40:52 -0700 Subject: [PATCH 133/233] Added comments and more format fixes --- .../Shaders/MorphTargets/MorphTargetSRG.azsli | 3 +++ .../LuminanceHistogramGenerator.azsl | 4 +++ .../RHI/DX12/Code/Source/RHI/Conversions.cpp | 10 +++++++ .../Metal/Code/Source/RHI/ArgumentBuffer.cpp | 26 +++++++++---------- .../Metal/Code/Source/RHI/ArgumentBuffer.h | 13 +++++++--- .../RHI/Metal/Code/Source/RHI/CommandList.h | 6 ++++- .../RHI/Metal/Code/Source/RHI/Conversions.cpp | 10 +++++++ .../RHI/Vulkan/Code/Source/RHI/Conversion.cpp | 6 +++++ 8 files changed, 60 insertions(+), 18 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli index 83193c8559..f4b1beeb3e 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli @@ -16,6 +16,9 @@ ShaderResourceGroup MorphTargetPassSrg : SRG_PerPass { + //Since we do Interlocked atomic operations on this buffer it can not be RWBuffer due to broken MetalSL generation. + //It stems from the fact that typed buffers gets converted to textures and that breaks with atomic operations. + //In future we can handle this under the hood via our metal shader pipeline RWStructuredBuffer m_accumulatedDeltas; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl index 9d01a12fd6..ee95515a2a 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl @@ -20,6 +20,10 @@ ShaderResourceGroup PassSrg : SRG_PerPass { Texture2D m_inputTexture; + + //Since we do Interlocked atomic operations on this buffer it can not be RWBuffer due to broken MetalSL generation. + //It stems from the fact that typed buffers gets converted to textures and that breaks with atomic operations. + //In future we can handle this under the hood via our metal shader pipeline RWStructuredBuffer m_outputTexture; } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp index 73d80ed78c..9776734158 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp @@ -1360,6 +1360,16 @@ namespace AZ uint32_t ConvertColorWriteMask(uint8_t writeMask) { uint32_t dflags = 0; + if(writeMask == 0) + { + return dflags; + } + + if(RHI::CheckBitsAll(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) + { + return D3D12_COLOR_WRITE_ENABLE_ALL; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) { dflags |= D3D12_COLOR_WRITE_ENABLE_RED; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp index 680481d417..49ff4146fc 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp @@ -397,19 +397,18 @@ namespace AZ uint8_t numBitsSet = RHI::CountBitsSet(static_cast(srgResourcesVisInfo.m_constantDataStageMask)); if( numBitsSet > 0) { + id mtlconstantBufferResource = m_constantBuffer.GetGpuAddress>(); if(RHI::CheckBitsAny(srgResourcesVisInfo.m_constantDataStageMask, RHI::ShaderStageMask::Compute)) { - uint16_t arrayIndex = resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArrayLen; - resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArray[arrayIndex] = m_constantBuffer.GetGpuAddress>(); - resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArrayLen++; + uint16_t arrayIndex = resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArrayLen++; + resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArray[arrayIndex] = mtlconstantBufferResource; } else { MTLRenderStages mtlRenderStages = GetRenderStages(srgResourcesVisInfo.m_constantDataStageMask); AZStd::pair key = AZStd::make_pair(MTLResourceUsageRead, mtlRenderStages); - uint16_t arrayIndex = resourcesToMakeResidentGraphics[key].m_resourceArrayLen; - resourcesToMakeResidentGraphics[key].m_resourceArray[arrayIndex] = m_constantBuffer.GetGpuAddress>(); - resourcesToMakeResidentGraphics[key].m_resourceArrayLen++; + uint16_t arrayIndex = resourcesToMakeResidentGraphics[key].m_resourceArrayLen++; + resourcesToMakeResidentGraphics[key].m_resourceArray[arrayIndex] = mtlconstantBufferResource; } } } @@ -431,7 +430,8 @@ namespace AZ } else { - AZ_Assert(RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Vertex) || RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Fragment), "The visibility mask %i is not set for Vertex or fragment stage", visMaskIt->second); + bool isBoundToGraphics = RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Vertex) || RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Fragment); + AZ_Assert(isBoundToGraphics, "The visibility mask %i is not set for Vertex or fragment stage", visMaskIt->second); CollectResourcesForGraphics(commandEncoder, visMaskIt->second, it.second, resourcesToMakeResidentGraphics); } } @@ -480,9 +480,9 @@ namespace AZ AZ_Assert(false, "Undefined Resource type"); } } - uint16_t arrayIndex = resourcesToMakeResidentMap[resourceUsage].m_resourceArrayLen; - resourcesToMakeResidentMap[resourceUsage].m_resourceArray[arrayIndex] = resourceBindingData.m_resourcPtr->GetGpuAddress>(); - resourcesToMakeResidentMap[resourceUsage].m_resourceArrayLen++; + uint16_t arrayIndex = resourcesToMakeResidentMap[resourceUsage].m_resourceArrayLen++; + id mtlResourceToBind = resourceBindingData.m_resourcPtr->GetGpuAddress>(); + resourcesToMakeResidentMap[resourceUsage].m_resourceArray[arrayIndex] = mtlResourceToBind; } } @@ -516,9 +516,9 @@ namespace AZ } AZStd::pair key = AZStd::make_pair(resourceUsage, mtlRenderStages); - uint16_t arrayIndex = resourcesToMakeResidentMap[key].m_resourceArrayLen; - resourcesToMakeResidentMap[key].m_resourceArray[arrayIndex] = resourceBindingData.m_resourcPtr->GetGpuAddress>(); - resourcesToMakeResidentMap[key].m_resourceArrayLen++; + uint16_t arrayIndex = resourcesToMakeResidentMap[key].m_resourceArrayLen++; + id mtlResourceToBind = resourceBindingData.m_resourcPtr->GetGpuAddress>(); + resourcesToMakeResidentMap[key].m_resourceArray[arrayIndex] = mtlResourceToBind; } } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h index 0825c07654..c4cfd17390 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h @@ -126,12 +126,17 @@ namespace AZ uint16_t m_resourceArrayLen = 0; }; //Map to cache all the resources based on the usage as we can batch all the resources for a given usage - using ComputeResourcesToMakeResidentMap = AZStd::unordered_map; + using ComputeResourcesToMakeResidentMap = AZStd::unordered_map; //Map to cache all the resources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage - using GraphicsResourcesToMakeResidentMap = AZStd::unordered_map, MetalResourceArray>; + using GraphicsResourcesToMakeResidentMap = AZStd::unordered_map, MetalResourceArray>; - void CollectResourcesForCompute(id encoder, const ResourceBindingsSet& resourceBindingData, ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; - void CollectResourcesForGraphics(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet, GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; + void CollectResourcesForCompute(id encoder, + const ResourceBindingsSet& resourceBindingData, + ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; + void CollectResourcesForGraphics(id encoder, + RHI::ShaderStageMask visShaderMask, + const ResourceBindingsSet& resourceBindingDataSet, + GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; //! Use visibility information to call UseResource on all resources for this Argument Buffer void ApplyUseResource(id encoder, const ResourceBindingsMap& resourceMap, diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h index 9dd14cd175..8674124cc2 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h @@ -104,7 +104,11 @@ namespace AZ using MetalArgumentBufferArray = AZStd::array, RHI::Limits::Pipeline::ShaderResourceGroupCountMax>; using MetalArgumentBufferArrayOffsets = AZStd::array; - void BindArgumentBuffers(RHI::ShaderStage shaderStage, uint16_t registerIdMin, uint16_t registerIdMax, MetalArgumentBufferArray& mtlArgBuffers, MetalArgumentBufferArrayOffsets mtlArgBufferOffsets); + void BindArgumentBuffers(RHI::ShaderStage shaderStage, + uint16_t registerIdMin, + uint16_t registerIdMax, + MetalArgumentBufferArray& mtlArgBuffers, + MetalArgumentBufferArrayOffsets mtlArgBufferOffsets); ShaderResourceBindings& GetShaderResourceBindingsByPipelineType(RHI::PipelineStateType pipelineType); diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp index e41ba9bf2a..81e589e62e 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp @@ -458,6 +458,16 @@ namespace AZ MTLColorWriteMask ConvertColorWriteMask(AZ::u8 writeMask) { MTLColorWriteMask colorMask = MTLColorWriteMaskNone; + if(writeMask == 0) + { + return colorMask; + } + + if(RHI::CheckBitsAll(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) + { + return MTLColorWriteMaskAll; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) { colorMask |= MTLColorWriteMaskRed; diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp index 7945dd2894..5ce9731506 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp @@ -334,6 +334,12 @@ namespace AZ VkColorComponentFlags ConvertComponentFlags(uint8_t sflags) { VkColorComponentFlags dflags = 0; + + if(sflags == 0) + { + return dflags; + } + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) { dflags |= VK_COLOR_COMPONENT_R_BIT; From 963894c7e3edbbca9f2891a6412b56a9c92eae31 Mon Sep 17 00:00:00 2001 From: moudgils Date: Thu, 10 Jun 2021 15:00:42 -0700 Subject: [PATCH 134/233] Modified the WriteColoorMask --- Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp | 10 +++++----- Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp | 10 +++++----- Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp | 13 +++++++++---- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp index 9776734158..8c7b9f4389 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp @@ -1365,24 +1365,24 @@ namespace AZ return dflags; } - if(RHI::CheckBitsAll(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) + if(RHI::CheckBitsAll(writeMask, RHI::WriteChannelMask::ColorWriteMaskAll)) { return D3D12_COLOR_WRITE_ENABLE_ALL; } - if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) + if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskRed)) { dflags |= D3D12_COLOR_WRITE_ENABLE_RED; } - if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) + if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskGreen)) { dflags |= D3D12_COLOR_WRITE_ENABLE_GREEN; } - if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) + if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskBlue)) { dflags |= D3D12_COLOR_WRITE_ENABLE_BLUE; } - if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) + if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskAlpha)) { dflags |= D3D12_COLOR_WRITE_ENABLE_ALPHA; } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp index 81e589e62e..5b153fd648 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp @@ -463,24 +463,24 @@ namespace AZ return colorMask; } - if(RHI::CheckBitsAll(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) + if(RHI::CheckBitsAll(writeMask, RHI::WriteChannelMask::ColorWriteMaskAll)) { return MTLColorWriteMaskAll; } - if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) + if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskRed)) { colorMask |= MTLColorWriteMaskRed; } - if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) + if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskGreen)) { colorMask |= MTLColorWriteMaskGreen; } - if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) + if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskBlue)) { colorMask |= MTLColorWriteMaskBlue; } - if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) + if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskAlpha)) { colorMask |= MTLColorWriteMaskAlpha; } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp index 5ce9731506..1ba002748b 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp @@ -340,19 +340,24 @@ namespace AZ return dflags; } - if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) + if(RHI::CheckBitsAll(writeMask, RHI::WriteChannelMask::ColorWriteMaskAll)) + { + return VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + } + + if (RHI::CheckBitsAny(sflags, RHI::WriteChannelMask::ColorWriteMaskRed)) { dflags |= VK_COLOR_COMPONENT_R_BIT; } - if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) + if (RHI::CheckBitsAny(sflags, RHI::WriteChannelMask::ColorWriteMaskGreen)) { dflags |= VK_COLOR_COMPONENT_G_BIT; } - if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) + if (RHI::CheckBitsAny(sflags, RHI::WriteChannelMask::ColorWriteMaskBlue)) { dflags |= VK_COLOR_COMPONENT_B_BIT; } - if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) + if (RHI::CheckBitsAny(sflags, RHI::WriteChannelMask::ColorWriteMaskAlpha)) { dflags |= VK_COLOR_COMPONENT_A_BIT; } From c0cfe239fe8c4b4aa26a85a34ee188177f6a4bec Mon Sep 17 00:00:00 2001 From: moudgils Date: Thu, 10 Jun 2021 15:03:08 -0700 Subject: [PATCH 135/233] Modify WritecolorMask --- .../Include/Atom/RHI.Reflect/RenderStates.h | 18 +++++------------- .../RHI/DX12/Code/Source/RHI/Conversions.cpp | 10 +++++----- .../RHI/Metal/Code/Source/RHI/Conversions.cpp | 10 +++++----- .../RHI/Vulkan/Code/Source/RHI/Conversion.cpp | 10 +++++----- 4 files changed, 20 insertions(+), 28 deletions(-) diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h index d5c55e2c7f..651be829b5 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h @@ -160,21 +160,13 @@ namespace AZ StencilState m_stencil; }; - enum class WriteChannel : uint32_t - { - ColorWriteMaskRed = 0, - ColorWriteMaskGreen, - ColorWriteMaskBlue, - ColorWriteMaskAlpha, - }; - - enum class WriteChannelMask : uint32_t + enum class WriteChannelMask : uint8_t { ColorWriteMaskNone = 0, - ColorWriteMaskRed = AZ_BIT(static_cast(WriteChannel::ColorWriteMaskRed)), - ColorWriteMaskGreen = AZ_BIT(static_cast(WriteChannel::ColorWriteMaskGreen)), - ColorWriteMaskBlue = AZ_BIT(static_cast(WriteChannel::ColorWriteMaskBlue)), - ColorWriteMaskAlpha = AZ_BIT(static_cast(WriteChannel::ColorWriteMaskAlpha)), + ColorWriteMaskRed = AZ_BIT(0), + ColorWriteMaskGreen = AZ_BIT(1), + ColorWriteMaskBlue = AZ_BIT(2), + ColorWriteMaskAlpha = AZ_BIT(3), ColorWriteMaskAll = ColorWriteMaskRed | ColorWriteMaskGreen | ColorWriteMaskBlue | ColorWriteMaskAlpha }; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp index 8c7b9f4389..9776734158 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp @@ -1365,24 +1365,24 @@ namespace AZ return dflags; } - if(RHI::CheckBitsAll(writeMask, RHI::WriteChannelMask::ColorWriteMaskAll)) + if(RHI::CheckBitsAll(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) { return D3D12_COLOR_WRITE_ENABLE_ALL; } - if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskRed)) + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) { dflags |= D3D12_COLOR_WRITE_ENABLE_RED; } - if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskGreen)) + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) { dflags |= D3D12_COLOR_WRITE_ENABLE_GREEN; } - if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskBlue)) + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) { dflags |= D3D12_COLOR_WRITE_ENABLE_BLUE; } - if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskAlpha)) + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) { dflags |= D3D12_COLOR_WRITE_ENABLE_ALPHA; } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp index 5b153fd648..81e589e62e 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp @@ -463,24 +463,24 @@ namespace AZ return colorMask; } - if(RHI::CheckBitsAll(writeMask, RHI::WriteChannelMask::ColorWriteMaskAll)) + if(RHI::CheckBitsAll(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) { return MTLColorWriteMaskAll; } - if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskRed)) + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) { colorMask |= MTLColorWriteMaskRed; } - if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskGreen)) + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) { colorMask |= MTLColorWriteMaskGreen; } - if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskBlue)) + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) { colorMask |= MTLColorWriteMaskBlue; } - if (RHI::CheckBitsAny(writeMask, RHI::WriteChannelMask::ColorWriteMaskAlpha)) + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) { colorMask |= MTLColorWriteMaskAlpha; } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp index 1ba002748b..5a382267bb 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp @@ -340,24 +340,24 @@ namespace AZ return dflags; } - if(RHI::CheckBitsAll(writeMask, RHI::WriteChannelMask::ColorWriteMaskAll)) + if(RHI::CheckBitsAll(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) { return VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; } - if (RHI::CheckBitsAny(sflags, RHI::WriteChannelMask::ColorWriteMaskRed)) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) { dflags |= VK_COLOR_COMPONENT_R_BIT; } - if (RHI::CheckBitsAny(sflags, RHI::WriteChannelMask::ColorWriteMaskGreen)) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) { dflags |= VK_COLOR_COMPONENT_G_BIT; } - if (RHI::CheckBitsAny(sflags, RHI::WriteChannelMask::ColorWriteMaskBlue)) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) { dflags |= VK_COLOR_COMPONENT_B_BIT; } - if (RHI::CheckBitsAny(sflags, RHI::WriteChannelMask::ColorWriteMaskAlpha)) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) { dflags |= VK_COLOR_COMPONENT_A_BIT; } From 7dd98c4e2b12c318d6533e254a52139b108e3589 Mon Sep 17 00:00:00 2001 From: moudgils Date: Thu, 10 Jun 2021 15:17:17 -0700 Subject: [PATCH 136/233] Missed a change --- Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp index 5a382267bb..8cad9b1f56 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp @@ -340,7 +340,7 @@ namespace AZ return dflags; } - if(RHI::CheckBitsAll(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) + if(RHI::CheckBitsAll(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) { return VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; } From 7590ef04b26c9c68076dba8f5446fe8538a0caa6 Mon Sep 17 00:00:00 2001 From: jiaweig Date: Thu, 10 Jun 2021 16:48:48 -0700 Subject: [PATCH 137/233] Increase the platform limit to handle more objects. --- .../Assets/Config/Platform/Windows/DX12/PlatformLimits.azasset | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/Feature/Common/Assets/Config/Platform/Windows/DX12/PlatformLimits.azasset b/Gems/Atom/Feature/Common/Assets/Config/Platform/Windows/DX12/PlatformLimits.azasset index 7e71111ef1..3c88544e62 100644 --- a/Gems/Atom/Feature/Common/Assets/Config/Platform/Windows/DX12/PlatformLimits.azasset +++ b/Gems/Atom/Feature/Common/Assets/Config/Platform/Windows/DX12/PlatformLimits.azasset @@ -8,7 +8,7 @@ "$type": "DX12::PlatformLimitsDescriptor", "m_descriptorHeapLimits": { - "DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [16384, 262144], + "DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [1000000, 1000000], "DESCRIPTOR_HEAP_TYPE_SAMPLER": [2048, 2048], "DESCRIPTOR_HEAP_TYPE_RTV": [2048, 0], "DESCRIPTOR_HEAP_TYPE_DSV": [2048, 0] From f6f90ee4c2f4a34b8691bb698760e77f18274718 Mon Sep 17 00:00:00 2001 From: moudgils Date: Thu, 10 Jun 2021 21:45:38 -0700 Subject: [PATCH 138/233] Change ConvertColorWriteMask return type to uint8_t --- Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp | 4 ++-- Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp index 9776734158..a29c7ae402 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp @@ -1357,9 +1357,9 @@ namespace AZ return table[(uint32_t)mask]; } - uint32_t ConvertColorWriteMask(uint8_t writeMask) + uint8_t ConvertColorWriteMask(uint8_t writeMask) { - uint32_t dflags = 0; + uint8_t dflags = 0; if(writeMask == 0) { return dflags; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h index 887598de79..640a5fef31 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h @@ -165,6 +165,6 @@ namespace AZ D3D12_SHADER_VISIBILITY shaderVisibility, D3D12_STATIC_SAMPLER_DESC& staticSamplerDesc); - uint32_t ConvertColorWriteMask(uint8_t writeMask); + uint8_t ConvertColorWriteMask(uint8_t writeMask); } } From ef87ce094a720319bbdb09b25761b7e7da8a6899 Mon Sep 17 00:00:00 2001 From: antonmic Date: Fri, 11 Jun 2021 00:52:24 -0700 Subject: [PATCH 139/233] Fixing pass binding issue that breaks certain ASV screenshot tests. Should also fix a crash on mac that was reported. --- .../Code/Include/Atom/RPI.Public/Pass/Pass.h | 6 +++++ .../RPI/Code/Source/RPI.Public/Pass/Pass.cpp | 24 ++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h index 9d0b36d126..a4a26c3070 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h @@ -265,6 +265,12 @@ namespace AZ // Update all bindings on this pass that are connected to bindings on other passes void UpdateConnectedBindings(); + // Update input and input/output bindings on this pass that are connected to bindings on other passes + void UpdateConnectedInputBindings(); + + // Update output bindings on this pass that are connected to bindings on other passes + void UpdateConnectedOutputBindings(); + protected: explicit Pass(const PassDescriptor& descriptor); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index 99a6e23914..865a13e13d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -1036,6 +1036,26 @@ namespace AZ } } + void Pass::UpdateConnectedInputBindings() + { + for (uint8_t idx : m_inputBindingIndices) + { + UpdateConnectedBinding(m_attachmentBindings[idx]); + } + for (uint8_t idx : m_inputOutputBindingIndices) + { + UpdateConnectedBinding(m_attachmentBindings[idx]); + } + } + + void Pass::UpdateConnectedOutputBindings() + { + for (uint8_t idx : m_outputBindingIndices) + { + UpdateConnectedBinding(m_attachmentBindings[idx]); + } + } + // --- Queuing functions with PassSystem --- void Pass::QueueForBuildAndInitialization() @@ -1264,7 +1284,7 @@ namespace AZ AZ_Assert(m_state == PassState::Idle, "Pass::FrameBegin - Pass [%s] is attempting to render, but is not in the Idle state.", m_path.GetCStr()); m_state = PassState::Rendering; - UpdateConnectedBindings(); + UpdateConnectedInputBindings(); UpdateOwnedAttachments(); CreateTransientAttachments(params.m_frameGraphBuilder->GetAttachmentDatabase()); @@ -1273,6 +1293,8 @@ namespace AZ // FrameBeginInternal needs to be the last function be called in FrameBegin because its implementation expects // all the attachments are imported to database (for example, ImageAttachmentPreview) FrameBeginInternal(params); + + UpdateConnectedOutputBindings(); } void Pass::FrameEnd() From ed47bb07abe8a9f00dcd879dad1584769f8c8302 Mon Sep 17 00:00:00 2001 From: John Jones-Steele Date: Fri, 11 Jun 2021 14:52:01 +0100 Subject: [PATCH 140/233] Menu icon behaviour fixed. --- .../AzQtComponents/Components/Style.cpp | 24 +++++++++++++++++++ .../Source/Editor/UI/AWSCoreEditorMenu.cpp | 1 + 2 files changed, 25 insertions(+) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp index 0f58f06420..137df2b4bd 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp @@ -493,6 +493,30 @@ namespace AzQtComponents } } break; + case CE_MenuItem: + { + const QMenu* menu = qobject_cast(widget); + QAction* action = menu->activeAction(); + if (action) + { + QMenu* subMenu = action->menu(); + if (subMenu) + { + QVariant noHover = subMenu->property("noHover"); + if (noHover.isValid() && noHover.toBool()) + { + // First draw as standard to get the correct hover background for the complete control. + QProxyStyle::drawControl(element, option, painter, widget); + // Now draw the icon as non-hovered so control behaves as designed. + const QStyleOptionMenuItem* opt = qstyleoption_cast(option); + QStyleOptionMenuItem myOpt = *(const_cast(opt)); + myOpt.state &= ~QStyle::State_Selected; + return QProxyStyle::drawControl(element, &myOpt, painter, widget); + } + } + } + } + break; } return QProxyStyle::drawControl(element, option, painter, widget); diff --git a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp index c319788547..3375f85a7a 100644 --- a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp +++ b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp @@ -209,6 +209,7 @@ namespace AWSCore { QMenu* subMenu = new QMenu(QObject::tr(menuText.c_str())); subMenu->setIcon(QIcon(QString(":/Notifications/checkmark.svg"))); + subMenu->setProperty("noHover", true); this->insertMenu(*itr, subMenu); this->removeAction(*itr); return subMenu; From f775ba7df83730870e71644da835a5660cc72be7 Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Fri, 11 Jun 2021 08:12:49 -0700 Subject: [PATCH 141/233] Provide more informative error messages on android related environment / device related issues (#1261) - If gradle is installed, but JAVA_HOME is not set properly, no detail message is given. Bubble up the error message as part of the description - When deploying a newer API Level APK (30) to an API Level 29 device, a python callstack is given without any detail of the error. Now it will report the actual error that is from the adb call so the user can act upon it --- .../Platform/Android/android_deployment.py | 14 +++++++++----- cmake/Tools/common.py | 18 +++++++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/cmake/Tools/Platform/Android/android_deployment.py b/cmake/Tools/Platform/Android/android_deployment.py index eb8361ec18..82d14a2746 100755 --- a/cmake/Tools/Platform/Android/android_deployment.py +++ b/cmake/Tools/Platform/Android/android_deployment.py @@ -184,11 +184,15 @@ class AndroidDeployment(object): call_arguments.extend(arg_list) - output = subprocess.check_output(call_arguments, - shell=True, - stderr=subprocess.DEVNULL).decode(common.DEFAULT_TEXT_READ_ENCODING, - common.ENCODING_ERROR_HANDLINGS) - return output + try: + output = subprocess.check_output(call_arguments, + shell=True, + stderr=subprocess.PIPE).decode(common.DEFAULT_TEXT_READ_ENCODING, + common.ENCODING_ERROR_HANDLINGS) + return output + except subprocess.CalledProcessError as err: + raise common.LmbrCmdError(err.stderr.decode(common.DEFAULT_TEXT_READ_ENCODING, + common.ENCODING_ERROR_HANDLINGS)) def adb_shell(self, command, device_id): """ diff --git a/cmake/Tools/common.py b/cmake/Tools/common.py index 1990041c86..cf4631c227 100755 --- a/cmake/Tools/common.py +++ b/cmake/Tools/common.py @@ -274,6 +274,8 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too :return: Tuple of the resolved tool version and the resolved override tool path if provided """ + tool_source = tool_name + try: # Use either the provided gradle override or the gradle in the path environment if override_tool_path: @@ -306,18 +308,17 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too tool_desc = f"{tool_name} path provided in the command line argument '{argument_name}={override_tool_path}' " else: resolved_override_tool_path = None - tool_source = tool_name tool_desc = f"installed {tool_name} in the system path" # Extract the version and verify version_output = subprocess.check_output([tool_source, tool_version_argument], - shell=True).decode(DEFAULT_TEXT_READ_ENCODING, - ENCODING_ERROR_HANDLINGS) + shell=True, + stderr=subprocess.PIPE).decode(DEFAULT_TEXT_READ_ENCODING, + ENCODING_ERROR_HANDLINGS) version_match = tool_version_regex.search(version_output) if not version_match: raise RuntimeError() - # Since we are doing a compare, strip out any non-numeric and non . character from the version otherwise we will get a TypeError on the LooseVersion comparison result_version_str = re.sub(r"[^\.0-9]", "", str(version_match.group(1)).strip()) result_version = LooseVersion(result_version_str) @@ -331,7 +332,14 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too return result_version, resolved_override_tool_path - except (CalledProcessError, WindowsError, RuntimeError) as e: + except CalledProcessError as e: + error_msg = e.output.decode(DEFAULT_TEXT_READ_ENCODING, + ENCODING_ERROR_HANDLINGS) + raise LmbrCmdError(f"{tool_name} cannot be resolved or there was a problem determining its version number. " + f"Either make sure its in the system path environment or a valid path is passed in " + f"through the {argument_name} argument.\n{error_msg}", + ERROR_CODE_ERROR_NOT_SUPPORTED) + except (WindowsError, RuntimeError) as e: logging.error(f"Call to '{tool_source}' resulted in error: {e}") raise LmbrCmdError(f"{tool_name} cannot be resolved or there was a problem determining its version number. " f"Either make sure its in the system path environment or a valid path is passed in " From a995aee35a60d93ab3ffc826afe024a8325b0814 Mon Sep 17 00:00:00 2001 From: chcurran Date: Fri, 11 Jun 2021 09:47:44 -0700 Subject: [PATCH 142/233] Properties__Index and Properties__NewIndex upvalue counts no reflect removal of net binding component --- .../AzFramework/AzFramework/Script/ScriptComponent.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp b/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp index 4f8da821c1..d80094a071 100644 --- a/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp @@ -693,11 +693,11 @@ namespace AzFramework // set the __index so we can read values in case we change the script // after we export the component lua_pushliteral(lua, "__index"); - lua_pushcclosure(lua, &Internal::Properties__Index, 1); + lua_pushcclosure(lua, &Internal::Properties__Index, 0); lua_rawset(lua, -3); lua_pushliteral(lua, "__newindex"); - lua_pushcclosure(lua, &Internal::Properties__NewIndex, 1); + lua_pushcclosure(lua, &Internal::Properties__NewIndex, 0); lua_rawset(lua, -3); } lua_pop(lua, 1); // pop the properties table (or the nil value) @@ -900,11 +900,11 @@ namespace AzFramework // Ensure that this instance of Properties table has the proper __index and __newIndex metamethods. lua_newtable(lua); // This new table will become the Properties instance metatable. Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} lua_pushliteral(lua, "__index"); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index - lua_pushcclosure(lua, &Internal::Properties__Index, 1); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index function + lua_pushcclosure(lua, &Internal::Properties__Index, 0); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index function lua_rawset(lua, -3); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {__index=Internal::Properties__Index} lua_pushliteral(lua, "__newindex"); - lua_pushcclosure(lua, &Internal::Properties__NewIndex, 1); + lua_pushcclosure(lua, &Internal::Properties__NewIndex, 0); lua_rawset(lua, -3); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {__index=Internal::Properties__Index __newindex=Internal::Properties__NewIndex} lua_setmetatable(lua, -2); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {Meta{__index=Internal::Properties__Index __newindex=Internal::Properties__NewIndex} } From 0c7605c9b697522042f3161393d719dc76494ad8 Mon Sep 17 00:00:00 2001 From: Eric Phister <52085794+amzn-phist@users.noreply.github.com> Date: Fri, 11 Jun 2021 12:00:55 -0500 Subject: [PATCH 143/233] Update minimum required CMake version to 3.20 (#1253) * Update the minimum CMake version to 3.20 Sets the cmake_minimum_required calls to version 3.20 and updates the README.md to point at the general CMake download page instead of a stale link. * Remove unnecessary cmake minimum version It was using an old 3.0 version and can be removed. * Additional updates to CMake 3.20, build scripts Updates the version and remove logic to find a CMake in 3rdParty. * Removing backup path to ninja path in the build_ninja_windows.cmd The backup path for finding ninja was coming from the Perforce depot which isn't available for o3de builds. * Removing reference to 3rdParty Android SDK 29 from the build and run unit test script The Android SDK is not part of the new 3rdParty system and users are expected to install the Android SDK on their own in order to build the engine for Android. * Update the get_python scripts and README No longer try to append a CMake path to LY_3RDPARTY_PATH, but do still support LY_CMAKE_PATH because there are still uses of it. Remove mention of an LY_3RDPARTY_PATH-relative CMake path from the README.md. * Removing LY_NINJA_PATH from the build_ninja_windows.cmd Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- AutomatedTesting/CMakeLists.txt | 2 +- CMakeLists.txt | 13 ++-------- Code/Framework/AzAutoGen/CMakeLists.txt | 2 -- README.md | 4 ++-- .../DefaultProject/Template/CMakeLists.txt | 2 +- .../Android/generate_android_project.py | 2 +- python/get_python.bat | 13 ++++------ python/get_python.cmake | 2 +- python/get_python.sh | 22 +++++------------ .../Android/build_and_run_unit_tests.cmd | 3 --- scripts/build/Platform/Linux/env_linux.sh | 24 ++++--------------- scripts/build/Platform/Mac/env_mac.sh | 12 ++-------- .../Platform/Windows/build_ninja_windows.cmd | 11 +-------- .../build/Platform/Windows/env_windows.cmd | 13 ++-------- 14 files changed, 28 insertions(+), 97 deletions(-) diff --git a/AutomatedTesting/CMakeLists.txt b/AutomatedTesting/CMakeLists.txt index e239ba7674..9a870b1279 100644 --- a/AutomatedTesting/CMakeLists.txt +++ b/AutomatedTesting/CMakeLists.txt @@ -10,7 +10,7 @@ # if(NOT PROJECT_NAME) - cmake_minimum_required(VERSION 3.19) + cmake_minimum_required(VERSION 3.20) project(AutomatedTesting LANGUAGES C CXX VERSION 1.0.0.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ca9aeacb8..0585089325 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,17 +9,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# Cmake version 3.19 is the minimum version needed for all of Open 3D Engine's supported platforms -cmake_minimum_required(VERSION 3.19) - -# CMP0111 introduced in 3.19 has a bug that produces the policy to warn every time there is an -# INTERFACE IMPORTED library. We use this type of libraries for handling 3rdParty. The rest of -# the documentation states that INTERFACE IMPORTED libraries do not require to set locations, but -# the policy still warns about it. Issue: https://gitlab.kitware.com/cmake/cmake/-/issues/21470 -# The issue was fixed in 3.19.1 so we just disable the policy for 3.19 -if(CMAKE_VERSION VERSION_EQUAL 3.19) - cmake_policy(SET CMP0111 OLD) -endif() +# Cmake version 3.20 is the minimum version needed for all of Open 3D Engine's supported platforms +cmake_minimum_required(VERSION 3.20) include(cmake/LySet.cmake) include(cmake/Version.cmake) diff --git a/Code/Framework/AzAutoGen/CMakeLists.txt b/Code/Framework/AzAutoGen/CMakeLists.txt index 925e7aed29..4c79f268fe 100644 --- a/Code/Framework/AzAutoGen/CMakeLists.txt +++ b/Code/Framework/AzAutoGen/CMakeLists.txt @@ -9,8 +9,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -cmake_minimum_required(VERSION 3.0) - ly_add_target( NAME AzAutoGen HEADERONLY NAMESPACE AZ diff --git a/README.md b/README.md index 0c59837a62..7a9751529f 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ If you have the Git credential manager core or other credential helpers installe * Game Development with C++ * MSVC v142 - VS 2019 C++ x64/x86 * C++ 2019 redistributable update -* CMake 3.19.1 minimum: [https://cmake.org/files/LatestRelease/cmake-3.19.1-win64-x64.msi](https://cmake.org/files/LatestRelease/cmake-3.19.1-win64-x64.msi) +* CMake 3.20 minimum: [https://cmake.org/download/](https://cmake.org/download/) #### Optional @@ -105,7 +105,7 @@ If you have the Git credential manager core or other credential helpers installe 1. Install the following redistributables to the following: - Visual Studio and VC++ redistributable can be installed to any location - - CMake can be installed to any location, as long as it's available in the system path, otherwise it can be installed to: `<3rdParty Path>\CMake\3.19.1` + - CMake can be installed to any location, as long as it's available in the system path - WWise can be installed anywhere, but you will need to set an environment variable for CMake to detect it: `set LY_WWISE_INSTALL_PATH=` 1. Navigate into the repo folder, then download the python runtime with this command diff --git a/Templates/DefaultProject/Template/CMakeLists.txt b/Templates/DefaultProject/Template/CMakeLists.txt index 50e3a528e6..4dcfc5325b 100644 --- a/Templates/DefaultProject/Template/CMakeLists.txt +++ b/Templates/DefaultProject/Template/CMakeLists.txt @@ -12,7 +12,7 @@ # {END_LICENSE} if(NOT PROJECT_NAME) - cmake_minimum_required(VERSION 3.19) + cmake_minimum_required(VERSION 3.20) project(${Name} LANGUAGES C CXX VERSION 1.0.0.0 diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index d25b62dde8..5a52ac385e 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -48,7 +48,7 @@ def verify_gradle(override_gradle_path=None): CMAKE_ARGUMENT_NAME = '--cmake-install-path' -CMAKE_MIN_VERSION = LooseVersion('3.19.0') +CMAKE_MIN_VERSION = LooseVersion('3.20.0') CMAKE_VERSION_REGEX = re.compile(r'cmake version (\d+.\d+.?\d*)') CMAKE_EXECUTABLE = 'cmake' diff --git a/python/get_python.bat b/python/get_python.bat index e11c4ab92f..b3c3bf9cfb 100644 --- a/python/get_python.bat +++ b/python/get_python.bat @@ -32,18 +32,15 @@ IF !ERRORLEVEL!==0 ( cd /D %CMD_DIR%\.. REM IF you update this logic, update it in scripts/build/Platform/Windows/env_windows.cmd -REM If cmake is not found on path, try a known location, using LY_CMAKE_PATH as the first fallback +REM If cmake is not found on path, try a known location at LY_CMAKE_PATH where /Q cmake IF NOT !ERRORLEVEL!==0 ( IF "%LY_CMAKE_PATH%"=="" ( - IF "%LY_3RDPARTY_PATH%"=="" ( - ECHO ERROR: CMake was not found on the PATH and LY_3RDPARTY_PATH is not defined. - ECHO Please ensure CMake is on the path or set LY_3RDPARTY_PATH or LY_CMAKE_PATH. - EXIT /b 1 - ) - SET LY_CMAKE_PATH=!LY_3RDPARTY_PATH!\CMake\3.19.1\Windows\bin - echo CMake was not found on the path, will use known location: !LY_CMAKE_PATH! + ECHO ERROR: CMake was not found on the PATH and LY_CMAKE_PATH is not defined. + ECHO Please ensure CMake is on the path or set LY_CMAKE_PATH. + EXIT /b 1 ) + PATH !LY_CMAKE_PATH!;!PATH! where /Q cmake if NOT !ERRORLEVEL!==0 ( diff --git a/python/get_python.cmake b/python/get_python.cmake index 5d6bf6fd96..198d3f0ba3 100644 --- a/python/get_python.cmake +++ b/python/get_python.cmake @@ -16,7 +16,7 @@ # example: # cmake -DPAL_PLATFORM_NAME:string=Windows -DLY_3RDPARTY_PATH:string=%CMD_DIR% -P get_python.cmake -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) if(LY_3RDPARTY_PATH) file(TO_CMAKE_PATH ${LY_3RDPARTY_PATH} LY_3RDPARTY_PATH) diff --git a/python/get_python.sh b/python/get_python.sh index 780f1bbdd8..9b774d91fc 100755 --- a/python/get_python.sh +++ b/python/get_python.sh @@ -42,31 +42,21 @@ then CMAKE_FOLDER_RELATIVE_TO_ROOT=CMake.app/Contents/bin else PAL=Linux - CMAKE_FOLDER_RELATIVE_TO_ROOT=bin + CMAKE_FOLDER_RELATIVE_TO_ROOT=bin fi if ! [ -x "$(command -v cmake)" ]; then - # Note that LY_3RDPARTY_PATH is only required here if you have no cmake in your PATH. if [ -z ${LY_CMAKE_PATH} ]; then - if [ -z ${LY_3RDPARTY_PATH} ]; then - echo "ERROR: Could not find cmake on the PATH and LY_3RDPARTY_PATH is not defined, cannot continue." - echo "Please add cmake to your PATH, or define $LY_3RDPARTY_PATH" - exit 1 - fi - LY_CMAKE_PATH=$LY_3RDPARTY_PATH/CMake/3.19.1/$PAL/$CMAKE_FOLDER_RELATIVE_TO_ROOT - # if you change the version number, change it also in: - # scripts/build/Platform/Mac/env_mac.sh - # and - # scripts/build/Platform/Linux/env_linux.sh + echo "ERROR: Could not find cmake on the PATH and LY_CMAKE_PATH is not defined, cannot continue." + echo "Please add cmake to your PATH, or define LY_CMAKE_PATH" + exit 1 fi - + export PATH=$LY_CMAKE_PATH:$PATH if ! [ -x "$(command -v cmake)" ]; then - echo "ERROR: Could not find cmake on the PATH or at the known location: $CMAKE_KNOWN_LOCATION" + echo "ERROR: Could not find cmake on the PATH or at the known location: $LY_CMAKE_PATH" echo "Please add cmake to the environment PATH or place it at the above known location." exit 1 - else - echo "CMake not found on path, but was found in the known 3rd Party location." fi fi diff --git a/scripts/build/Platform/Android/build_and_run_unit_tests.cmd b/scripts/build/Platform/Android/build_and_run_unit_tests.cmd index fbb31f95ec..5a51621f40 100644 --- a/scripts/build/Platform/Android/build_and_run_unit_tests.cmd +++ b/scripts/build/Platform/Android/build_and_run_unit_tests.cmd @@ -20,9 +20,6 @@ IF NOT EXIST "%LY_3RDPARTY_PATH%" ( GOTO :error ) -IF NOT EXIST "%LY_ANDROID_SDK%" ( - SET LY_ANDROID_SDK=!LY_3RDPARTY_PATH!/android-sdk/platform-29 -) IF NOT EXIST "%LY_ANDROID_SDK%" ( ECHO [ci_build] FAIL: LY_ANDROID_SDK=!LY_ANDROID_SDK! GOTO :error diff --git a/scripts/build/Platform/Linux/env_linux.sh b/scripts/build/Platform/Linux/env_linux.sh index 1d7324778c..85bdf0a745 100755 --- a/scripts/build/Platform/Linux/env_linux.sh +++ b/scripts/build/Platform/Linux/env_linux.sh @@ -13,27 +13,11 @@ set -o errexit # exit on the first failure encountered if ! command -v cmake &> /dev/null; then - if [[ -z $LY_CMAKE_PATH ]]; then LY_CMAKE_PATH=${LY_3RDPARTY_PATH}/CMake/3.19.1/Linux/bin; fi - if [[ ! -d $LY_CMAKE_PATH ]]; then - echo "[ci_build] CMake path not found" - exit 1 - fi - PATH=${LY_CMAKE_PATH}:${PATH} - if ! command -v cmake &> /dev/null; then - echo "[ci_build] CMake not found" - exit 1 - fi + echo "[ci_build] CMake not found" + exit 1 fi if ! command -v ninja &> /dev/null; then - if [[ -z $LY_NINJA_PATH ]]; then LY_NINJA_PATH=${LY_3RDPARTY_PATH}/ninja/1.10.1/Linux; fi - if [[ ! -d $LY_NINJA_PATH ]]; then - echo "[ci_build] Ninja path not found" - exit 1 - fi - PATH=${LY_NINJA_PATH}:${PATH} - if ! command -v ninja &> /dev/null; then - echo "[ci_build] Ninja not found" - exit 1 - fi + echo "[ci_build] Ninja not found" + exit 1 fi diff --git a/scripts/build/Platform/Mac/env_mac.sh b/scripts/build/Platform/Mac/env_mac.sh index 8f38d1d90b..917329b6af 100755 --- a/scripts/build/Platform/Mac/env_mac.sh +++ b/scripts/build/Platform/Mac/env_mac.sh @@ -13,14 +13,6 @@ set -o errexit # exit on the first failure encountered if ! command -v cmake &> /dev/null; then - if [[ -z $LY_CMAKE_PATH ]]; then LY_CMAKE_PATH=${LY_3RDPARTY_PATH}/CMake/3.19.1/Mac/CMake.app/Contents/bin; fi - if [[ ! -d $LY_CMAKE_PATH ]]; then - echo "[ci_build] CMake path not found" - exit 1 - fi - PATH=${LY_CMAKE_PATH}:${PATH} - if ! command -v cmake &> /dev/null; then - echo "[ci_build] CMake not found" - exit 1 - fi + echo "[ci_build] CMake not found" + exit 1 fi diff --git a/scripts/build/Platform/Windows/build_ninja_windows.cmd b/scripts/build/Platform/Windows/build_ninja_windows.cmd index b782cc2357..e1a802e254 100644 --- a/scripts/build/Platform/Windows/build_ninja_windows.cmd +++ b/scripts/build/Platform/Windows/build_ninja_windows.cmd @@ -12,19 +12,10 @@ REM SETLOCAL EnableDelayedExpansion -IF NOT EXIST "%LY_NINJA_PATH%" ( - SET LY_NINJA_PATH=%LY_3RDPARTY_PATH%/ninja/1.10.1/Windows -) -IF NOT EXIST "%LY_NINJA_PATH%" ( - ECHO [ci_build] FAIL: LY_NINJA_PATH=%LY_NINJA_PATH% - GOTO :error -) -PATH %LY_NINJA_PATH%;%PATH% - CALL "%~dp0build_windows.cmd" IF NOT %ERRORLEVEL%==0 GOTO :error EXIT /b 0 :error -EXIT /b 1 \ No newline at end of file +EXIT /b 1 diff --git a/scripts/build/Platform/Windows/env_windows.cmd b/scripts/build/Platform/Windows/env_windows.cmd index 592b2867cd..3ef161c2ef 100644 --- a/scripts/build/Platform/Windows/env_windows.cmd +++ b/scripts/build/Platform/Windows/env_windows.cmd @@ -12,17 +12,8 @@ REM where /Q cmake IF NOT %ERRORLEVEL%==0 ( - IF "%LY_CMAKE_PATH%"=="" (SET LY_CMAKE_PATH=%LY_3RDPARTY_PATH%/CMake/3.19.1/Windows/bin) - IF NOT EXIST !LY_CMAKE_PATH! ( - ECHO [ci_build] CMake path not found - GOTO :error - ) - PATH !LY_CMAKE_PATH!;!PATH! - where /Q cmake - IF NOT !ERRORLEVEL!==0 ( - ECHO [ci_build] CMake not found - GOTO :error - ) + ECHO [ci_build] CMake not found + GOTO :error ) EXIT /b 0 From 119bc95844e638838c6271db51c9abdc949db1f1 Mon Sep 17 00:00:00 2001 From: chcurran Date: Fri, 11 Jun 2021 10:01:11 -0700 Subject: [PATCH 144/233] Update graph to address SPEC-7168 --- .../Weapons/Revolver/Tracer_FX.scriptcanvas | 16399 ++++++++-------- 1 file changed, 7818 insertions(+), 8581 deletions(-) diff --git a/Gems/PhysXSamples/Assets/ScriptCanvas/Weapons/Revolver/Tracer_FX.scriptcanvas b/Gems/PhysXSamples/Assets/ScriptCanvas/Weapons/Revolver/Tracer_FX.scriptcanvas index eebc79585d..f0fff5e69f 100644 --- a/Gems/PhysXSamples/Assets/ScriptCanvas/Weapons/Revolver/Tracer_FX.scriptcanvas +++ b/Gems/PhysXSamples/Assets/ScriptCanvas/Weapons/Revolver/Tracer_FX.scriptcanvas @@ -3,12 +3,12 @@ - + - + - - + + @@ -16,19 +16,21 @@ - + - + - + - + + + - + @@ -60,10 +62,13 @@ + - + + + - + @@ -95,10 +100,13 @@ + - + + + - + @@ -130,10 +138,13 @@ + - + + + - + @@ -165,10 +176,13 @@ + - + + + - + @@ -200,10 +214,13 @@ + - + + + - + @@ -235,22 +252,22 @@ + - - + - + - + - + @@ -260,7 +277,7 @@ - + @@ -270,7 +287,7 @@ - + @@ -281,104 +298,26 @@ - + - + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + @@ -389,7 +328,7 @@ - + @@ -410,10 +349,13 @@ + - + + + - + @@ -424,7 +366,7 @@ - + @@ -445,69 +387,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + @@ -516,44 +402,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + @@ -562,22 +418,25 @@ - - + + - + + - + + + - + @@ -609,10 +468,13 @@ + - + + + - + @@ -644,10 +506,13 @@ + - + + + - + @@ -679,10 +544,13 @@ + - + + + - + @@ -714,22 +582,38 @@ + - - - + + + + + + + + + + + + + + + + + + - + - + @@ -739,7 +623,7 @@ - + @@ -749,7 +633,7 @@ - + @@ -760,24 +644,26 @@ - + - + - + - - + + - + - + + + - + @@ -788,7 +674,7 @@ - + @@ -809,10 +695,13 @@ + - + + + - + @@ -822,8 +711,8 @@ - - + + @@ -844,10 +733,13 @@ + - + + + - + @@ -856,9 +748,14 @@ + + + + + - - + + @@ -867,22 +764,25 @@ - - + + - + + - + + + - + @@ -891,23 +791,18 @@ - - - - - - - + + - + - + @@ -919,6 +814,7 @@ + @@ -932,32 +828,43 @@ - + - - + + + + + + + + + + + - + - + - + - - + + - + - + + + - + @@ -968,7 +875,7 @@ - + @@ -989,10 +896,13 @@ + - + + + - + @@ -1003,7 +913,7 @@ - + @@ -1024,10 +934,13 @@ + - + + + - + @@ -1036,23 +949,18 @@ - - - - - - + - + - + @@ -1064,10 +972,13 @@ + - + + + - + @@ -1076,23 +987,18 @@ - - - - - - + - + - + @@ -1104,10 +1010,13 @@ + - + + + - + @@ -1117,10 +1026,10 @@ - + - + @@ -1139,60 +1048,110 @@ + - - - - - - - + + + + + - - - - + + + + + + - - - - - + + + - - - + + - + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + - + - - + + - + - + + + - + @@ -1203,7 +1162,7 @@ - + @@ -1224,10 +1183,13 @@ + - + + + - + @@ -1238,7 +1200,7 @@ - + @@ -1259,10 +1221,13 @@ + - + + + - + @@ -1271,23 +1236,18 @@ - - - - - - + - + - + @@ -1299,10 +1259,13 @@ + - + + + - + @@ -1311,23 +1274,18 @@ - - - - - - + - + - + @@ -1339,10 +1297,13 @@ + - + + + - + @@ -1352,10 +1313,10 @@ - + - + @@ -1374,58 +1335,110 @@ + - - - - - - - + + + + + - - - + + + + + + + - - - - - + + + - - - + + - + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + - + - - + + - + - + + + - + @@ -1434,9 +1447,14 @@ + + + + + - - + + @@ -1446,21 +1464,24 @@ - + - + + - + + + - + @@ -1469,9 +1490,14 @@ + + + + + - - + + @@ -1480,22 +1506,25 @@ - - + + - + + - + + + - + @@ -1504,18 +1533,23 @@ + + + + + - + - + - + @@ -1527,39 +1561,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + @@ -1570,7 +1578,7 @@ - + @@ -1591,10 +1599,13 @@ + - + + + - + @@ -1605,7 +1616,7 @@ - + @@ -1626,10 +1637,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -1639,10 +1723,10 @@ - + - + @@ -1661,10 +1745,13 @@ + - + + + - + @@ -1673,18 +1760,23 @@ + + + + + - + - + - + @@ -1696,10 +1788,13 @@ + - + + + - + @@ -1708,18 +1803,23 @@ + + + + + - + - + - + @@ -1731,220 +1831,13 @@ + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -1981,8 +1874,11 @@ + - + + + @@ -2016,8 +1912,11 @@ + - + + + @@ -2051,6 +1950,7 @@ + @@ -2091,8 +1991,7 @@ - - + @@ -2106,24 +2005,26 @@ - + - + - + - - + + - + - + + + - + @@ -2133,8 +2034,8 @@ - - + + @@ -2155,10 +2056,13 @@ + - + + + - + @@ -2168,8 +2072,8 @@ - - + + @@ -2178,7 +2082,7 @@ - + @@ -2190,10 +2094,13 @@ + - + + + - + @@ -2203,10 +2110,10 @@ - - + + - + @@ -2214,21 +2121,24 @@ - + - + + - + + + - + @@ -2238,10 +2148,10 @@ - - + + - + @@ -2249,21 +2159,24 @@ - + - + + - + + + - + @@ -2273,10 +2186,10 @@ - - + + - + @@ -2284,21 +2197,24 @@ - + - + + - + + + - + @@ -2307,18 +2223,35 @@ + + + + + + + + + + + + + + + + + - - + + - + - + @@ -2330,70 +2263,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + @@ -2403,10 +2279,10 @@ - + - + @@ -2425,10 +2301,13 @@ + - + + + - + @@ -2437,13 +2316,8 @@ - - - - - - + @@ -2453,22 +2327,25 @@ - - + + - + - + + - + + + - + @@ -2478,32 +2355,35 @@ - + - + - - + + - + + - + + + - + @@ -2513,7 +2393,7 @@ - + @@ -2526,7 +2406,7 @@ - + @@ -2535,6 +2415,7 @@ + @@ -2550,42 +2431,88 @@ - + - - + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - + - + - + - - + + - + - + + + - + @@ -2596,7 +2523,7 @@ - + @@ -2617,10 +2544,13 @@ + - + + + - + @@ -2631,7 +2561,7 @@ - + @@ -2652,10 +2582,13 @@ + - + + + - + @@ -2664,23 +2597,18 @@ - - - - - - + - + - + @@ -2692,10 +2620,13 @@ + - + + + - + @@ -2705,10 +2636,10 @@ - + - + @@ -2727,197 +2658,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -2949,10 +2696,13 @@ + - + + + - + @@ -2984,22 +2734,22 @@ + - - + - + - + - + @@ -3009,7 +2759,7 @@ - + @@ -3019,7 +2769,7 @@ - + @@ -3030,24 +2780,26 @@ - + - + - + - - + + - + - + + + - + @@ -3058,7 +2810,7 @@ - + @@ -3079,10 +2831,13 @@ + - + + + - + @@ -3093,7 +2848,7 @@ - + @@ -3114,10 +2869,56 @@ + - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3149,10 +2950,13 @@ + - + + + - + @@ -3184,10 +2988,13 @@ + - + + + - + @@ -3219,10 +3026,13 @@ + - + + + - + @@ -3254,22 +3064,38 @@ + - - - + + + + + + + + + + + + + + + - + + + + - + - + @@ -3279,7 +3105,7 @@ - + @@ -3289,7 +3115,7 @@ - + @@ -3300,584 +3126,246 @@ - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + - - - - - - - - - + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - + - + - + - + - + - + + + - + @@ -3909,10 +3397,13 @@ + - + + + - + @@ -3944,10 +3435,13 @@ + - + + + - + @@ -3957,10 +3451,10 @@ - + - + @@ -3979,109 +3473,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + @@ -4091,10 +3489,10 @@ - + - + @@ -4113,10 +3511,13 @@ + - + + + - + @@ -4126,7 +3527,7 @@ - + @@ -4148,10 +3549,13 @@ + - + + + - + @@ -4161,42 +3565,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -4218,22 +3587,22 @@ + - - + - + - + @@ -4243,7 +3612,7 @@ - + @@ -4253,7 +3622,7 @@ - + @@ -4264,24 +3633,26 @@ - + - + - + - - + + - + - + + + - + @@ -4290,14 +3661,9 @@ - - - - - - - + + @@ -4307,21 +3673,24 @@ - + - + + - + + + - + @@ -4330,23 +3699,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + @@ -4358,10 +3760,13 @@ + - + + + - + @@ -4371,32 +3776,35 @@ - + - + - - + + - + + - + + + - + @@ -4406,10 +3814,10 @@ - + - + @@ -4417,80 +3825,121 @@ - + - + + - - - - - - - + + + + + - - - - + + + + + + - - - - - - + + + + - - - + + + + + + + + + + + + + + + - + - - + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + - + - - + + - + - + + + - + @@ -4500,32 +3949,35 @@ - - + + - + - - + + - + + - + + + - + @@ -4534,9 +3986,14 @@ + + + + + - - + + @@ -4546,21 +4003,24 @@ - + - + + - + + + - + @@ -4570,8 +4030,8 @@ - - + + @@ -4580,7 +4040,7 @@ - + @@ -4592,10 +4052,13 @@ + - + + + - + @@ -4605,8 +4068,8 @@ - - + + @@ -4627,45 +4090,59 @@ + - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -4674,26 +4151,9 @@ - - - - - - - - - - - - - - - - - - - + + @@ -4703,21 +4163,24 @@ - + - + + - + + + - + @@ -4727,10 +4190,10 @@ - + - + @@ -4738,21 +4201,24 @@ - + - + + - + + + - + @@ -4761,8 +4227,13 @@ + + + + + - + @@ -4772,22 +4243,25 @@ - - + + - + + - + + + - + @@ -4796,18 +4270,23 @@ + + + + + - + - + - + @@ -4819,10 +4298,13 @@ + - + + + - + @@ -4832,10 +4314,10 @@ - + - + @@ -4843,17 +4325,18 @@ - + - + + @@ -4869,85 +4352,44 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - + + + + + - - - - + + + - - + + - - - - - + - + - + - + - + + + @@ -4981,8 +4423,11 @@ + - + + + @@ -5016,8 +4461,11 @@ + - + + + @@ -5051,70 +4499,757 @@ + - - + - + - + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -5143,10 +5278,13 @@ + - + + + - + @@ -5178,10 +5316,13 @@ + - + + + - + @@ -5213,10 +5354,13 @@ + - + + + - + @@ -5248,10 +5392,13 @@ + - + + + - + @@ -5283,22 +5430,22 @@ + - - + - + - + - + @@ -5308,7 +5455,7 @@ - + @@ -5318,7 +5465,7 @@ - + @@ -5329,24 +5476,26 @@ - + - + - + - + - + + + - + @@ -5378,10 +5527,13 @@ + - + + + - + @@ -5413,10 +5565,13 @@ + - + + + - + @@ -5448,10 +5603,13 @@ + - + + + - + @@ -5483,10 +5641,13 @@ + - + + + - + @@ -5518,10 +5679,13 @@ + - + + + - + @@ -5553,22 +5717,22 @@ + - - + - + - + - + @@ -5578,7 +5742,7 @@ - + @@ -5588,7 +5752,7 @@ - + @@ -5599,24 +5763,26 @@ - + - + - + - - + + - + - + + + - + @@ -5627,7 +5793,7 @@ - + @@ -5648,10 +5814,13 @@ + - + + + - + @@ -5662,7 +5831,7 @@ - + @@ -5683,10 +5852,13 @@ + - + + + - + @@ -5695,18 +5867,23 @@ + + + + + - + - + - + @@ -5718,10 +5895,13 @@ + - + + + - + @@ -5731,10 +5911,10 @@ - + - + @@ -5753,23 +5933,72 @@ + - - - + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + @@ -5788,10 +6017,13 @@ + - + + + - + @@ -5800,18 +6032,23 @@ + + + + + - + - + - + @@ -5823,72 +6060,137 @@ + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - + - + - + - - + + - + - + + + - + - + @@ -5900,35 +6202,11 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + @@ -5940,19 +6218,22 @@ - + + - + + + - + - + @@ -5964,35 +6245,11 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + @@ -6004,19 +6261,22 @@ - + + - + + + - + - + @@ -6028,35 +6288,11 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + @@ -6068,17 +6304,20 @@ - + + - + + + - + @@ -6093,8 +6332,8 @@ - - + + @@ -6115,104 +6354,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + @@ -6244,45 +6392,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + @@ -6314,81 +6430,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + @@ -6397,31 +6444,31 @@ - + - + - + - + - + - + - + - + - + @@ -6431,36 +6478,43 @@ - + - + - - + - - - + + + + + + + + + - + - + - + - + + + @@ -6494,8 +6548,11 @@ + - + + + @@ -6529,8 +6586,11 @@ + - + + + @@ -6564,8 +6624,11 @@ + - + + + @@ -6599,8 +6662,11 @@ + - + + + @@ -6634,8 +6700,11 @@ + - + + + @@ -6669,11 +6738,11 @@ + - - + @@ -6715,24 +6784,26 @@ - + - + - + - + - + + + - + @@ -6764,10 +6835,13 @@ + - + + + - + @@ -6799,10 +6873,13 @@ + - + + + - + @@ -6834,10 +6911,13 @@ + - + + + - + @@ -6869,10 +6949,13 @@ + - + + + - + @@ -6904,10 +6987,13 @@ + - + + + - + @@ -6939,22 +7025,22 @@ + - - + - + - + - + @@ -6964,7 +7050,7 @@ - + @@ -6974,7 +7060,7 @@ - + @@ -6985,24 +7071,26 @@ - + - + - + - - + + - + - + + + - + @@ -7011,13 +7099,8 @@ - - - - - - + @@ -7028,21 +7111,24 @@ - + - + + - + + + - + @@ -7051,11 +7137,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - @@ -7064,7 +7181,7 @@ - + @@ -7086,10 +7203,13 @@ + - + + + - + @@ -7098,8 +7218,13 @@ + + + + + - + @@ -7110,21 +7235,24 @@ - + - + + - + + + - + @@ -7134,10 +7262,10 @@ - + - + @@ -7145,17 +7273,18 @@ - + - + + @@ -7171,54 +7300,46 @@ - + - + - + - + - - + - - - - - - - - - - + - + - + - + - - + + - + - + + + - + @@ -7227,9 +7348,14 @@ + + + + + - - + + @@ -7239,21 +7365,24 @@ - + - + + - + + + - + @@ -7262,9 +7391,21 @@ + + + + + + + + + + + + - - + + @@ -7273,22 +7414,25 @@ - - + + - + + - + + + - + @@ -7297,13 +7441,8 @@ - - - - - - + @@ -7314,21 +7453,24 @@ - + - + + - + + + - + @@ -7338,10 +7480,10 @@ - + - + @@ -7349,21 +7491,82 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -7373,32 +7576,35 @@ - - + + - + - - + + - + + - + + + - + @@ -7408,10 +7614,10 @@ - - + + - + @@ -7419,21 +7625,24 @@ - + - + + - + + + - + @@ -7443,7 +7652,7 @@ - + @@ -7465,86 +7674,41 @@ + - - - - - - - - - - - - - - - - + + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - + - - + + - + - + + + - + @@ -7555,7 +7719,7 @@ - + @@ -7576,10 +7740,13 @@ + - + + + - + @@ -7590,7 +7757,7 @@ - + @@ -7611,50 +7778,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + @@ -7686,10 +7816,13 @@ + - + + + - + @@ -7721,10 +7854,13 @@ + - + + + - + @@ -7756,10 +7892,13 @@ + - + + + - + @@ -7791,38 +7930,22 @@ + - - - - - - - - - - - - - - - - + + - - - - + - + - + @@ -7832,7 +7955,7 @@ - + @@ -7842,7 +7965,7 @@ - + @@ -7853,24 +7976,26 @@ - + - + - + - - + + - + - + + + - + @@ -7879,14 +8004,9 @@ - - - - - - - + + @@ -7896,21 +8016,24 @@ - + - + + - + + + - + @@ -7919,14 +8042,9 @@ - - - - - - - + + @@ -7935,22 +8053,25 @@ - - + + - + + - + + + - + @@ -7959,23 +8080,18 @@ - - - - - - + - + - + @@ -7987,10 +8103,13 @@ + - + + + - + @@ -8000,32 +8119,35 @@ - + - + - - + + - + + - + + + - + @@ -8035,10 +8157,10 @@ - + - + @@ -8046,132 +8168,121 @@ - + - + + - - - - - - - + + + + + - - - - + + + + + + - - - - - - + + + + - - - + + - - - - - - - + + + - - - + + + - + + + + + + - - + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + @@ -8180,14 +8291,9 @@ - - - - - - - + + @@ -8197,21 +8303,24 @@ - + - + + - + + + - + @@ -8221,8 +8330,8 @@ - - + + @@ -8231,7 +8340,7 @@ - + @@ -8243,10 +8352,13 @@ + - + + + - + @@ -8256,10 +8368,10 @@ - + - + @@ -8267,80 +8379,52 @@ - + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - + + - + + + + - + - + - + - + - + - + + + - + @@ -8349,18 +8433,23 @@ + + + + + - + - + - + @@ -8372,10 +8461,13 @@ + - + + + - + @@ -8384,13 +8476,20 @@ + + + + + + + - + @@ -8412,10 +8511,13 @@ + - + + + - + @@ -8447,10 +8549,13 @@ + - + + + - + @@ -8482,6 +8587,7 @@ + @@ -8499,500 +8605,476 @@ + + + + + + + + + + + + - - + - + - + - + - + - + - - - - - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - + - + - + - - + + - + - + + + - + @@ -9001,14 +9083,9 @@ - - - - - - - + + @@ -9018,21 +9095,24 @@ - + - + + - + + + - + @@ -9041,14 +9121,9 @@ - - - - - - - + + @@ -9057,22 +9132,25 @@ - - + + - + + - + + + - + @@ -9082,8 +9160,8 @@ - - + + @@ -9092,7 +9170,7 @@ - + @@ -9104,10 +9182,13 @@ + - + + + - + @@ -9116,9 +9197,14 @@ + + + + + - - + + @@ -9127,81 +9213,59 @@ - - + + - + + - - - - - - - - - - - - - - - + - + - + - - - - - - - - - - - + - - + - + - + - - + + - + - + + + - + @@ -9211,10 +9275,48 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -9222,21 +9324,24 @@ - + - + + - + + + - + @@ -9251,7 +9356,7 @@ - + @@ -9273,10 +9378,13 @@ + - + + + - + @@ -9285,8 +9393,13 @@ + + + + + - + @@ -9297,21 +9410,24 @@ - + - + + - + + + - + @@ -9321,10 +9437,10 @@ - + - + @@ -9332,68 +9448,71 @@ - + - + + - + - - - + - + + + + + + + + + + + + + - - - - - - - - - - - + - + - + - + - + - - + + - + - + + + - + @@ -9403,32 +9522,35 @@ - - + + - + - - + + - + + - + + + - + @@ -9437,14 +9559,9 @@ - - - - - - - + + @@ -9453,22 +9570,25 @@ - - + + - + + - + + + - + @@ -9478,32 +9598,35 @@ - + - + - - + + - + + - + + + - + @@ -9513,10 +9636,10 @@ - + - + @@ -9524,864 +9647,814 @@ - + - + + - - - - - - - + + + + + - - - - - + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - + + + - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - + + + + + - - - - - + + + + - - - - - - - - - - + - + - + - + + + @@ -10415,8 +10488,11 @@ + - + + + @@ -10450,8 +10526,11 @@ + - + + + @@ -10490,8 +10569,11 @@ + - + + + @@ -10525,8 +10607,11 @@ + - + + + @@ -10560,8 +10645,11 @@ + - + + + @@ -10595,8 +10683,11 @@ + - + + + @@ -10630,6 +10721,7 @@ + @@ -10646,8 +10738,7 @@ - - + @@ -10692,24 +10783,26 @@ - + - + - + - - + + - + - + + + - + @@ -10718,9 +10811,14 @@ + + + + + - - + + @@ -10730,21 +10828,24 @@ - + - + + - + + + - + @@ -10753,53 +10854,23 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + @@ -10811,39 +10882,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + @@ -10854,7 +10899,7 @@ - + @@ -10875,10 +10920,13 @@ + - + + + - + @@ -10889,7 +10937,7 @@ - + @@ -10910,244 +10958,729 @@ + - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - + + + + + + - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -11157,2578 +11690,847 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + - - - + + + - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + - - - + + + + + - - - - + + + + + - - - - - - - + + + + - - - + + + + + - - - - - - + + + + - - - + + + + + - - - - - - + + + + - - - + + + + + - - - - - - + + + + - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -13736,30 +12538,202 @@ - + - + - - - - - - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + - - + + + + + + + + + + @@ -13767,61 +12741,141 @@ - + - + - - - - - - - - - - - - - - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - + + + @@ -13829,90 +12883,172 @@ - + - + - - - - - - - - - - - - - - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - + + + + + - + - + - + - + - + - + - + @@ -13922,28 +13058,28 @@ - + - + - + - + - + - + - + @@ -13953,28 +13089,28 @@ - + - + - + - + - + - + - + @@ -13984,28 +13120,28 @@ - + - + - + - + - + - + - + @@ -14015,28 +13151,28 @@ - + - + - + - + - + - + - + @@ -14046,28 +13182,28 @@ - + - + - + - + - + - + - + @@ -14077,28 +13213,28 @@ - + - + - + - + - + - + - + @@ -14108,28 +13244,28 @@ - + - + - + - + - + - + - + @@ -14139,28 +13275,28 @@ - + - + - + - + - + - + - + @@ -14170,28 +13306,28 @@ - + - + - + - + - + - + - + @@ -14201,28 +13337,28 @@ - + - + - + - + - + - + - + @@ -14232,28 +13368,28 @@ - + - + - + - + - + - + - + @@ -14263,28 +13399,28 @@ - + - + - + - + - + - + - + @@ -14294,28 +13430,28 @@ - + - + - + - + - + - + - + @@ -14325,28 +13461,28 @@ - + - + - + - + - + - + - + @@ -14356,28 +13492,28 @@ - + - + - + - + - + - + - + @@ -14387,28 +13523,28 @@ - + - + - + - + - + - + - + @@ -14418,28 +13554,28 @@ - + - + - + - + - + - + - + @@ -14449,28 +13585,28 @@ - + - + - + - + - + - + - + @@ -14480,28 +13616,28 @@ - + - + - + - + - + - + - + @@ -14511,28 +13647,28 @@ - + - + - + - + - + - + - + @@ -14542,28 +13678,28 @@ - + - + - + - + - + - + - + @@ -14573,28 +13709,28 @@ - + - + - + - + - + - + - + @@ -14604,28 +13740,28 @@ - + - + - + - + - + - + - + @@ -14635,28 +13771,28 @@ - + - + - + - + - + - + - + @@ -14666,28 +13802,28 @@ - + - + - + - + - + - + - + @@ -14697,28 +13833,28 @@ - + - + - + - + - + - + - + @@ -14728,28 +13864,28 @@ - + - + - + - + - + - + - + @@ -14759,28 +13895,28 @@ - + - + - + - + - + - + - + @@ -14790,28 +13926,28 @@ - + - + - + - + - + - + - + @@ -14821,28 +13957,28 @@ - + - + - + - + - + - + - + @@ -14852,28 +13988,28 @@ - + - + - + - + - + - + - + @@ -14883,28 +14019,28 @@ - + - + - + - + - + - + - + @@ -14914,28 +14050,28 @@ - + - + - + - + - + - + - + @@ -14945,28 +14081,28 @@ - + - + - + - + - + - + - + @@ -14976,28 +14112,28 @@ - + - + - + - + - + - + - + @@ -15007,28 +14143,28 @@ - + - + - + - + - + - + - + @@ -15038,28 +14174,28 @@ - + - + - + - + - + - + - + @@ -15069,17 +14205,17 @@ - + - + - + @@ -15087,7 +14223,7 @@ - + @@ -15100,17 +14236,17 @@ - + - + - + @@ -15118,7 +14254,7 @@ - + @@ -15131,17 +14267,17 @@ - + - + - + @@ -15149,7 +14285,7 @@ - + @@ -15162,17 +14298,17 @@ - + - + - + @@ -15180,7 +14316,7 @@ - + @@ -15193,17 +14329,17 @@ - + - + - + @@ -15211,7 +14347,7 @@ - + @@ -15224,17 +14360,17 @@ - + - + - + @@ -15242,7 +14378,7 @@ - + @@ -15255,17 +14391,17 @@ - + - + - + @@ -15273,7 +14409,7 @@ - + @@ -15286,17 +14422,17 @@ - + - + - + @@ -15304,7 +14440,7 @@ - + @@ -15317,17 +14453,17 @@ - + - + - + @@ -15335,7 +14471,7 @@ - + @@ -15348,17 +14484,17 @@ - + - + - + @@ -15366,7 +14502,7 @@ - + @@ -15379,17 +14515,17 @@ - + - + - + @@ -15397,7 +14533,7 @@ - + @@ -15410,17 +14546,17 @@ - + - + - + @@ -15428,7 +14564,7 @@ - + @@ -15441,17 +14577,17 @@ - + - + - + @@ -15459,7 +14595,7 @@ - + @@ -15472,17 +14608,17 @@ - + - + - + @@ -15490,7 +14626,7 @@ - + @@ -15503,17 +14639,17 @@ - + - + - + @@ -15521,7 +14657,7 @@ - + @@ -15534,28 +14670,121 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + @@ -15565,28 +14794,28 @@ - + - + - + - + - + - + - + @@ -15596,28 +14825,28 @@ - + - + - + - + - + - + - + @@ -15627,28 +14856,28 @@ - + - + - + - + - + - + - + @@ -15658,28 +14887,28 @@ - + - + - + - + - + - + - + @@ -15689,28 +14918,28 @@ - + - + - + - + - + - + - + @@ -15720,28 +14949,28 @@ - + - + - + - + - + - + - + @@ -15751,28 +14980,28 @@ - + - + - + - + - + - + - + @@ -15782,28 +15011,28 @@ - + - + - + - + - + - + - + @@ -15813,404 +15042,310 @@ - + - + - + - + - + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - + + + + - - - + + + + - + - - - - + + + - - - - + + + @@ -16218,41 +15353,41 @@ - + - - - + + + + - - - + + + + - + - - - - + + + - - - - + + + @@ -16260,7 +15395,7 @@ - + @@ -16268,27 +15403,27 @@ - + - + - + - + @@ -16296,35 +15431,49 @@ - + - - + + - + + + + + + + + + - - + + - + - + - + + + + + + + @@ -16332,20 +15481,22 @@ - + - - - + + + + - - - + + + + @@ -16356,17 +15507,9 @@ - - - - - - - - - - - + + + @@ -16374,41 +15517,41 @@ - + - - - + + + + - - - + + + + - + - - - - + + + - - - - + + + @@ -16416,7 +15559,7 @@ - + @@ -16424,27 +15567,99 @@ - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -16452,7 +15667,7 @@ - + @@ -16460,7 +15675,7 @@ - + @@ -16480,7 +15695,7 @@ - + @@ -16488,41 +15703,41 @@ - + - - - + + + + - - - + + + + - + - - - - + + + - - - - + + + @@ -16530,7 +15745,7 @@ - + @@ -16538,7 +15753,7 @@ - + @@ -16558,7 +15773,13 @@ - + + + + + + + @@ -16566,41 +15787,41 @@ - + - - - + + + + - - - + + + + - + - - - - + + + - - - - + + + @@ -16608,41 +15829,35 @@ - + - - - + + + + - - - + + + + - - - - - - - - + - - - - + + + @@ -16650,7 +15865,7 @@ - + @@ -16658,27 +15873,27 @@ - + - + - + - + @@ -16686,41 +15901,35 @@ - + - - - + + + + - - - + + + + - - - - - - - - + - - - - + + + @@ -16728,7 +15937,7 @@ - + @@ -16736,7 +15945,7 @@ - + @@ -16756,7 +15965,13 @@ - + + + + + + + @@ -16764,49 +15979,35 @@ - + - - - + + + + - - - + + + + - - - - - - - - + - - - - - - - - - - - - + + + @@ -16814,41 +16015,41 @@ - + - - - + + + + - - - + + + + - + - - - - + + + - - - - + + + @@ -16856,41 +16057,41 @@ - + - - - + + + + - - - + + + + - + - - - - + + + - - - - + + + @@ -16898,7 +16099,7 @@ - + @@ -16906,27 +16107,27 @@ - + - + - + - + @@ -16934,7 +16135,7 @@ - + @@ -16942,7 +16143,7 @@ - + @@ -16962,7 +16163,7 @@ - + @@ -16970,7 +16171,7 @@ - + @@ -16998,7 +16199,13 @@ - + + + + + + + @@ -17006,7 +16213,7 @@ - + @@ -17042,7 +16249,7 @@ - + @@ -17050,27 +16257,27 @@ - + - + - + - + @@ -17078,41 +16285,35 @@ - + - - - - - - - - - - - - - - + + - + - + - - + + - + + + + + + + @@ -17120,7 +16321,7 @@ - + @@ -17128,7 +16329,7 @@ - + @@ -17148,72 +16349,13 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -17221,41 +16363,35 @@ - + - - - + + + + - - - + + + + - - - - - - - - + - - - - + + + @@ -17263,7 +16399,7 @@ - + @@ -17271,7 +16407,7 @@ - + @@ -17291,7 +16427,7 @@ - + @@ -17299,7 +16435,7 @@ - + @@ -17307,27 +16443,33 @@ - + - + - + - + + + + + + + @@ -17335,20 +16477,22 @@ - + - - - + + + + - - - + + + + @@ -17359,17 +16503,15 @@ - - - - + + + - - - - + + + @@ -17377,41 +16519,41 @@ - + - - - + + + + - - - + + + + - + - - - - + + + - - - - + + + @@ -17419,41 +16561,41 @@ - + - - - + + + + - - - + + + + - + - - - - + + + - - - - + + + @@ -17461,41 +16603,71 @@ - + - - - + + + + - - - + + + + - + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + + + + + + + @@ -17503,7 +16675,7 @@ - + @@ -17511,7 +16683,7 @@ - + @@ -17531,7 +16703,7 @@ - + @@ -17539,7 +16711,7 @@ - + @@ -17547,27 +16719,33 @@ - + - + - + - + + + + + + + @@ -17575,41 +16753,41 @@ - + - - - + + + + - - - + + + + - + - - - - + + + - - - - + + + @@ -17617,7 +16795,7 @@ - + @@ -17653,35 +16831,46 @@ - + - - + + - + + + + + + - - + + + + + + + + - + - + - - - + + + @@ -17689,41 +16878,41 @@ - + - - - + + + + - - - + + + + - + - - - - + + + - - - - + + + @@ -17731,7 +16920,7 @@ - + @@ -17739,27 +16928,33 @@ - + - + - + - + + + + + + + @@ -17767,20 +16962,22 @@ - + - - - + + + + - - - + + + + @@ -17790,18 +16987,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + + + + + + + @@ -17809,7 +17040,7 @@ - + @@ -17817,27 +17048,27 @@ - + - + - + - + @@ -17845,7 +17076,7 @@ - + @@ -17853,27 +17084,27 @@ - + - + - + - + @@ -17894,19 +17125,15 @@ - - + + - + - - - - - + @@ -17914,36 +17141,28 @@ - - - - - - - - - + - - + + - + - - + + - + - - + + @@ -17954,79 +17173,79 @@ - - + + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - - + + - + - - + + - - + + - - + + - + @@ -18042,20 +17261,9 @@ - + - - - - - - - - - - - - + @@ -18064,62 +17272,64 @@ - + - - - - - - - - - - - - - + + + + + - + + + + + + + + + + + - + - - - + - - - - - - - - - - - - - + + + + + - + + + + + + + + + + + @@ -18130,27 +17340,62 @@ - + - + + + + + + + + + + + + + + - + - - - - + + + + + + + + + + + + + + + + - + + + + + + + + + + + @@ -18163,25 +17408,27 @@ + + + + + + + + + + + + + - - - - - - - - - - - - + @@ -18194,25 +17441,27 @@ - - - - - - - - - - - - + + + + - + + + + + + + + + + + @@ -18223,58 +17472,29 @@ - - - - - - - - - - - - - + - - + + + - - - - - - - - - - - + + - + + + - + - - - - - - - - - - - - + @@ -18283,42 +17503,59 @@ - + - - - - - - - - - - - - - + + + + + - + + + + + + + + + + + - + - + + + - + - + + + + + + + + + + + + + + From 6584e1290be6c5c8adbb889da4f780b63ef4e294 Mon Sep 17 00:00:00 2001 From: michabr <82236305+michabr@users.noreply.github.com> Date: Fri, 11 Jun 2021 10:03:06 -0700 Subject: [PATCH 145/233] Reenable LyShine mask support now using Atom (#1218) * Add option to set stencil ref in Dynamic Draw Context * Add depth/stencil attachment slot to UI pass * Rework mask rendering to use Atom * Add missing circle mask image --- .../Common/Assets/Passes/LowEndPipeline.pass | 9 +- .../Common/Assets/Passes/MainPipeline.pass | 7 ++ .../Atom/Feature/Common/Assets/Passes/UI.pass | 17 +++ .../Common/Assets/Passes/UIParent.pass | 12 ++ .../DynamicDraw/DynamicDrawContext.h | 11 +- .../DynamicDraw/DynamicDrawContext.cpp | 13 +++ .../Shaders/LyShineUI.shadervariantlist | 4 +- Gems/LyShine/Code/Source/RenderGraph.cpp | 109 ++++++++---------- Gems/LyShine/Code/Source/RenderGraph.h | 7 +- Gems/LyShine/Code/Source/UiRenderer.cpp | 56 +++++---- Gems/LyShine/Code/Source/UiRenderer.h | 42 ++++++- .../Textures/LyShineExamples/CircleMask.tif | 3 + 12 files changed, 196 insertions(+), 94 deletions(-) create mode 100644 Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleMask.tif diff --git a/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass index 38a4524aa2..849094dd69 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass @@ -322,7 +322,14 @@ "Pass": "AuxGeomPass", "Attachment": "ColorInputOutput" } - } + }, + { + "LocalSlot": "DepthInputOutput", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "Depth" + } + } ] }, { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass index b2e0cf088e..314d999e4d 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass @@ -427,6 +427,13 @@ "Pass": "DebugOverlayPass", "Attachment": "InputOutput" } + }, + { + "LocalSlot": "DepthInputOutput", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "Depth" + } } ] }, diff --git a/Gems/Atom/Feature/Common/Assets/Passes/UI.pass b/Gems/Atom/Feature/Common/Assets/Passes/UI.pass index 569fe1d722..ac43f17c11 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/UI.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/UI.pass @@ -7,6 +7,23 @@ "Name": "UIPassTemplate", "PassClass": "RasterPass", "Slots": [ + { + "Name": "DepthInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "DepthStencil", + "LoadStoreAction": { + "ClearValue": { + "Type": "DepthStencil", + "Value": [ + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "LoadActionStencil": "Clear" + } + }, { "Name": "InputOutput", "SlotType": "InputOutput", diff --git a/Gems/Atom/Feature/Common/Assets/Passes/UIParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/UIParent.pass index 0069e89401..4ae67b9b09 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/UIParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/UIParent.pass @@ -10,6 +10,11 @@ { "Name": "InputOutput", "SlotType": "InputOutput" + }, + { + "Name": "DepthInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "DepthStencil" } ], "PassRequests": [ @@ -24,6 +29,13 @@ "Pass": "Parent", "Attachment": "InputOutput" } + }, + { + "LocalSlot": "DepthInputOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "DepthInputOutput" + } } ], "PassData": { diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h index 81b23068a1..94fd20f828 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h @@ -138,6 +138,12 @@ namespace AZ //! Without per draw viewport, the viewport setup in pass is usually used. void UnsetViewport(); + //! Set stencil reference for following draws which are added to this DynamicDrawContext + void SetStencilReference(uint8_t stencilRef); + + //! Get the current stencil reference. + uint8_t GetStencilReference() const; + //! Draw Indexed primitives with vertex and index data and per draw srg //! The per draw srg need to be provided if it's required by shader. void DrawIndexed(void* vertexData, uint32_t vertexCount, void* indexData, uint32_t indexCount, RHI::IndexFormat indexFormat, Data::Instance < ShaderResourceGroup> drawSrg = nullptr); @@ -204,10 +210,13 @@ namespace AZ bool m_useScissor = false; RHI::Scissor m_scissor; - // current scissor + // current viewport bool m_useViewport = false; RHI::Viewport m_viewport; + // Current stencil reference value + uint8_t m_stencilRef = 0; + // Cached RHI pipeline states for different combination of render states AZStd::unordered_map m_cachedRhiPipelineStates; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp index 00c2122825..09e3499820 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp @@ -382,6 +382,16 @@ namespace AZ m_useViewport = false; } + void DynamicDrawContext::SetStencilReference(uint8_t stencilRef) + { + m_stencilRef = stencilRef; + } + + uint8_t DynamicDrawContext::GetStencilReference() const + { + return m_stencilRef; + } + void DynamicDrawContext::SetShaderVariant(ShaderVariantId shaderVariantId) { AZ_Assert( m_initialized && m_supportShaderVariants, "DynamicDrawContext is not initialized or unable to support shader variants. " @@ -475,6 +485,9 @@ namespace AZ drawItem.m_viewports = &m_viewport; } + // Set stencil reference. Used when stencil is enabled. + drawItem.m_stencilRef = m_stencilRef; + drawItemInfo.m_sortKey = m_sortKey++; m_cachedDrawItems.emplace_back(drawItemInfo); } diff --git a/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shadervariantlist b/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shadervariantlist index 79bb726c9f..c7eddb10f2 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shadervariantlist +++ b/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shadervariantlist @@ -4,7 +4,7 @@ { "StableId": 1, "Options": { - "o_preMultiplyAlpha": "true", + "o_preMultiplyAlpha": "false", "o_alphaTest": "false", "o_srgbWrite": "true", "o_modulate": "Modulate::None" @@ -14,7 +14,7 @@ "StableId": 2, "Options": { "o_preMultiplyAlpha": "false", - "o_alphaTest": "false", + "o_alphaTest": "true", "o_srgbWrite": "true", "o_modulate": "Modulate::None" } diff --git a/Gems/LyShine/Code/Source/RenderGraph.cpp b/Gems/LyShine/Code/Source/RenderGraph.cpp index d5a1df7b15..ad2c46f8e6 100644 --- a/Gems/LyShine/Code/Source/RenderGraph.cpp +++ b/Gems/LyShine/Code/Source/RenderGraph.cpp @@ -137,7 +137,11 @@ namespace LyShine AZ::RHI::Ptr dynamicDraw = uiRenderer->GetDynamicDrawContext(); const UiRenderer::UiShaderData& uiShaderData = uiRenderer->GetUiShaderData(); - dynamicDraw->SetShaderVariant(uiShaderData.m_shaderVariantDefault); + // Set render state + dynamicDraw->SetStencilState(uiRenderer->GetBaseState().m_stencilState); + dynamicDraw->SetTarget0BlendState(uiRenderer->GetBaseState().m_blendState); + + dynamicDraw->SetShaderVariant(uiRenderer->GetCurrentShaderVariant()); // Set up per draw SRG AZ::Data::Instance drawSrg = dynamicDraw->NewDrawSrg(); @@ -307,7 +311,7 @@ namespace LyShine //////////////////////////////////////////////////////////////////////////////////////////////////// void MaskRenderNode::Render(UiRenderer* uiRenderer) { - int priorBaseState = uiRenderer->GetBaseState(); + UiRenderer::BaseState priorBaseState = uiRenderer->GetBaseState(); if (m_isMaskingEnabled || m_drawBehind) { @@ -369,68 +373,61 @@ namespace LyShine #endif //////////////////////////////////////////////////////////////////////////////////////////////////// - void MaskRenderNode::SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, int priorBaseState) + void MaskRenderNode::SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState) { + UiRenderer::BaseState curBaseState = priorBaseState; + // If using alpha test for drawing the renderable components on this element then we turn on // alpha test as a pre-render step - int alphaTest = 0; - if (m_useAlphaTest) - { - alphaTest = GS_ALPHATEST_GREATER; - } + curBaseState.m_useAlphaTest = m_useAlphaTest; // if either of the draw flags are checked then we may want to draw the renderable component(s) // on this element, otherwise use the color mask to stop them rendering - int colorMask = GS_COLMASK_NONE; + curBaseState.m_blendState.m_enable = false; + curBaseState.m_blendState.m_writeMask = 0x0; if ((m_drawBehind && firstPass) || (m_drawInFront && !firstPass)) { - colorMask = 0; // mask everything, don't write color or alpha, we just write to stencil buffer + curBaseState.m_blendState.m_enable = true; + curBaseState.m_blendState.m_writeMask = 0xF; } - if (m_isMaskingEnabled) + if (m_isMaskingEnabled) { + AZ::RHI::StencilOpState stencilOpState; + stencilOpState.m_func = AZ::RHI::ComparisonFunc::Equal; + // masking is enabled so we want to setup to increment (first pass) or decrement (second pass) // the stencil buff when rendering the renderable component(s) on this element - int passOp = 0; if (firstPass) { - passOp = STENCOP_PASS(FSS_STENCOP_INCR); - gEnv->pRenderer->PushProfileMarker(s_maskIncrProfileMarker); + stencilOpState.m_passOp = AZ::RHI::StencilOp::Increment; } else { - passOp = STENCOP_PASS(FSS_STENCOP_DECR); - gEnv->pRenderer->PushProfileMarker(s_maskDecrProfileMarker); + stencilOpState.m_passOp = AZ::RHI::StencilOp::Decrement; } + curBaseState.m_stencilState.m_frontFace = stencilOpState; + curBaseState.m_stencilState.m_backFace = stencilOpState; + // set up for stencil write - const uint32 stencilRef = uiRenderer->GetStencilRef(); - const uint32 stencilMask = 0xFF; - const uint32 stencilWriteMask = 0xFF; - const int32 stencilState = STENC_FUNC(FSS_STENCFUNC_EQUAL) - | STENCOP_FAIL(FSS_STENCOP_KEEP) | STENCOP_ZFAIL(FSS_STENCOP_KEEP) | passOp; - gEnv->pRenderer->SetStencilState(stencilState, stencilRef, stencilMask, stencilWriteMask); - - // Set the base state that should be used when rendering the renderable component(s) on this - // element - uiRenderer->SetBaseState(priorBaseState | GS_STENCIL | alphaTest | colorMask); + AZ::RHI::Ptr dynamicDraw = uiRenderer->GetDynamicDrawContext(); + dynamicDraw->SetStencilReference(uiRenderer->GetStencilRef()); + curBaseState.m_stencilState.m_enable = true; + curBaseState.m_stencilState.m_writeMask = 0xFF; } else { // masking is not enabled - - // Even if not masking we still use alpha test (if checked). This is primarily to help the user to - // visualize what their alpha tested mask looks like. - if (colorMask || alphaTest) - { - uiRenderer->SetBaseState(priorBaseState | colorMask | alphaTest); - } + curBaseState.m_stencilState.m_enable = false; } + + uiRenderer->SetBaseState(curBaseState); } //////////////////////////////////////////////////////////////////////////////////////////////////// - void MaskRenderNode::SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, int priorBaseState) + void MaskRenderNode::SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState) { if (m_isMaskingEnabled) { @@ -442,26 +439,29 @@ namespace LyShine if (firstPass) { uiRenderer->IncrementStencilRef(); - gEnv->pRenderer->PopProfileMarker(s_maskIncrProfileMarker); } else { uiRenderer->DecrementStencilRef(); - gEnv->pRenderer->PopProfileMarker(s_maskDecrProfileMarker); } - // turn off stencil write and turn on stencil test - const uint32 stencilRef = uiRenderer->GetStencilRef(); - const uint32 stencilMask = 0xFF; - const uint32 stencilWriteMask = 0x00; - const int32 stencilState = STENC_FUNC(FSS_STENCFUNC_EQUAL) - | STENCOP_FAIL(FSS_STENCOP_KEEP) | STENCOP_ZFAIL(FSS_STENCOP_KEEP) | STENCOP_PASS(FSS_STENCOP_KEEP); - gEnv->pRenderer->SetStencilState(stencilState, stencilRef, stencilMask, stencilWriteMask); + AZ::RHI::Ptr dynamicDraw = uiRenderer->GetDynamicDrawContext(); + dynamicDraw->SetStencilReference(uiRenderer->GetStencilRef()); if (firstPass) { - // first pass, turn on stencil test for drawing children - uiRenderer->SetBaseState(priorBaseState | GS_STENCIL); + UiRenderer::BaseState curBaseState = priorBaseState; + + // turn off stencil write and turn on stencil test + curBaseState.m_stencilState.m_enable = true; + curBaseState.m_stencilState.m_writeMask = 0x00; + + AZ::RHI::StencilOpState stencilOpState; + stencilOpState.m_func = AZ::RHI::ComparisonFunc::Equal; + curBaseState.m_stencilState.m_frontFace = stencilOpState; + curBaseState.m_stencilState.m_backFace = stencilOpState; + + uiRenderer->SetBaseState(curBaseState); } else { @@ -475,7 +475,6 @@ namespace LyShine // remove any color mask or alpha test that we set in pre-render uiRenderer->SetBaseState(priorBaseState); } - } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -637,35 +636,24 @@ namespace LyShine //////////////////////////////////////////////////////////////////////////////////////////////////// void RenderGraph::BeginMask(bool isMaskingEnabled, bool useAlphaTest, bool drawBehind, bool drawInFront) { -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (masks and render targets) - // this uses pool allocator MaskRenderNode* maskRenderNode = new MaskRenderNode(m_currentMask, isMaskingEnabled, useAlphaTest, drawBehind, drawInFront); m_currentMask = maskRenderNode; m_renderNodeListStack.push(&maskRenderNode->GetMaskRenderNodeList()); -#else - AZ_UNUSED(drawInFront); - AZ_UNUSED(drawBehind); - AZ_UNUSED(useAlphaTest); - AZ_UNUSED(isMaskingEnabled); -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// void RenderGraph::StartChildrenForMask() { -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (masks and render targets) AZ_Assert(m_currentMask, "Calling StartChildrenForMask while not defining a mask"); m_renderNodeListStack.pop(); m_renderNodeListStack.push(&m_currentMask->GetContentRenderNodeList()); -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// void RenderGraph::EndMask() { -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (masks and render targets) AZ_Assert(m_currentMask, "Calling EndMask while not defining a mask"); if (m_currentMask) { @@ -686,7 +674,6 @@ namespace LyShine m_renderNodeListStack.top()->push_back(newMaskRenderNode); } } -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -996,6 +983,12 @@ namespace LyShine // LYSHINE_ATOM_TODO - will probably need to support this when converting UI Editor to use Atom AZ_UNUSED(viewportSize); + AZ::RHI::Ptr dynamicDraw = uiRenderer->GetDynamicDrawContext(); + + // Disable stencil and enable blend/color write + dynamicDraw->SetStencilState(uiRenderer->GetBaseState().m_stencilState); + dynamicDraw->SetTarget0BlendState(uiRenderer->GetBaseState().m_blendState); + // First render the render targets, they are sorted so that more deeply nested ones are rendered first. #ifdef LYSHINE_ATOM_TODO // keeping this code for reference for future phase (render targets) diff --git a/Gems/LyShine/Code/Source/RenderGraph.h b/Gems/LyShine/Code/Source/RenderGraph.h index 2f1586e857..355616a29a 100644 --- a/Gems/LyShine/Code/Source/RenderGraph.h +++ b/Gems/LyShine/Code/Source/RenderGraph.h @@ -22,12 +22,11 @@ #include #include +#include "UiRenderer.h" #ifndef _RELEASE #include "LyShineDebug.h" #endif -class UiRenderer; - namespace LyShine { enum RenderNodeType @@ -157,8 +156,8 @@ namespace LyShine #endif private: // functions - void SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, int priorBaseState); - void SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, int priorBaseState); + void SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState); + void SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState); private: // data AZStd::vector m_maskRenderNodes; //!< The render nodes used to render the mask shape diff --git a/Gems/LyShine/Code/Source/UiRenderer.cpp b/Gems/LyShine/Code/Source/UiRenderer.cpp index 57acbed5d5..e3ded694b5 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.cpp +++ b/Gems/LyShine/Code/Source/UiRenderer.cpp @@ -20,7 +20,6 @@ #include #include #include -#include // LYSHINE_ATOM_TODO - remove when GS_DEPTHFUNC_LEQUAL reference is removed with LyShine render target Atom conversion #include #include @@ -33,9 +32,7 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// UiRenderer::UiRenderer(AZ::RPI::ViewportContextPtr viewportContext) - : m_baseState(GS_DEPTHFUNC_LEQUAL) - , m_stencilRef(0) - , m_viewportContext(viewportContext) + : m_viewportContext(viewportContext) { // Use bootstrap scene event to indicate when the RPI has fully // initialized with all assets loaded and is ready to be used @@ -127,6 +124,8 @@ void UiRenderer::CreateDynamicDrawContext(AZ::RPI::ScenePtr scene, AZ::Data::Ins { "TEXCOORD", AZ::RHI::Format::R32G32_FLOAT }, { "BLENDINDICES", AZ::RHI::Format::R16G16_UINT } } ); + m_dynamicDraw->AddDrawStateOptions(AZ::RPI::DynamicDrawContext::DrawStateOptions::StencilState + | AZ::RPI::DynamicDrawContext::DrawStateOptions::BlendMode); m_dynamicDraw->EndInit(); } @@ -170,25 +169,24 @@ void UiRenderer::CacheShaderData(const AZ::RHI::Ptr shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("true"))); shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None"))); m_uiShaderData.m_shaderVariantDefault = dynamicDraw->UseShaderVariant(shaderOptionsDefault); + AZ::RPI::ShaderOptionList shaderOptionsAlphaTest; + shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_preMultiplyAlpha"), AZ::Name("false"))); + shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_alphaTest"), AZ::Name("true"))); + shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("true"))); + shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None"))); + m_uiShaderData.m_shaderVariantAlphaTest = dynamicDraw->UseShaderVariant(shaderOptionsAlphaTest); } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiRenderer::BeginUiFrameRender() { -#ifdef LYSHINE_ATOM_TODO - m_renderer = gEnv->pRenderer; - - // we are rendering at the end of the frame, after tone mapping, so we should be writing sRGB values - m_renderer->SetSrgbWrite(true); - #ifndef _RELEASE if (m_debugTextureDataRecordLevel > 0) { m_texturesUsedInFrame.clear(); } #endif -#endif - + // Various platform drivers expect all texture slots used in the shader to be bound BindNullTexture(); } @@ -204,18 +202,10 @@ void UiRenderer::EndUiFrameRender() //////////////////////////////////////////////////////////////////////////////////////////////////// void UiRenderer::BeginCanvasRender() { -#ifdef LYSHINE_ATOM_TODO - m_baseState = GS_NODEPTHTEST; - m_stencilRef = 0; - // Set default starting state - IRenderer* renderer = gEnv->pRenderer; - - renderer->SetCullMode(R_CULL_DISABLE); - renderer->SetColorOp(eCO_MODULATE, eCO_MODULATE, DEF_TEXARG0, DEF_TEXARG0); - renderer->SetState(GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA | GS_NODEPTHTEST); -#endif + // Set base state + m_baseState.ResetToDefault(); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -229,11 +219,13 @@ AZ::RHI::Ptr UiRenderer::GetDynamicDrawContext() return m_dynamicDraw; } +//////////////////////////////////////////////////////////////////////////////////////////////////// const UiRenderer::UiShaderData& UiRenderer::GetUiShaderData() { return m_uiShaderData; } +//////////////////////////////////////////////////////////////////////////////////////////////////// AZ::Matrix4x4 UiRenderer::GetModelViewProjectionMatrix() { auto viewportContext = GetViewportContext(); @@ -253,6 +245,7 @@ AZ::Matrix4x4 UiRenderer::GetModelViewProjectionMatrix() return modelViewProjMat; } +//////////////////////////////////////////////////////////////////////////////////////////////////// AZ::Vector2 UiRenderer::GetViewportSize() { auto viewportContext = GetViewportContext(); @@ -267,17 +260,30 @@ AZ::Vector2 UiRenderer::GetViewportSize() } //////////////////////////////////////////////////////////////////////////////////////////////////// -int UiRenderer::GetBaseState() +UiRenderer::BaseState UiRenderer::GetBaseState() { return m_baseState; } //////////////////////////////////////////////////////////////////////////////////////////////////// -void UiRenderer::SetBaseState(int state) +void UiRenderer::SetBaseState(BaseState state) { m_baseState = state; } +//////////////////////////////////////////////////////////////////////////////////////////////////// +AZ::RPI::ShaderVariantId UiRenderer::GetCurrentShaderVariant() +{ + AZ::RPI::ShaderVariantId variantId = m_uiShaderData.m_shaderVariantDefault; + + if (m_baseState.m_useAlphaTest) + { + variantId = m_uiShaderData.m_shaderVariantAlphaTest; + } + + return variantId; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// uint32 UiRenderer::GetStencilRef() { @@ -354,6 +360,7 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) { if (recordingOption > 0) { +#ifdef LYSHINE_ATOM_TODO // Convert debug to use Atom images // compute the total area of all the textures, also create a vector that we can sort by area AZStd::vector textures; int totalArea = 0; @@ -431,6 +438,7 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) texture->GetWidth(), texture->GetHeight(), texture->GetDataSize(), texture->GetFormatName(), texture->GetName()); WriteLine(buffer, white); } +#endif } } diff --git a/Gems/LyShine/Code/Source/UiRenderer.h b/Gems/LyShine/Code/Source/UiRenderer.h index 888c88586a..413e8c45cf 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.h +++ b/Gems/LyShine/Code/Source/UiRenderer.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #ifndef _RELEASE @@ -38,6 +39,36 @@ public: // types AZ::RHI::ShaderInputConstantIndex m_isClampInputIndex; AZ::RPI::ShaderVariantId m_shaderVariantDefault; + AZ::RPI::ShaderVariantId m_shaderVariantAlphaTest; + }; + + // Base state + struct BaseState + { + BaseState() + { + ResetToDefault(); + } + + void ResetToDefault() + { + // Enable blend/color write + m_blendState.m_enable = true; + m_blendState.m_writeMask = 0xF; + m_blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource; + m_blendState.m_blendDest = AZ::RHI::BlendFactor::AlphaSourceInverse; + m_blendState.m_blendOp = AZ::RHI::BlendOp::Add; + + // Disable stencil + m_stencilState = AZ::RHI::StencilState(); + m_stencilState.m_enable = 0; + + m_useAlphaTest = false; + } + + AZ::RHI::TargetBlendState m_blendState; + AZ::RHI::StencilState m_stencilState; + bool m_useAlphaTest = false; }; public: // member functions @@ -74,10 +105,13 @@ public: // member functions AZ::Vector2 GetViewportSize(); //! Get the current base state - int GetBaseState(); + BaseState GetBaseState(); //! Set the base state - void SetBaseState(int state); + void SetBaseState(BaseState state); + + //! Get the shader variant based on current render properties + AZ::RPI::ShaderVariantId GetCurrentShaderVariant(); //! Get the current stencil test reference value uint32 GetStencilRef(); @@ -126,8 +160,8 @@ protected: // attributes static constexpr char LogName[] = "UiRenderer"; - int m_baseState; - uint32 m_stencilRef; + BaseState m_baseState; + uint32 m_stencilRef = 0; UiShaderData m_uiShaderData; AZ::RHI::Ptr m_dynamicDraw; diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleMask.tif b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleMask.tif new file mode 100644 index 0000000000..fd29516609 --- /dev/null +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleMask.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8474b897fe02f70ed8d0e5c47cae8c816c832a7a5739b8c32317737fd275774f +size 1069752 From bf1b8001361923548feb06ea077ce4fcf61bc8fc Mon Sep 17 00:00:00 2001 From: srikappa-amzn Date: Fri, 11 Jun 2021 10:25:28 -0700 Subject: [PATCH 146/233] minor change to order of if and foreach --- scripts/ctest/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/ctest/CMakeLists.txt b/scripts/ctest/CMakeLists.txt index c4f86087b1..c07aaf4bff 100644 --- a/scripts/ctest/CMakeLists.txt +++ b/scripts/ctest/CMakeLists.txt @@ -20,8 +20,8 @@ endif() # Tests ################################################################################ -foreach(suite_name ${LY_TEST_GLOBAL_KNOWN_SUITE_NAMES}) - if(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED) +if(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED) + foreach(suite_name ${LY_TEST_GLOBAL_KNOWN_SUITE_NAMES}) ly_add_pytest( NAME pytest_sanity_${suite_name}_no_gpu PATH ${CMAKE_CURRENT_LIST_DIR}/sanity_test.py @@ -34,8 +34,8 @@ foreach(suite_name ${LY_TEST_GLOBAL_KNOWN_SUITE_NAMES}) TEST_SUITE ${suite_name} TEST_REQUIRES gpu ) - endif() -endforeach() + endforeach() +endif() # EPB Sanity test is being registered here to validate that the ly_add_editor_python_test function works. #if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedTesting IN_LIST LY_PROJECTS_TARGET_NAME) From 4818d1ce809436efd72b4fa51064e60fae565640 Mon Sep 17 00:00:00 2001 From: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Fri, 11 Jun 2021 12:25:45 -0500 Subject: [PATCH 147/233] {LYN-4224} Fix for the file scan slowdown (#1252) * {LYN-4224} Fix for the file scan slowdown (#1183) * {LYN-4224} Fix for the file scan slowdown * Fixed a slowdown in the file scanning logic * Improved the file scanning logic from previous code by 40% Tests: Using Testing\Pytest\AutomatedTesting_BlastTest old code: === 7 passed in 96.13s (0:01:36) === current code: === 7 passed in 160.45s (0:02:40) ==== newest code: === 7 passed in 52.91s === * fixing a unit test compile error * unit test fixes * another file improvement * fix for legacy level loading taking too long * making an enum for the search types * switched the enum to "allow" types to make the input more clear * got rid of orphaned const variables --- Code/CryEngine/CryCommon/Mocks/ICryPakMock.h | 2 +- .../CrySystem/LevelSystem/LevelSystem.cpp | 11 ++-- .../AzFramework/Archive/Archive.cpp | 24 +++++++- .../AzFramework/AzFramework/Archive/Archive.h | 2 +- .../AzFramework/Archive/ArchiveFindData.cpp | 60 ++++++++++--------- .../AzFramework/Archive/ArchiveFindData.h | 7 +-- .../AzFramework/Archive/IArchive.h | 9 ++- Code/Sandbox/Editor/CryEditDoc.cpp | 2 +- .../Code/Tests/AudioSystemEditorTest.cpp | 13 ++-- 9 files changed, 79 insertions(+), 51 deletions(-) diff --git a/Code/CryEngine/CryCommon/Mocks/ICryPakMock.h b/Code/CryEngine/CryCommon/Mocks/ICryPakMock.h index 2d451793bc..072a798985 100644 --- a/Code/CryEngine/CryCommon/Mocks/ICryPakMock.h +++ b/Code/CryEngine/CryCommon/Mocks/ICryPakMock.h @@ -67,7 +67,7 @@ struct CryPakMock MOCK_METHOD1(PoolMalloc, void*(size_t size)); MOCK_METHOD1(PoolFree, void(void* p)); MOCK_METHOD3(PoolAllocMemoryBlock, AZStd::intrusive_ptr (size_t nSize, const char* sUsage, size_t nAlign)); - MOCK_METHOD3(FindFirst, AZ::IO::ArchiveFileIterator(AZStd::string_view pDir, uint32_t nFlags, bool bAllOwUseFileSystem)); + MOCK_METHOD2(FindFirst, AZ::IO::ArchiveFileIterator(AZStd::string_view pDir, AZ::IO::IArchive::EFileSearchType)); MOCK_METHOD1(FindNext, AZ::IO::ArchiveFileIterator(AZ::IO::ArchiveFileIterator handle)); MOCK_METHOD1(FindClose, bool(AZ::IO::ArchiveFileIterator)); MOCK_METHOD1(GetModificationTime, AZ::IO::IArchive::FileTime(AZ::IO::HandleType f)); diff --git a/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp b/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp index c36b9bcee7..08caeeeb70 100644 --- a/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp +++ b/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp @@ -306,8 +306,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder) AZStd::unordered_set pakList; - bool allowFileSystem = true; - AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(search.c_str(), 0, allowFileSystem); + AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(search.c_str(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskOnly); if (handle) { @@ -320,7 +319,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder) { if (AZ::StringFunc::Equal(handle.m_filename.data(), LevelPakName)) { - // level folder contain pak files like 'level.pak' + // level folder contain pak files like 'level.pak' // which we only want to load during level loading. continue; } @@ -351,7 +350,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder) PopulateLevels(search, folder, pPak, modFolder, false); // Load levels outside of the bundles to maintain backward compatibility. PopulateLevels(search, folder, pPak, modFolder, true); - + } void CLevelSystem::PopulateLevels( @@ -360,7 +359,7 @@ void CLevelSystem::PopulateLevels( { // allow this find first to actually touch the file system // (causes small overhead but with minimal amount of levels this should only be around 150ms on actual DVD Emu) - AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(searchPattern.c_str(), 0, fromFileSystemOnly); + AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(searchPattern.c_str(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskOnly); if (handle) { @@ -973,7 +972,7 @@ void CLevelSystem::UnloadLevel() m_lastLevelName.clear(); SAFE_RELEASE(m_pCurrentLevel); - + // Force Lua garbage collection (may no longer be needed now the legacy renderer has been removed). // Normally the GC step is triggered at the end of this method (by the ESYSTEM_EVENT_LEVEL_POST_UNLOAD event). EBUS_EVENT(AZ::ScriptSystemRequestBus, GarbageCollect); diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp index 04573eb2e5..5cb2006447 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp @@ -1290,7 +1290,7 @@ namespace AZ::IO ////////////////////////////////////////////////////////////////////////// - AZ::IO::ArchiveFileIterator Archive::FindFirst(AZStd::string_view pDir, [[maybe_unused]] uint32_t nPathFlags, bool bAllowUseFileSystem) + AZ::IO::ArchiveFileIterator Archive::FindFirst(AZStd::string_view pDir, EFileSearchType searchType) { auto szFullPath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pDir); if (!szFullPath) @@ -1299,8 +1299,26 @@ namespace AZ::IO return {}; } + bool bScanZips{}; + bool bAllowUseFileSystem{}; + switch (searchType) + { + case IArchive::eFileSearchType_AllowInZipsOnly: + bAllowUseFileSystem = false; + bScanZips = true; + break; + case IArchive::eFileSearchType_AllowOnDiskAndInZips: + bAllowUseFileSystem = true; + bScanZips = true; + break; + case IArchive::eFileSearchType_AllowOnDiskOnly: + bAllowUseFileSystem = true; + bScanZips = false; + break; + } + AZStd::intrusive_ptr pFindData = new AZ::IO::FindData(); - pFindData->Scan(this, szFullPath->Native(), bAllowUseFileSystem); + pFindData->Scan(this, szFullPath->Native(), bAllowUseFileSystem, bScanZips); return pFindData->Fetch(); } @@ -1676,7 +1694,7 @@ namespace AZ::IO return true; } - if (AZ::IO::ArchiveFileIterator fileIterator = FindFirst(pWildcardIn, 0, true); fileIterator) + if (AZ::IO::ArchiveFileIterator fileIterator = FindFirst(pWildcardIn, IArchive::eFileSearchType_AllowOnDiskOnly); fileIterator) { AZStd::vector files; do diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h index 997b3e3d2c..329beb4291 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h @@ -234,7 +234,7 @@ namespace AZ::IO uint64_t FTell(AZ::IO::HandleType handle) override; int FFlush(AZ::IO::HandleType handle) override; int FClose(AZ::IO::HandleType handle) override; - AZ::IO::ArchiveFileIterator FindFirst(AZStd::string_view pDir, uint32_t nPathFlags = 0, bool bAllOwUseFileSystem = false) override; + AZ::IO::ArchiveFileIterator FindFirst(AZStd::string_view pDir, EFileSearchType searchType = eFileSearchType_AllowInZipsOnly) override; AZ::IO::ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator fileIterator) override; bool FindClose(AZ::IO::ArchiveFileIterator fileIterator) override; int FEof(AZ::IO::HandleType handle) override; diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp index 1794ae90e7..05da5f16eb 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp @@ -77,7 +77,7 @@ namespace AZ::IO return m_findData && m_lastFetchValid; } - void FindData::Scan(IArchive* archive, AZStd::string_view szDir, bool bAllowUseFS) + void FindData::Scan(IArchive* archive, AZStd::string_view szDir, bool bAllowUseFS, bool bScanZips) { // get the priority into local variable to avoid it changing in the course of // this function execution @@ -87,12 +87,18 @@ namespace AZ::IO { // first, find the file system files ScanFS(archive, szDir); - ScanZips(archive, szDir); + if (bScanZips) + { + ScanZips(archive, szDir); + } } else { // first, find the zip files - ScanZips(archive, szDir); + if (bScanZips) + { + ScanZips(archive, szDir); + } if (bAllowUseFS || nVarPakPriority != ArchiveLocationPriority::ePakPriorityPakOnly) { ScanFS(archive, szDir); @@ -111,30 +117,31 @@ namespace AZ::IO } AZ::IO::FileIOBase::GetDirectInstance()->FindFiles(searchDirectory.c_str(), pattern.c_str(), [&](const char* filePath) -> bool { - AZ::IO::FileDesc fileDesc; - AZStd::string filePathEntry{filePath}; + AZ::IO::ArchiveFileIterator fileIterator; + fileIterator.m_filename = AZ::IO::PathView(filePath).Filename().Native(); + fileIterator.m_fileDesc.nAttrib = {}; if (AZ::IO::FileIOBase::GetDirectInstance()->IsDirectory(filePath)) { - fileDesc.nAttrib = fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::Subdirectory; + fileIterator.m_fileDesc.nAttrib = fileIterator.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::Subdirectory; + m_fileStack.emplace_back(AZStd::move(fileIterator)); } else { if (AZ::IO::FileIOBase::GetDirectInstance()->IsReadOnly(filePath)) { - fileDesc.nAttrib = fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::ReadOnly; + fileIterator.m_fileDesc.nAttrib = fileIterator.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::ReadOnly; } AZ::u64 fileSize = 0; AZ::IO::FileIOBase::GetDirectInstance()->Size(filePath, fileSize); - fileDesc.nSize = fileSize; - fileDesc.tWrite = AZ::IO::FileIOBase::GetDirectInstance()->ModificationTime(filePath); + fileIterator.m_fileDesc.nSize = fileSize; + fileIterator.m_fileDesc.tWrite = AZ::IO::FileIOBase::GetDirectInstance()->ModificationTime(filePath); // These times are not supported by our file interface - fileDesc.tAccess = fileDesc.tWrite; - fileDesc.tCreate = fileDesc.tWrite; + fileIterator.m_fileDesc.tAccess = fileIterator.m_fileDesc.tWrite; + fileIterator.m_fileDesc.tCreate = fileIterator.m_fileDesc.tWrite; + m_fileStack.emplace_back(AZStd::move(fileIterator)); } - [[maybe_unused]] auto result = m_mapFiles.emplace(AZStd::move(filePathEntry), fileDesc); - AZ_Assert(result.second, "Failed to insert FindData entry for filePath %s", filePath); return true; }); } @@ -164,7 +171,7 @@ namespace AZ::IO fileDesc.nAttrib = AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive; fileDesc.nSize = fileEntry->desc.lSizeUncompressed; fileDesc.tWrite = fileEntry->GetModificationTime(); - m_mapFiles.emplace(fname, fileDesc); + m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ this, fname, fileDesc }); } ZipDir::FindDir findDirectoryEntry(zipCache); @@ -177,7 +184,7 @@ namespace AZ::IO } AZ::IO::FileDesc fileDesc; fileDesc.nAttrib = AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive | AZ::IO::FileDesc::Attribute::Subdirectory; - m_mapFiles.emplace(fname, fileDesc); + m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ this, fname, fileDesc }); } }; @@ -246,7 +253,7 @@ namespace AZ::IO if (!bindRootIter->empty() && AZStd::wildcard_match(sourcePathRemainder.Native(), bindRootIter->Native())) { AZ::IO::FileDesc fileDesc{ AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive | AZ::IO::FileDesc::Attribute::Subdirectory }; - m_mapFiles.emplace(bindRootIter->Native(), fileDesc); + m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ this, bindRootIter->Native(), fileDesc }); } } else @@ -262,22 +269,19 @@ namespace AZ::IO AZ::IO::ArchiveFileIterator FindData::Fetch() { - AZ::IO::ArchiveFileIterator fileIterator; - fileIterator.m_findData = this; - if (m_mapFiles.empty()) + if (m_fileStack.empty()) { - return fileIterator; + AZ::IO::ArchiveFileIterator emptyFileIterator; + emptyFileIterator.m_lastFetchValid = false; + emptyFileIterator.m_findData = this; + return emptyFileIterator; } - auto pakFileIter = m_mapFiles.begin(); - AZStd::string fullFilePath; - AZ::StringFunc::Path::GetFullFileName(pakFileIter->first.c_str(), fullFilePath); - fileIterator.m_filename = AZStd::move(fullFilePath); - fileIterator.m_fileDesc = pakFileIter->second; - fileIterator.m_lastFetchValid = true; - // Remove Fetched item from the FindData map so that the iteration continues - m_mapFiles.erase(pakFileIter); + AZ::IO::ArchiveFileIterator fileIterator{ m_fileStack.back() }; + fileIterator.m_lastFetchValid = true; + fileIterator.m_findData = this; + m_fileStack.pop_back(); return fileIterator; } } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.h b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.h index d5e23779fc..a07e98c81f 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.h @@ -15,7 +15,6 @@ #include #include - namespace AZ::IO { struct IArchive; @@ -74,13 +73,13 @@ namespace AZ::IO AZ_CLASS_ALLOCATOR(FindData, AZ::SystemAllocator, 0); FindData() = default; AZ::IO::ArchiveFileIterator Fetch(); - void Scan(IArchive* archive, AZStd::string_view path, bool bAllowUseFS = false); + void Scan(IArchive* archive, AZStd::string_view path, bool bAllowUseFS = false, bool bScanZips = true); protected: void ScanFS(IArchive* archive, AZStd::string_view path); void ScanZips(IArchive* archive, AZStd::string_view path); - using FileMap = AZStd::map; - FileMap m_mapFiles; + using FileStack = AZStd::vector; + FileStack m_fileStack; }; } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h b/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h index ce8403b033..08bd87c334 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h @@ -197,6 +197,13 @@ namespace AZ::IO eInMemoryPakLocale_PAK, }; + enum EFileSearchType + { + eFileSearchType_AllowInZipsOnly = 0, + eFileSearchType_AllowOnDiskAndInZips, + eFileSearchType_AllowOnDiskOnly + }; + using SignedFileSize = int64_t; virtual ~IArchive() = default; @@ -315,7 +322,7 @@ namespace AZ::IO // Arguments: // nFlags is a combination of EPathResolutionRules flags. - virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, uint32_t nFlags = 0, bool bAllowUseFileSystem = false) = 0; + virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, EFileSearchType searchType = eFileSearchType_AllowInZipsOnly) = 0; virtual ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator handle) = 0; virtual bool FindClose(AZ::IO::ArchiveFileIterator handle) = 0; //returns file modification time diff --git a/Code/Sandbox/Editor/CryEditDoc.cpp b/Code/Sandbox/Editor/CryEditDoc.cpp index 5b2a4b9a83..77d5794ab3 100644 --- a/Code/Sandbox/Editor/CryEditDoc.cpp +++ b/Code/Sandbox/Editor/CryEditDoc.cpp @@ -1139,7 +1139,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename) const QString oldLevelPattern = QDir(oldLevelFolder).absoluteFilePath("*.*"); const QString oldLevelName = Path::GetFile(GetLevelPathName()); const QString oldLevelXml = Path::ReplaceExtension(oldLevelName, "xml"); - AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), 0, true); + AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskAndInZips); if (findHandle) { do diff --git a/Gems/AudioSystem/Code/Tests/AudioSystemEditorTest.cpp b/Gems/AudioSystem/Code/Tests/AudioSystemEditorTest.cpp index e086246e29..d7f9b31e25 100644 --- a/Gems/AudioSystem/Code/Tests/AudioSystemEditorTest.cpp +++ b/Gems/AudioSystem/Code/Tests/AudioSystemEditorTest.cpp @@ -36,14 +36,14 @@ namespace CustomMocks : m_levelName(levelName) {} - AZ::IO::ArchiveFileIterator FindFirst([[maybe_unused]] AZStd::string_view dir, [[maybe_unused]] unsigned int flags, [[maybe_unused]] bool allowUseFileSystem) override + AZ::IO::ArchiveFileIterator FindFirst([[maybe_unused]] AZStd::string_view dir, AZ::IO::IArchive::EFileSearchType) override { AZ::IO::FileDesc fileDesc; fileDesc.nSize = sizeof(AZ::IO::FileDesc); // Add a filename and file description reference to the TestFindData map to make sure the file iterator is valid - AZStd::intrusive_ptr findData = new TestFindData{}; - findData->m_mapFiles.emplace(m_levelName, fileDesc); - return findData->Fetch(); + m_findData = new TestFindData(); + m_findData->m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ static_cast(m_findData.get()), m_levelName, fileDesc }); + return m_findData->Fetch(); } AZ::IO::ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator iter) override @@ -54,13 +54,14 @@ namespace CustomMocks // public: for easy resetting... AZStd::string m_levelName; - // Add an inherited FindData class to control the adding of a mapfile which indicates that a FileIterator is valid struct TestFindData : AZ::IO::FindData { - using AZ::IO::FindData::m_mapFiles; + using AZ::IO::FindData::m_fileStack; }; + + AZStd::intrusive_ptr m_findData; }; } // namespace CustomMocks From 9bb34a61219ea8b8487cedb71323bdd11dbdf0d1 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Fri, 11 Jun 2021 11:48:59 -0700 Subject: [PATCH 148/233] Remove references to Wireframe menu item that was removed in a previous PR (#1263) --- Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp b/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp index ebc688f9da..0dec8e3af6 100644 --- a/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp +++ b/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp @@ -715,11 +715,11 @@ QMenu* LevelEditorMenuHandler::CreateViewMenu() { return view.IsViewportPane(); }); -#endif - viewportViewsMenuWrapper.AddAction(ID_WIREFRAME); viewportViewsMenuWrapper.AddSeparator(); +#endif + if (CViewManager::IsMultiViewportEnabled()) { viewportViewsMenuWrapper.AddAction(ID_VIEW_CONFIGURELAYOUT); From 01f80c7b408f0659d5360ff1a435923f057f34e2 Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Fri, 11 Jun 2021 14:39:53 -0500 Subject: [PATCH 149/233] Changing the default level to use 1024 shadowmaps instead of 2048, pcf instead of esm+pcf, and bifubic filtering instead of boundary search. This should make the default level more perfomant and it actuallly looks a little better. (#1273) --- .../CommonFeatures/Assets/LevelAssets/default.slice | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice b/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice index c20c6cde0b..b4c9eac10f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice +++ b/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice @@ -960,7 +960,7 @@ - + @@ -968,10 +968,11 @@ - + + @@ -1109,4 +1110,3 @@ - From 015f85db20ee0067084589d8e71cf6d598953eac Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Fri, 11 Jun 2021 15:15:45 -0500 Subject: [PATCH 150/233] [LYN-4160] Fixed intermittent crash by changing instantiate prefab dialog to use the AzToolsFramework::GetActiveWindow() helper which always gives a valid active window. --- .../UI/Prefab/PrefabIntegrationManager.cpp | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 77918565e4..54ad96535f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -588,15 +589,6 @@ namespace AzToolsFramework bool PrefabIntegrationManager::QueryUserForPrefabFilePath(AZStd::string& outPrefabFilePath) { - QWidget* mainWindow = nullptr; - EditorRequests::Bus::BroadcastResult(mainWindow, &EditorRequests::Bus::Events::GetMainWindow); - - if (mainWindow == nullptr) - { - AZ_Assert(false, "Prefab - Could not detect Editor main window to generate the asset picker."); - return false; - } - AssetSelectionModel selection; // Note, stringfilter will match every source file CONTAINING ".prefab". @@ -624,7 +616,7 @@ namespace AzToolsFramework selection.SetDisplayFilter(compositeFilterPtr); selection.SetSelectionFilter(compositeFilterPtr); - AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, mainWindow); + AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, AzToolsFramework::GetActiveWindow()); if (!selection.IsValid()) { @@ -983,12 +975,7 @@ namespace AzToolsFramework includedEntities.c_str(), referencedEntities.c_str()); - QWidget* mainWindow = nullptr; - AzToolsFramework::EditorRequests::Bus::BroadcastResult( - mainWindow, - &AzToolsFramework::EditorRequests::Bus::Events::GetMainWindow); - - QMessageBox msgBox(mainWindow); + QMessageBox msgBox(AzToolsFramework::GetActiveWindow()); msgBox.setWindowTitle("External Entity References"); msgBox.setText("The prefab contains references to external entities that are not selected."); msgBox.setInformativeText("You can move the referenced entities into this prefab or retain the external references."); From 3895c93e04a3fe33c458c0a5eb7ccb6768d956ce Mon Sep 17 00:00:00 2001 From: greerdv Date: Fri, 11 Jun 2021 22:05:37 +0100 Subject: [PATCH 151/233] avoid some divisions by zero in simd math types --- .../AzCore/Math/Internal/SimdMathCommon_simd.inl | 13 +++++++++++-- .../AzCore/Math/Internal/SimdMathVec2_sse.inl | 2 ++ .../AzCore/Math/Internal/SimdMathVec3_sse.inl | 2 ++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathCommon_simd.inl b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathCommon_simd.inl index 770a8fa104..d06372256d 100644 --- a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathCommon_simd.inl +++ b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathCommon_simd.inl @@ -292,8 +292,13 @@ namespace AZ const typename VecType::FloatType cmp2 = VecType::AndNot(cmp0, cmp1); // -1/x + // this step is calculated for all values of x, but only used if x > Sqrt(2) + 1 + // in order to avoid a division by zero, detect if xabs is zero here and replace it with an arbitrary value + // if xabs does equal zero, the value here doesn't matter because the result will be thrown away + typename VecType::FloatType xabsSafe = + VecType::Add(xabs, VecType::And(VecType::CmpEq(xabs, VecType::ZeroFloat()), FastLoadConstant(Simd::g_vec1111))); const typename VecType::FloatType y0 = VecType::And(cmp0, FastLoadConstant(Simd::g_HalfPi)); - typename VecType::FloatType x0 = VecType::Div(FastLoadConstant(Simd::g_vec1111), xabs); + typename VecType::FloatType x0 = VecType::Div(FastLoadConstant(Simd::g_vec1111), xabsSafe); x0 = VecType::Xor(x0, VecType::CastToFloat(FastLoadConstant(Simd::g_negateMask))); const typename VecType::FloatType y1 = VecType::And(cmp2, FastLoadConstant(Simd::g_QuarterPi)); @@ -368,8 +373,12 @@ namespace AZ typename VecType::FloatType offset = VecType::And(x_lt_0, offset1); + // the result of this part of the computation is thrown away if x equals 0, + // but if x does equal 0, it will cause a division by zero + // so replace zero by an arbitrary value here in that case + typename VecType::FloatType xSafe = VecType::Add(x, VecType::And(x_eq_0, FastLoadConstant(Simd::g_vec1111))); const typename VecType::FloatType atan_mask = VecType::Not(VecType::Or(x_eq_0, y_eq_0)); - const typename VecType::FloatType atan_arg = VecType::Div(y, x); + const typename VecType::FloatType atan_arg = VecType::Div(y, xSafe); typename VecType::FloatType atan_result = VecType::Atan(atan_arg); atan_result = VecType::Add(atan_result, offset); atan_result = VecType::AndNot(pio2_mask, atan_result); diff --git a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec2_sse.inl b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec2_sse.inl index 8f35af258c..63e2dca8fd 100644 --- a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec2_sse.inl +++ b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec2_sse.inl @@ -471,6 +471,7 @@ namespace AZ AZ_MATH_INLINE Vec2::FloatType Vec2::Reciprocal(FloatArgType value) { + value = Sse::ReplaceFourth(Sse::ReplaceThird(value, 1.0f), 1.0f); return Sse::Reciprocal(value); } @@ -513,6 +514,7 @@ namespace AZ AZ_MATH_INLINE Vec2::FloatType Vec2::SqrtInv(FloatArgType value) { + value = Sse::ReplaceFourth(Sse::ReplaceThird(value, 1.0f), 1.0f); return Sse::SqrtInv(value); } diff --git a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec3_sse.inl b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec3_sse.inl index 78a0d5db66..75ee2ab7c5 100644 --- a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec3_sse.inl +++ b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec3_sse.inl @@ -507,6 +507,7 @@ namespace AZ AZ_MATH_INLINE Vec3::FloatType Vec3::Reciprocal(FloatArgType value) { + value = Sse::ReplaceFourth(value, 1.0f); return Sse::Reciprocal(value); } @@ -549,6 +550,7 @@ namespace AZ AZ_MATH_INLINE Vec3::FloatType Vec3::SqrtInv(FloatArgType value) { + value = Sse::ReplaceFourth(value, 1.0f); return Sse::SqrtInv(value); } From e6b8dc2ce1cb8571e9197cfec15b73ac795e7166 Mon Sep 17 00:00:00 2001 From: amzn-sj Date: Fri, 11 Jun 2021 14:37:12 -0700 Subject: [PATCH 152/233] Disable AzTest and AzTestRunner in monolithic builds --- Code/Framework/AzTest/CMakeLists.txt | 42 ++++++++++++++------------ Code/Tools/AzTestRunner/CMakeLists.txt | 2 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Code/Framework/AzTest/CMakeLists.txt b/Code/Framework/AzTest/CMakeLists.txt index fe5ec2d0ff..d7b8813639 100644 --- a/Code/Framework/AzTest/CMakeLists.txt +++ b/Code/Framework/AzTest/CMakeLists.txt @@ -8,25 +8,27 @@ # remove or modify any license notices. This file is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # - -ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/AzTest/Platform/${PAL_PLATFORM_NAME}) -ly_add_target( - NAME AzTest STATIC - NAMESPACE AZ - FILES_CMAKE - AzTest/aztest_files.cmake - ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake - INCLUDE_DIRECTORIES - PUBLIC - . - ${pal_dir} - BUILD_DEPENDENCIES - PUBLIC - 3rdParty::googletest::GMock - 3rdParty::googletest::GTest - 3rdParty::GoogleBenchmark - AZ::AzCore - PLATFORM_INCLUDE_FILES +if(NOT LY_MONOLITHIC_GAME) + ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/AzTest/Platform/${PAL_PLATFORM_NAME}) + + ly_add_target( + NAME AzTest STATIC + NAMESPACE AZ + FILES_CMAKE + AzTest/aztest_files.cmake + ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + . + ${pal_dir} + BUILD_DEPENDENCIES + PUBLIC + 3rdParty::googletest::GMock + 3rdParty::googletest::GTest + 3rdParty::GoogleBenchmark + AZ::AzCore + PLATFORM_INCLUDE_FILES ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake -) + ) +endif() diff --git a/Code/Tools/AzTestRunner/CMakeLists.txt b/Code/Tools/AzTestRunner/CMakeLists.txt index e6dd09e15b..fcff173b01 100644 --- a/Code/Tools/AzTestRunner/CMakeLists.txt +++ b/Code/Tools/AzTestRunner/CMakeLists.txt @@ -13,7 +13,7 @@ ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${P include(${pal_dir}/platform_traits_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) -if(PAL_TRAIT_AZTESTRUNNER_SUPPORTED) +if(PAL_TRAIT_AZTESTRUNNER_SUPPORTED AND NOT LY_MONOLITHIC_GAME) ly_add_target( NAME AzTestRunner ${PAL_TRAIT_AZTESTRUNNER_LAUNCHER_TYPE} From 21f209311cd50ec2e4cb989bac4333d390cd1b3c Mon Sep 17 00:00:00 2001 From: amzn-sj Date: Fri, 11 Jun 2021 14:54:24 -0700 Subject: [PATCH 153/233] Parameter may be unused --- Code/LauncherUnified/StaticModules.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/LauncherUnified/StaticModules.in b/Code/LauncherUnified/StaticModules.in index 03b22b076a..5f2c9d7526 100644 --- a/Code/LauncherUnified/StaticModules.in +++ b/Code/LauncherUnified/StaticModules.in @@ -27,7 +27,7 @@ namespace AZ ${extern_module_declarations} -extern "C" void CreateStaticModules(AZStd::vector& modulesOut) +extern "C" void CreateStaticModules([[maybe_unused]] AZStd::vector& modulesOut) { ${module_invocations} } From ac5196dbf531c66c076f7ffda243a235013d3fda Mon Sep 17 00:00:00 2001 From: amzn-sj Date: Fri, 11 Jun 2021 15:19:28 -0700 Subject: [PATCH 154/233] Revert previous change --- Code/LauncherUnified/StaticModules.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/LauncherUnified/StaticModules.in b/Code/LauncherUnified/StaticModules.in index 5f2c9d7526..03b22b076a 100644 --- a/Code/LauncherUnified/StaticModules.in +++ b/Code/LauncherUnified/StaticModules.in @@ -27,7 +27,7 @@ namespace AZ ${extern_module_declarations} -extern "C" void CreateStaticModules([[maybe_unused]] AZStd::vector& modulesOut) +extern "C" void CreateStaticModules(AZStd::vector& modulesOut) { ${module_invocations} } From f6a6614e4b68d3a5dfcdd979e61a619a95d60931 Mon Sep 17 00:00:00 2001 From: amzn-sj Date: Fri, 11 Jun 2021 15:29:46 -0700 Subject: [PATCH 155/233] StaticModules.in must be generated after the gems are enabled --- CMakeLists.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0585089325..4a12e0c50a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,24 +105,25 @@ endforeach() # Post-processing ################################################################################ # The following steps have to be done after all targets are registered: -# Defer generation of the StaticModules.inl file which is needed to create the AZ::Module derived class in monolithic -# builds until after all the targets are known -ly_delayed_generate_static_modules_inl() # 1. Add any dependencies registered via ly_enable_gems ly_enable_gems_delayed() -# 2. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls +# 2. Defer generation of the StaticModules.inl file which is needed to create the AZ::Module derived class in monolithic +# builds until after all the targets are known and all the gems are enabled +ly_delayed_generate_static_modules_inl() + +# 3. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls # to provide applications with the filenames of gem modules to load # This must be done before ly_delayed_target_link_libraries() as that inserts BUILD_DEPENDENCIES as MANUALLY_ADDED_DEPENDENCIES # if the build dependency is a MODULE_LIBRARY. That would cause a false load dependency to be generated ly_delayed_generate_settings_registry() -# 3. link targets where the dependency was yet not declared, we need to have the declaration so we do different +# 4. link targets where the dependency was yet not declared, we need to have the declaration so we do different # linking logic depending on the type of target ly_delayed_target_link_libraries() -# 4. generate a registry file for unit testing for platforms that support unit testing +# 5. generate a registry file for unit testing for platforms that support unit testing if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_delayed_generate_unit_test_module_registry() endif() From f9fe2392f422655fa8803bb30296cf90f63ee331 Mon Sep 17 00:00:00 2001 From: Junbo Liang <68558268+junbo75@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:34:31 -0700 Subject: [PATCH 156/233] [SPEC-7257] [LYN-4472] [AWSMetrics] The background thread that monitors the metrics queue burns a lot of CPU (#1256) [LYN-4472] [AWSMetrics] The background thread that monitors the metrics queue burns a lot of CPU --- .../Code/Include/Private/MetricsManager.h | 19 ++---- .../AWSMetrics/Code/Source/MetricsManager.cpp | 65 ++++++++----------- .../Code/Tests/MetricsManagerTest.cpp | 5 +- 3 files changed, 38 insertions(+), 51 deletions(-) diff --git a/Gems/AWSMetrics/Code/Include/Private/MetricsManager.h b/Gems/AWSMetrics/Code/Include/Private/MetricsManager.h index 28c8c3d762..b8b6a8ccce 100644 --- a/Gems/AWSMetrics/Code/Include/Private/MetricsManager.h +++ b/Gems/AWSMetrics/Code/Include/Private/MetricsManager.h @@ -122,29 +122,22 @@ namespace AWSMetrics //! @return Outcome of the operation. AZ::Outcome SendMetricsToFile(AZStd::shared_ptr metricsQueue); - //! Check whether the consumer should flush the metrics queue. - //! @return whether the limit is hit. - bool ShouldSendMetrics(); - //! Push metrics events to the front of the queue for retry. //! @param metricsEventsForRetry Metrics events for retry. void PushMetricsForRetry(MetricsQueue& metricsEventsForRetry); void SubmitLocalMetricsAsync(); - //////////////////////////////////////////// - // These data are protected by m_metricsMutex. - AZStd::mutex m_metricsMutex; - AZStd::chrono::system_clock::time_point m_lastSendMetricsTime; - MetricsQueue m_metricsQueue; - //////////////////////////////////////////// + AZStd::mutex m_metricsMutex; //!< Mutex to protect the metrics queue + MetricsQueue m_metricsQueue; //!< Queue fo buffering the metrics events - AZStd::mutex m_metricsFileMutex; //!< Local metrics file is protected by m_metricsFileMutex + AZStd::mutex m_metricsFileMutex; //!< Mutex to protect the local metrics file AZStd::atomic m_sendMetricsId;//!< Request ID for sending metrics - AZStd::thread m_consumerThread; //!< Thread to monitor and consume the metrics queue - AZStd::atomic m_consumerTerminated; + AZStd::thread m_monitorThread; //!< Thread to monitor and consume the metrics queue + AZStd::atomic m_monitorTerminated; + AZStd::binary_semaphore m_waitEvent; // Client Configurations. AZStd::unique_ptr m_clientConfiguration; diff --git a/Gems/AWSMetrics/Code/Source/MetricsManager.cpp b/Gems/AWSMetrics/Code/Source/MetricsManager.cpp index 2f4f1fb5e5..d48ec1a041 100644 --- a/Gems/AWSMetrics/Code/Source/MetricsManager.cpp +++ b/Gems/AWSMetrics/Code/Source/MetricsManager.cpp @@ -29,7 +29,7 @@ namespace AWSMetrics MetricsManager::MetricsManager() : m_clientConfiguration(AZStd::make_unique()) , m_clientIdProvider(IdentityProvider::CreateIdentityProvider()) - , m_consumerTerminated(true) + , m_monitorTerminated(true) , m_sendMetricsId(0) { } @@ -53,31 +53,27 @@ namespace AWSMetrics void MetricsManager::StartMetrics() { - if (!m_consumerTerminated) + if (!m_monitorTerminated) { // The background thread has been started. return; } - - m_consumerTerminated = false; - - AZStd::lock_guard lock(m_metricsMutex); - m_lastSendMetricsTime = AZStd::chrono::system_clock::now(); + m_monitorTerminated = false; // Start a separate thread to monitor and consume the metrics queue. // Avoid using the job system since the worker is long-running over multiple frames - m_consumerThread = AZStd::thread(AZStd::bind(&MetricsManager::MonitorMetricsQueue, this)); + m_monitorThread = AZStd::thread(AZStd::bind(&MetricsManager::MonitorMetricsQueue, this)); } void MetricsManager::MonitorMetricsQueue() { - while (!m_consumerTerminated) + // Continue to loop until the monitor is terminated. + while (!m_monitorTerminated) { - if (ShouldSendMetrics()) - { - // Flush the metrics queue when the accumulated metrics size or time period hits the limit - FlushMetricsAsync(); - } + // The thread will wake up either when the metrics event queue is full (try_acquire_for call returns true), + // or the flush period limit is hit (try_acquire_for call returns false). + m_waitEvent.try_acquire_for(AZStd::chrono::seconds(m_clientConfiguration->GetQueueFlushPeriodInSeconds())); + FlushMetricsAsync(); } } @@ -114,6 +110,12 @@ namespace AWSMetrics AZStd::lock_guard lock(m_metricsMutex); m_metricsQueue.AddMetrics(metricsEvent); + if (m_metricsQueue.GetSizeInBytes() >= m_clientConfiguration->GetMaxQueueSizeInBytes()) + { + // Flush the metrics queue when the accumulated metrics size hits the limit + m_waitEvent.release(); + } + return true; } @@ -348,9 +350,6 @@ namespace AWSMetrics void MetricsManager::FlushMetricsAsync() { AZStd::lock_guard lock(m_metricsMutex); - - m_lastSendMetricsTime = AZStd::chrono::system_clock::now(); - if (m_metricsQueue.GetNumMetrics() == 0) { return; @@ -363,34 +362,20 @@ namespace AWSMetrics SendMetricsAsync(metricsToFlush); } - bool MetricsManager::ShouldSendMetrics() - { - AZStd::lock_guard lock(m_metricsMutex); - - auto secondsSinceLastFlush = AZStd::chrono::duration_cast(AZStd::chrono::system_clock::now() - m_lastSendMetricsTime); - if (secondsSinceLastFlush >= AZStd::chrono::seconds(m_clientConfiguration->GetQueueFlushPeriodInSeconds()) || - m_metricsQueue.GetSizeInBytes() >= m_clientConfiguration->GetMaxQueueSizeInBytes()) - { - return true; - } - - return false; - } - void MetricsManager::ShutdownMetrics() { - if (m_consumerTerminated) + if (m_monitorTerminated) { return; } - // Terminate the consumer thread - m_consumerTerminated = true; - FlushMetricsAsync(); + // Terminate the monitor thread + m_monitorTerminated = true; + m_waitEvent.release(); - if (m_consumerThread.joinable()) + if (m_monitorThread.joinable()) { - m_consumerThread.join(); + m_monitorThread.join(); } } @@ -449,6 +434,12 @@ namespace AWSMetrics { AZStd::lock_guard lock(m_metricsMutex); m_metricsQueue.AddMetrics(offlineRecords[index]); + + if (m_metricsQueue.GetSizeInBytes() >= m_clientConfiguration->GetMaxQueueSizeInBytes()) + { + // Flush the metrics queue when the accumulated metrics size hits the limit + m_waitEvent.release(); + } } // Remove the local metrics file after reading all its content. diff --git a/Gems/AWSMetrics/Code/Tests/MetricsManagerTest.cpp b/Gems/AWSMetrics/Code/Tests/MetricsManagerTest.cpp index 82385b8bdd..bd7ecb722e 100644 --- a/Gems/AWSMetrics/Code/Tests/MetricsManagerTest.cpp +++ b/Gems/AWSMetrics/Code/Tests/MetricsManagerTest.cpp @@ -355,6 +355,9 @@ namespace AWSMetrics TEST_F(MetricsManagerTest, FlushMetrics_NonEmptyQueue_Success) { + ResetClientConfig(true, (double)TestMetricsEventSizeInBytes * (MaxNumMetricsEvents + 1) / MbToBytes, + DefaultFlushPeriodInSeconds, 1); + for (int index = 0; index < MaxNumMetricsEvents; ++index) { AZStd::vector metricsAttributes; @@ -377,7 +380,7 @@ namespace AWSMetrics TEST_F(MetricsManagerTest, ResetOfflineRecordingStatus_ResubmitLocalMetrics_Success) { // Disable offline recording in the config file. - ResetClientConfig(false, 0.0, 0, 0); + ResetClientConfig(false, (double)TestMetricsEventSizeInBytes * 2 / MbToBytes, 0, 0); // Enable offline recording after initialize the metric manager. m_metricsManager->UpdateOfflineRecordingStatus(true); From 469b1a0858ac8bf4827d93f2473cc41067a2282c Mon Sep 17 00:00:00 2001 From: amzn-sj Date: Fri, 11 Jun 2021 16:30:46 -0700 Subject: [PATCH 157/233] Fix monolithic link error involving gem variants which are "aliased" as interface libraries with multiple gem target dependencies --- Code/LauncherUnified/launcher_generator.cmake | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Code/LauncherUnified/launcher_generator.cmake b/Code/LauncherUnified/launcher_generator.cmake index 36cd3c5899..15bbc0fe89 100644 --- a/Code/LauncherUnified/launcher_generator.cmake +++ b/Code/LauncherUnified/launcher_generator.cmake @@ -196,6 +196,16 @@ function(ly_delayed_generate_static_modules_inl) ly_get_gem_load_dependencies(all_game_gem_dependencies ${project_name}.GameLauncher) foreach(game_gem_dependency ${all_game_gem_dependencies}) + # Sometimes, a gem's Client variant may be an interface library + # which dependes on multiple gem targets. The interface libraries + # should be skipped; the real dependencies of the interface will be processed + if(TARGET ${game_gem_dependency}) + get_target_property(target_type ${game_gem_dependency} TYPE) + if(${target_type} STREQUAL "INTERFACE_LIBRARY") + continue() + endif() + endif() + # To match the convention on how gems targets vs gem modules are named, # we remove the ".Static" from the suffix # Replace "." with "_" From a86062bd91eb3184726319cec221304a8976b32a Mon Sep 17 00:00:00 2001 From: amzn-sj Date: Fri, 11 Jun 2021 16:42:09 -0700 Subject: [PATCH 158/233] Skip interface libraries when genrating StaticModules for game server as well --- Code/LauncherUnified/launcher_generator.cmake | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Code/LauncherUnified/launcher_generator.cmake b/Code/LauncherUnified/launcher_generator.cmake index 15bbc0fe89..5fa0f7a0e3 100644 --- a/Code/LauncherUnified/launcher_generator.cmake +++ b/Code/LauncherUnified/launcher_generator.cmake @@ -234,6 +234,14 @@ function(ly_delayed_generate_static_modules_inl) list(APPEND all_server_gem_dependencies ${server_gem_load_dependencies} ${server_gem_dependency}) endforeach() foreach(server_gem_dependency ${all_server_gem_dependencies}) + # Skip interface libraries + if(TARGET ${server_gem_dependency}) + get_target_property(target_type ${server_gem_dependency} TYPE) + if(${target_type} STREQUAL "INTERFACE_LIBRARY") + continue() + endif() + endif() + # Replace "." with "_" string(REPLACE "." "_" server_gem_dependency ${server_gem_dependency}) From f32590d241c12b01c1ff0ed0c2f2c248b0ca60b1 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Fri, 11 Jun 2021 16:43:53 -0700 Subject: [PATCH 159/233] LYN-4042 | No UI Indication that Level is Modified (#1277) * Restore the code that display the dirty marker on the Level container. Ensure the dirty marker is cleared on level save. * Moving call one level up to allow usage of the SaveTemplateToString function without clearing the dirty flag. --- .../PrefabEditorEntityOwnershipService.cpp | 17 ++++++++++++----- .../UI/Prefab/LevelRootUiHandler.cpp | 13 +++++++++++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp index fa7d5f6e9b..6e96511507 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp @@ -252,13 +252,20 @@ namespace AzToolsFramework } AZStd::string out; - if (m_loaderInterface->SaveTemplateToString(m_rootInstance->GetTemplateId(), out)) + + if (!m_loaderInterface->SaveTemplateToString(m_rootInstance->GetTemplateId(), out)) { - const size_t bytesToWrite = out.size(); - const size_t bytesWritten = stream.Write(bytesToWrite, out.data()); - return bytesWritten == bytesToWrite; + return false; } - return false; + + const size_t bytesToWrite = out.size(); + const size_t bytesWritten = stream.Write(bytesToWrite, out.data()); + if(bytesWritten != bytesToWrite) + { + return false; + } + m_prefabSystemComponent->SetTemplateDirtyFlag(templateId, false); + return true; } void PrefabEditorEntityOwnershipService::CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp index 7915403c0a..d6b7aebddc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp @@ -58,8 +58,17 @@ namespace AzToolsFramework if (!path.empty()) { - infoString = - QObject::tr("(%1)").arg(path.Filename().Native().data()); + QString saveFlag = ""; + auto dirtyOutcome = m_prefabPublicInterface->HasUnsavedChanges(path); + + if (dirtyOutcome.IsSuccess() && dirtyOutcome.GetValue() == true) + { + saveFlag = "*"; + } + + infoString = QObject::tr("(%1%2)") + .arg(path.Filename().Native().data()) + .arg(saveFlag); } return infoString; From 8089b6469b2d62fa8954a02f348ed9ac4a8a9192 Mon Sep 17 00:00:00 2001 From: Peng Date: Fri, 11 Jun 2021 17:14:31 -0700 Subject: [PATCH 160/233] ATOM-15774 [RHI][Vulkan] Fix issue with null image that is released prior to creating image view. JIRA: https://jira.agscollab.com/browse/ATOM-15774 --- Gems/Atom/RHI/Vulkan/Code/Source/RHI/ImageView.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ImageView.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ImageView.cpp index 605f61fc33..ec58b5c940 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ImageView.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ImageView.cpp @@ -55,7 +55,11 @@ namespace AZ const auto& image = static_cast(resourceBase); const RHI::ImageViewDescriptor& descriptor = GetDescriptor(); - AZ_Assert(image.GetNativeImage() != VK_NULL_HANDLE, "Image has not been initialized."); + // this can happen when image has been invalidated/released right before re-compiling the image + if (image.GetNativeImage() == VK_NULL_HANDLE) + { + return RHI::ResultCode::Fail; + } RHI::Format viewFormat = descriptor.m_overrideFormat; // If an image is not owner of native image, it is a swapchain image. From e4921c8d0aa0fe5ea3916e2e486dfdabc52255a7 Mon Sep 17 00:00:00 2001 From: sconel Date: Fri, 11 Jun 2021 17:45:46 -0700 Subject: [PATCH 161/233] Fix nested container entities not getting editor info removed during prefab processing --- .../PrefabEditorEntityOwnershipService.cpp | 17 ++- .../Prefab/Instance/Instance.cpp | 114 ++++++++++++------ .../Prefab/Instance/Instance.h | 17 ++- .../Prefab/Spawnable/EditorInfoRemover.cpp | 33 ++--- .../Prefab/Spawnable/EditorInfoRemover.h | 4 +- .../Prefab/PrefabUpdateWithPatchesTests.cpp | 2 +- 6 files changed, 108 insertions(+), 79 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp index fa7d5f6e9b..71781d29d5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp @@ -159,12 +159,23 @@ namespace AzToolsFramework void PrefabEditorEntityOwnershipService::GetNonPrefabEntities(EntityList& entities) { - m_rootInstance->GetEntities(entities, false); + m_rootInstance->GetEntities( + [&entities](const AZStd::unique_ptr& entity) + { + entities.emplace_back(entity.get()); + return true; + }); } bool PrefabEditorEntityOwnershipService::GetAllEntities(EntityList& entities) { - m_rootInstance->GetEntities(entities, true); + m_rootInstance->GetAllEntitiesInHierarchy( + [&entities](const AZStd::unique_ptr& entity) + { + entities.emplace_back(entity.get()); + return true; + }); + return true; } @@ -544,7 +555,7 @@ namespace AzToolsFramework return; } - m_rootInstance->GetNestedEntities([this](AZStd::unique_ptr& entity) + m_rootInstance->GetAllEntitiesInHierarchy([this](AZStd::unique_ptr& entity) { AZ_Assert(entity, "Invalid entity found in root instance while starting play in editor."); if (entity->GetState() == AZ::Entity::State::Active) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp index 83a76aeb01..e5179f4229 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp @@ -373,17 +373,25 @@ namespace AzToolsFramework } } - void Instance::GetConstNestedEntities(const AZStd::function& callback) + bool Instance::GetEntities_Impl(const AZStd::function&)>& callback) { - GetConstEntities(callback); - - for (const auto& [instanceAlias, instance] : m_nestedInstances) + for (auto& [entityAlias, entity] : m_entities) { - instance->GetConstNestedEntities(callback); + if (!entity) + { + continue; + } + + if (!callback(entity)) + { + return false; + } } + + return true; } - void Instance::GetConstEntities(const AZStd::function& callback) + bool Instance::GetConstEntities_Impl(const AZStd::function& callback) const { for (const auto& [entityAlias, entity] : m_entities) { @@ -394,65 +402,91 @@ namespace AzToolsFramework if (!callback(*entity)) { - break; + return false; } } + + return true; } - void Instance::GetNestedEntities(const AZStd::function&)>& callback) + bool Instance::GetAllEntitiesInHierarchy_Impl(const AZStd::function&)>& callback) { - GetEntities(callback); - - for (auto& [instanceAlias, instance] : m_nestedInstances) + if (HasContainerEntity()) { - instance->GetNestedEntities(callback); + if (!callback(m_containerEntity)) + { + return false; + } } - } - void Instance::GetNestedInstances(const AZStd::function&)>& callback) - { - for (auto& [instanceAlias, instance] : m_nestedInstances) + if (!GetEntities_Impl(callback)) { - callback(instance); + return false; } - } - void Instance::GetEntities(const AZStd::function&)>& callback) - { - for (auto& [entityAlias, entity] : m_entities) + for (auto& [instanceAlias, instance] : m_nestedInstances) { - if (!callback(entity)) + if (!instance->GetAllEntitiesInHierarchy_Impl(callback)) { - break; + return false; } } + + return true; } - void Instance::GetEntities(EntityList& entities, bool includeNestedEntities) + bool Instance::GetAllEntitiesInHierarchyConst_Impl(const AZStd::function& callback) const { - // Non-recursive traversal of instances - AZStd::vector instancesToTraverse = { this }; - while (!instancesToTraverse.empty()) + if (HasContainerEntity()) { - Instance* currentInstance = instancesToTraverse.back(); - instancesToTraverse.pop_back(); - if (includeNestedEntities) + if (!callback(*m_containerEntity)) { - instancesToTraverse.reserve(instancesToTraverse.size() + currentInstance->m_nestedInstances.size()); - for (const auto& instanceByAlias : currentInstance->m_nestedInstances) - { - instancesToTraverse.push_back(instanceByAlias.second.get()); - } + return false; } + } - // Size increases by 1 for each instance because we have to count the container entity also. - entities.reserve(entities.size() + currentInstance->m_entities.size() + 1); - entities.push_back(m_containerEntity.get()); - for (const auto& entityByAlias : currentInstance->m_entities) + if (!GetConstEntities_Impl(callback)) + { + return false; + } + + for (const auto& [instanceAlias, instance] : m_nestedInstances) + { + if (!instance->GetAllEntitiesInHierarchyConst_Impl(callback)) { - entities.push_back(entityByAlias.second.get()); + return false; } } + + return true; + } + + void Instance::GetEntities(const AZStd::function&)>& callback) + { + GetEntities_Impl(callback); + } + + void Instance::GetConstEntities(const AZStd::function& callback) const + { + GetConstEntities_Impl(callback); + } + + void Instance::GetAllEntitiesInHierarchy(const AZStd::function&)>& callback) + { + GetAllEntitiesInHierarchy_Impl(callback); + } + + void Instance::GetAllEntitiesInHierarchyConst(const AZStd::function& callback) const + { + GetAllEntitiesInHierarchyConst_Impl(callback); + } + + void Instance::GetNestedInstances(const AZStd::function&)>& callback) + { + for (auto& [instanceAlias, instance] : m_nestedInstances) + { + callback(instance); + } } EntityAliasOptionalReference Instance::GetEntityAlias(const AZ::EntityId& id) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h index 5f36ac10dc..9d3ae31796 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h @@ -121,10 +121,10 @@ namespace AzToolsFramework /** * Gets the entities in the Instance DOM. Can recursively trace all nested instances. */ - void GetConstNestedEntities(const AZStd::function& callback); - void GetConstEntities(const AZStd::function& callback); - void GetNestedEntities(const AZStd::function&)>& callback); void GetEntities(const AZStd::function&)>& callback); + void GetConstEntities(const AZStd::function& callback) const; + void GetAllEntitiesInHierarchy(const AZStd::function&)>& callback); + void GetAllEntitiesInHierarchyConst(const AZStd::function& callback) const; void GetNestedInstances(const AZStd::function&)>& callback); /** @@ -184,12 +184,6 @@ namespace AzToolsFramework static InstanceAlias GenerateInstanceAlias(); - protected: - /** - * Gets the entities owned by this instance - */ - void GetEntities(EntityList& entities, bool includeNestedEntities = false); - private: static constexpr const char s_aliasPathSeparator = '/'; @@ -197,6 +191,11 @@ namespace AzToolsFramework void RemoveEntities(const AZStd::function&)>& filter); + bool GetEntities_Impl(const AZStd::function&)>& callback); + bool GetConstEntities_Impl(const AZStd::function& callback) const; + bool GetAllEntitiesInHierarchy_Impl(const AZStd::function&)>& callback); + bool GetAllEntitiesInHierarchyConst_Impl(const AZStd::function& callback) const; + bool RegisterEntity(const AZ::EntityId& entityId, const EntityAlias& entityAlias); AZStd::unique_ptr DetachEntity(const EntityAlias& entityAlias); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.cpp index 6480ac37d7..cc04fe24c1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.cpp @@ -62,25 +62,16 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils } } - AZStd::vector EditorInfoRemover::GetEntitiesFromInstance(AZStd::unique_ptr& instance) + void EditorInfoRemover::GetEntitiesFromInstance( + AZStd::unique_ptr& instance, EntityList& hierarchyEntities) { - AZStd::vector result; - - instance->GetNestedEntities( - [&result](const AZStd::unique_ptr& entity) + instance->GetAllEntitiesInHierarchy( + [&hierarchyEntities](const AZStd::unique_ptr& entity) { - result.emplace_back(entity.get()); + hierarchyEntities.emplace_back(entity.get()); return true; } ); - - if (instance->HasContainerEntity()) - { - auto containerEntityReference = instance->GetContainerEntity(); - result.emplace_back(&containerEntityReference->get()); - } - - return result; } void EditorInfoRemover::SetEditorOnlyEntityHandlerFromCandidates(const EntityList& entities) @@ -543,7 +534,9 @@ exportComponent, prefabProcessorContext); } // grab all nested entities from the Instance as source entities. - EntityList sourceEntities = GetEntitiesFromInstance(instance); + EntityList sourceEntities; + GetEntitiesFromInstance(instance, sourceEntities); + EntityList exportEntities; // prepare for validation of component requirements. @@ -616,7 +609,7 @@ exportComponent, prefabProcessorContext); ); // replace entities of instance with exported ones. - instance->GetNestedEntities( + instance->GetAllEntitiesInHierarchy( [&exportEntitiesMap](AZStd::unique_ptr& entity) { auto entityId = entity->GetId(); @@ -625,14 +618,6 @@ exportComponent, prefabProcessorContext); } ); - if (instance->HasContainerEntity()) - { - if (auto found = exportEntitiesMap.find(instance->GetContainerEntityId()); found != exportEntitiesMap.end()) - { - instance->SetContainerEntity(*found->second); - } - } - // save the final result in the target Prefab DOM. PrefabDom filteredPrefab; if (!PrefabDomUtils::StoreInstanceInPrefabDom(*instance, filteredPrefab)) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h index 1e00485e42..5de5c516f8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h @@ -55,8 +55,8 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils protected: using EntityList = AZStd::vector; - static EntityList GetEntitiesFromInstance( - AZStd::unique_ptr& instance); + static void GetEntitiesFromInstance( + AZStd::unique_ptr& instance, EntityList& hierarchyEntities); static bool ReadComponentAttribute( AZ::Component* component, diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabUpdateWithPatchesTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabUpdateWithPatchesTests.cpp index aef3cad5f3..e3460c307f 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabUpdateWithPatchesTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabUpdateWithPatchesTests.cpp @@ -93,7 +93,7 @@ namespace UnitTest // Retrieve the entity pointer from the component application bus. AZ::Entity* wheelEntityUnderAxle = nullptr; - axleInstance->GetNestedEntities([&wheelEntityUnderAxle, wheelEntityIdUnderAxle](AZStd::unique_ptr& entity) + axleInstance->GetAllEntitiesInHierarchy([&wheelEntityUnderAxle, wheelEntityIdUnderAxle](AZStd::unique_ptr& entity) { if (entity->GetId() == wheelEntityIdUnderAxle) { From 633de3d28bfa8582eebe0d41aff086cfc8995207 Mon Sep 17 00:00:00 2001 From: scottr Date: Fri, 11 Jun 2021 17:58:14 -0700 Subject: [PATCH 162/233] [cpack/stabilization/2106] bootstrap installer theme support (required for changes requested by legal) --- .../Windows/Packaging/Bootstrapper.wxs | 4 + .../Packaging/BootstrapperTheme.wxl.in | 70 +++++++++++++++ .../Packaging/BootstrapperTheme.xml.in | 87 +++++++++++++++++++ .../Platform/Windows/PackagingPostBuild.cmake | 1 + .../Platform/Windows/Packaging_windows.cmake | 22 +++++ .../Windows/platform_windows_files.cmake | 2 + 6 files changed, 186 insertions(+) create mode 100644 cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in create mode 100644 cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in diff --git a/cmake/Platform/Windows/Packaging/Bootstrapper.wxs b/cmake/Platform/Windows/Packaging/Bootstrapper.wxs index 55e8a8cd95..fd8aa68b77 100644 --- a/cmake/Platform/Windows/Packaging/Bootstrapper.wxs +++ b/cmake/Platform/Windows/Packaging/Bootstrapper.wxs @@ -22,6 +22,8 @@ @@ -29,6 +31,8 @@ diff --git a/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in b/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in new file mode 100644 index 0000000000..fe125cfdfe --- /dev/null +++ b/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in @@ -0,0 +1,70 @@ + + + + + [WixBundleName] Setup + [WixBundleName] + Version [WixBundleVersion] + Are you sure you want to cancel? + + + Welcome + +Setup will install [WixBundleName] on your computer. Click install to continue, options to set the install directory or Close to exit. + + [WixBundleName] <a href="#">license terms</a>. + I &agree to the license terms and conditions + &Options + &Install + &Close + + + Setup Options + Install location: + &Browse + &OK + &Cancel + + + Modify Setup + &Repair + &Uninstall + &Close + + + Setup Progress + Processing: + Initializing... + &Cancel + + + Setup Successful + Installation Successfully Completed + Repair Successfully Completed + Uninstall Successfully Completed + &Launch + &Close + + + Setup Failed + Setup Failed + Uninstall Failed + Repair Failed + +One or more issues caused the setup to fail. Please fix the issues and then retry setup. For more information see the <a href="#">log file</a>. + + &Close + + + Setup Help + +/install | /repair | /uninstall | /layout [directory] - installs, repairs, uninstalls or creates a complete local copy of the bundle in directory. Install is the default. + +/passive | /quiet - displays minimal UI with no prompts or displays no UI and no prompts. By default UI and all prompts are displayed. + +/log log.txt - logs to a specific file. By default a log file is created in %TEMP%. + + &Close + diff --git a/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in b/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in new file mode 100644 index 0000000000..61b338cc13 --- /dev/null +++ b/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in @@ -0,0 +1,87 @@ + + + + #(loc.WindowTitle) + + Segoe UI + Segoe UI + Segoe UI + Segoe UI + + + + #(loc.Title) + + + + @WIX_THEME_INSTALL_LICENSE_ELEMENT@ + + #(loc.InstallAcceptCheckbox) + + + + + + + + #(loc.OptionsHeader) + + #(loc.OptionsLocationLabel) + + + + + + + + + + #(loc.ModifyHeader) + + + + + + + + + #(loc.ProgressHeader) + + #(loc.ProgressLabel) + #(loc.OverallProgressPackageText) + + + + + + + + #(loc.SuccessHeader) + #(loc.SuccessInstallHeader) + #(loc.SuccessRepairHeader) + #(loc.SuccessUninstallHeader) + + + + + + + + #(loc.FailureHeader) + #(loc.FailureInstallHeader) + #(loc.FailureUninstallHeader) + #(loc.FailureRepairHeader) + + #(loc.FailureHyperlinkLogText) + + + + + + + + #(loc.HelpHeader) + #(loc.HelpText) + + + diff --git a/cmake/Platform/Windows/PackagingPostBuild.cmake b/cmake/Platform/Windows/PackagingPostBuild.cmake index 89b3efb44b..1dcbedcba7 100644 --- a/cmake/Platform/Windows/PackagingPostBuild.cmake +++ b/cmake/Platform/Windows/PackagingPostBuild.cmake @@ -25,6 +25,7 @@ set(_ext_flags ) set(_addtional_defines + -dCPACK_BOOTSTRAP_THEME_FILE=${CPACK_BINARY_DIR}/BootstrapperTheme -dCPACK_BOOTSTRAP_UPGRADE_GUID=${CPACK_WIX_BOOTSTRAP_UPGRADE_GUID} -dCPACK_DOWNLOAD_SITE=${CPACK_DOWNLOAD_SITE} -dCPACK_LOCAL_INSTALLER_DIR=${_cpack_wix_out_dir} diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 2fd281ad51..aec0edeee2 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -92,6 +92,28 @@ set(CPACK_WIX_EXTENSIONS set(_embed_artifacts "yes") if(LY_INSTALLER_DOWNLOAD_URL) + + if(LY_INSTALLER_LICENSE_URL) + set(WIX_THEME_INSTALL_LICENSE_ELEMENT + "#(loc.InstallLicenseLinkText)" + ) + else() + set(WIX_THEME_INSTALL_LICENSE_ELEMENT + "" + ) + endif() + + configure_file( + "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/BootstrapperTheme.xml.in" + "${CPACK_BINARY_DIR}/BootstrapperTheme.xml" + @ONLY + ) + configure_file( + "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/BootstrapperTheme.wxl.in" + "${CPACK_BINARY_DIR}/BootstrapperTheme.wxl" + @ONLY + ) + set(_embed_artifacts "no") # the bootstrapper will at the very least need a different upgrade guid diff --git a/cmake/Platform/Windows/platform_windows_files.cmake b/cmake/Platform/Windows/platform_windows_files.cmake index 3ce53fbcea..b3cdc8a4ff 100644 --- a/cmake/Platform/Windows/platform_windows_files.cmake +++ b/cmake/Platform/Windows/platform_windows_files.cmake @@ -26,6 +26,8 @@ set(FILES Packaging_windows.cmake PackagingPostBuild.cmake Packaging/Bootstrapper.wxs + Packaging/BootstrapperTheme.wxl.in + Packaging/BootstrapperTheme.xml.in Packaging/Shortcuts.wxs Packaging/Template.wxs.in ) From b2bcd6a326d6b5519198d875d38ce5fd92a18a67 Mon Sep 17 00:00:00 2001 From: jromnoa Date: Fri, 11 Jun 2021 18:12:24 -0700 Subject: [PATCH 163/233] adds TEST_SCREENSHOTS boolean toggle to build_config.json & adds subdirectory searching for upload_to_s3.py script --- .../build/Platform/Windows/build_config.json | 3 +- scripts/build/tools/upload_to_s3.py | 67 ++++++++++++++----- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 71abf9021f..abf163a2e7 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -177,7 +177,8 @@ "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", "CTEST_OPTIONS": "-L \"(SUITE_smoke_REQUIRES_gpu|SUITE_main_REQUIRES_gpu)\" -T Test", "TEST_METRICS": "True", - "TEST_RESULTS": "True" + "TEST_RESULTS": "True", + "TEST_SCREENSHOTS": "True" } }, "asset_profile_vs2019": { diff --git a/scripts/build/tools/upload_to_s3.py b/scripts/build/tools/upload_to_s3.py index 5dfe5eb66e..132929d83e 100755 --- a/scripts/build/tools/upload_to_s3.py +++ b/scripts/build/tools/upload_to_s3.py @@ -17,6 +17,9 @@ python upload_to_s3.py --base_dir %WORKSPACE% --file_regex "(.*zip$|.*MD5$)" --b Use profile to upload all .zip and .MD5 files in %WORKSPACE% folder to bucket ly-packages-mainline: python upload_to_s3.py --base_dir %WORKSPACE% --profile profile --file_regex "(.*zip$|.*MD5$)" --bucket ly-packages-mainline +Another example usage for uploading all .png and .ppm files inside base_dir and only subdirectories within base_dir: +python upload_to_s3.py --base_dir %WORKSPACE%/path/to/files --file_regex "(.*png$|.*ppm$)" --bucket screenshot-test-bucket --search_subdirectories True --key_prefix Test + ''' @@ -34,6 +37,8 @@ def parse_args(): parser.add_option("--profile", dest="profile", default=None, help="The name of a profile to use. If not given, then the default profile is used.") parser.add_option("--bucket", dest="bucket", default=None, help="S3 bucket the files are uploaded to.") parser.add_option("--key_prefix", dest="key_prefix", default='', help="Object key prefix.") + parser.add_option("--search_subdirectories", dest="search_subdirectories", action='store_true', + help="Toggle for searching for files in subdirectories beneath base_dir, defaults to False") ''' ExtraArgs used to call s3.upload_file(), should be in json format. extra_args key must be one of: ACL, CacheControl, ContentDisposition, ContentEncoding, ContentLanguage, ContentType, Expires, GrantFullControl, GrantRead, GrantReadACP, GrantWriteACP, Metadata, RequestPayer, ServerSideEncryption, StorageClass, @@ -62,48 +67,74 @@ def get_client(service_name, profile_name): return client -def get_files_to_upload(base_dir, regex): +def get_files_to_upload(base_dir, regex, search_subdirectories): + """ + Uses a regex expression pattern to return a list of file paths for files to upload to the s3 bucket. + :param base_dir: path for the base directory, if using search_subdirectories=True ensure this is the parent. + :param regex: pattern to use for regex searching, ex. "(.*zip$|.*MD5$)" + :param search_subdirectories: boolean False for only getting files in base_dir, True to get all files in base_dir + and any subdirectory inside base_dir, defaults to False from the parse_args() function. + :return: a list of string file paths for files to upload to the s3 bucket matching the regex expression. + """ # Get all file names in base directory - files = [x for x in os.listdir(base_dir) if os.path.isfile(os.path.join(base_dir, x))] - # strip the surround quotes, if they exist + files = [os.path.join(base_dir, x) for x in os.listdir(base_dir) if os.path.isfile(os.path.join(base_dir, x))] + if search_subdirectories: # Get all file names in base directory and any subdirectories. + for subdirectory in os.walk(base_dir): + # Example output for subdirectory: + # ('C:\path\to\base_dir\', ['Subfolder1', 'Subfolder2'], ['file1', 'file2']) + subdirectory_file_path = subdirectory[0] + subdirectory_files = subdirectory[2] + subdirectory_file_paths = _build_file_paths(subdirectory_file_path, subdirectory_files) + files.extend(subdirectory_file_paths) + try: - regex = json.loads(regex) + regex = json.loads(regex) # strip the surround quotes, if they exist except: pass # Get all file names matching the regular expression, those file will be uploaded to S3 - files_to_upload = [x for x in files if re.match(regex, x)] - return files_to_upload + regex_files_to_upload = [x for x in files if re.match(regex, x)] + return regex_files_to_upload -def s3_upload_file(client, base_dir, file, bucket, key_prefix=None, extra_args=None, max_retry=1): - print(('Uploading file {} to bucket {}.'.format(file, bucket))) + +def s3_upload_file(client, file, bucket, key_prefix=None, extra_args=None, max_retry=1): key = file if key_prefix is None else '{}/{}'.format(key_prefix, file) for x in range(max_retry): try: - client.upload_file( - os.path.join(base_dir, file), bucket, key, - ExtraArgs=extra_args - ) - print('Upload succeeded') + client.upload_file(file, bucket, key, ExtraArgs=extra_args) return True except Exception as err: - print(('exception while uploading: {}'.format(err))) - print('Retrying upload...') - print('Upload failed') + print(('Upload failed: Exception while uploading: {}'.format(err))) return False +def _build_file_paths(path_to_files, files_in_path): + """ + Given a path containing files, returns a list of strings representing complete paths to each file. + :param path_to_files: path to the location storing the files to create string paths for + :param files_in_path: list of files that are inside the path_to_files path string + :return: list of fully parsed file path strings from path_to_files path. + """ + parsed_file_paths = [] + + for file_in_path in files_in_path: + complete_file_path = os.path.join(path_to_files, file_in_path) + parsed_file_paths.append(complete_file_path) + + return parsed_file_paths + + if __name__ == "__main__": options = parse_args() client = get_client('s3', options.profile) - files_to_upload = get_files_to_upload(options.base_dir, options.file_regex) + files_to_upload = get_files_to_upload(options.base_dir, options.file_regex, options.search_subdirectories) extra_args = json.loads(options.extra_args) if options.extra_args else None print(('Uploading {} files to bucket {}.'.format(len(files_to_upload), options.bucket))) failure = [] success = [] for file in files_to_upload: - if not s3_upload_file(client, options.base_dir, file, options.bucket, options.key_prefix, extra_args, 2): + if not s3_upload_file(client, file, options.bucket, options.key_prefix, extra_args, 2): failure.append(file) else: success.append(file) From 8459cbe5c2e1c75f47720594ffa8976ebf81b465 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Sat, 12 Jun 2021 01:18:24 -0700 Subject: [PATCH 164/233] Removed the DiffuseGlobalIllumination component from the editor Entity Component list, it was intended to be a Level Component only. Checked for a valid quality level in DiffuseGlobalIlluminationFeatureProcessor::SetQualityLevel. Initialized the quality level to Low in DiffuseGlobalIlluminationComponentConfig. --- .../DiffuseGlobalIlluminationFeatureProcessorInterface.h | 4 +++- .../DiffuseGlobalIlluminationFeatureProcessor.cpp | 6 ++++++ .../DiffuseGlobalIlluminationComponentConfig.h | 2 +- .../EditorDiffuseGlobalIlluminationComponent.cpp | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h index 88faac1728..ce50cea17f 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h @@ -23,7 +23,9 @@ namespace AZ { Low, Medium, - High + High, + + Count }; //! This class provides general features and configuration for the diffuse global illumination environment, diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp index 5040665456..1c28e18e0e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp @@ -43,6 +43,12 @@ namespace AZ void DiffuseGlobalIlluminationFeatureProcessor::SetQualityLevel(DiffuseGlobalIlluminationQualityLevel qualityLevel) { + if (qualityLevel >= DiffuseGlobalIlluminationQualityLevel::Count) + { + AZ_Assert(false, "SetQualityLevel called with invalid quality level [%d]", qualityLevel); + return; + } + m_qualityLevel = qualityLevel; UpdatePasses(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h index fb99a99e1a..8f1b216af5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h @@ -29,7 +29,7 @@ namespace AZ static void Reflect(ReflectContext* context); - DiffuseGlobalIlluminationQualityLevel m_qualityLevel; + DiffuseGlobalIlluminationQualityLevel m_qualityLevel = DiffuseGlobalIlluminationQualityLevel::Low; }; } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp index bdb5686e89..7df965d831 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp @@ -36,7 +36,7 @@ namespace AZ ->Attribute(Edit::Attributes::Category, "Atom") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.png") - ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC("Level", 0x9aeacc13), AZ_CRC("Game", 0x232b318c) })) + ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC("Level", 0x9aeacc13) })) ->Attribute(Edit::Attributes::AutoExpand, true) ->Attribute(Edit::Attributes::HelpPageURL, "https://") ; From 886601ad947142cfeff23c6cb810c1798aeb43dc Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Sat, 12 Jun 2021 03:20:31 -0700 Subject: [PATCH 165/233] Created a new ReflectionScreenSpaceCompositePass to set the maximum roughness mip in the pass Srg. Changed the ReflectionScreenSpaceBlurPass to auto-generate the mip chain, which will compute the appropriate number of mips. --- .../Passes/ReflectionScreenSpaceBlur.pass | 4 +- .../ReflectionScreenSpaceComposite.pass | 2 +- .../ReflectionScreenSpaceComposite.azsl | 10 ++-- .../Code/Source/CommonSystemComponent.cpp | 2 + .../ReflectionScreenSpaceBlurPass.h | 3 + .../ReflectionScreenSpaceCompositePass.cpp | 55 +++++++++++++++++++ .../ReflectionScreenSpaceCompositePass.h | 43 +++++++++++++++ .../Code/atom_feature_common_files.cmake | 2 + 8 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp create mode 100644 Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h diff --git a/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceBlur.pass b/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceBlur.pass index 1d20382408..e2fde2d4ef 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceBlur.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceBlur.pass @@ -24,9 +24,9 @@ }, "ImageDescriptor": { "Format": "R16G16B16A16_FLOAT", - "MipLevels": "8", "SharedQueueMask": "Graphics" - } + }, + "GenerateFullMipChain": true } ], "Connections": [ diff --git a/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceComposite.pass b/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceComposite.pass index 80c4e8987b..5443c32406 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceComposite.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceComposite.pass @@ -5,7 +5,7 @@ "ClassData": { "PassTemplate": { "Name": "ReflectionScreenSpaceCompositePassTemplate", - "PassClass": "FullScreenTriangle", + "PassClass": "ReflectionScreenSpaceCompositePass", "Slots": [ { "Name": "TraceInput", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.azsl index fa3885f180..c5c724e106 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.azsl @@ -37,6 +37,9 @@ ShaderResourceGroup PassSrg : SRG_PerPass AddressV = Clamp; AddressW = Clamp; }; + + // the max roughness mip level for sampling the previous frame image + uint m_maxMipLevel; } #include @@ -69,10 +72,6 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) float4 positionWS = mul(ViewSrg::m_viewProjectionInverseMatrix, projectedPos); positionWS /= positionWS.w; - //float4 positionVS = mul(ViewSrg::m_projectionMatrixInverse, projectedPos); - //positionVS /= positionVS.w; - //float4 positionWS = mul(ViewSrg::m_viewMatrixInverse, positionVS); - // compute ray from camera to surface position float3 cameraToPositionWS = normalize(positionWS.xyz - ViewSrg::m_worldPosition); @@ -103,8 +102,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // compute the roughness mip to use in the previous frame image // remap the roughness mip into a lower range to more closely match the material roughness values const float MaxRoughness = 0.5f; - const float MaxRoughnessMip = 7; - float mip = saturate(roughness / MaxRoughness) * MaxRoughnessMip; + float mip = saturate(roughness / MaxRoughness) * PassSrg::m_maxMipLevel; // sample reflection value from the roughness mip float4 reflectionColor = float4(PassSrg::m_previousFrame.SampleLevel(PassSrg::LinearSampler, tracePrevUV, mip).rgb, 1.0f); diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index af28624357..1866da63e5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -103,6 +103,7 @@ #include #include #include +#include #include #include @@ -283,6 +284,7 @@ namespace AZ // Add Reflection passes passSystem->AddPassCreator(Name("ReflectionScreenSpaceBlurPass"), &Render::ReflectionScreenSpaceBlurPass::Create); passSystem->AddPassCreator(Name("ReflectionScreenSpaceBlurChildPass"), &Render::ReflectionScreenSpaceBlurChildPass::Create); + passSystem->AddPassCreator(Name("ReflectionScreenSpaceCompositePass"), &Render::ReflectionScreenSpaceCompositePass::Create); passSystem->AddPassCreator(Name("ReflectionCopyFrameBufferPass"), &Render::ReflectionCopyFrameBufferPass::Create); // Add RayTracing pas diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h index 4a2ccce1d4..be8dc6d596 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h @@ -37,6 +37,9 @@ namespace AZ //! to store the previous frame image Data::Instance& GetFrameBufferImageAttachment() { return m_frameBufferImageAttachment; } + //! Returns the number of mip levels in the blur + uint32_t GetNumBlurMips() const { return m_numBlurMips; } + private: explicit ReflectionScreenSpaceBlurPass(const RPI::PassDescriptor& descriptor); diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp new file mode 100644 index 0000000000..fe2030ca7e --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp @@ -0,0 +1,55 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "ReflectionScreenSpaceCompositePass.h" +#include "ReflectionScreenSpaceBlurPass.h" +#include +#include + +namespace AZ +{ + namespace Render + { + RPI::Ptr ReflectionScreenSpaceCompositePass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew ReflectionScreenSpaceCompositePass(descriptor); + return AZStd::move(pass); + } + + ReflectionScreenSpaceCompositePass::ReflectionScreenSpaceCompositePass(const RPI::PassDescriptor& descriptor) + : RPI::FullscreenTrianglePass(descriptor) + { + } + + void ReflectionScreenSpaceCompositePass::CompileResources([[maybe_unused]] const RHI::FrameGraphCompileContext& context) + { + if (!m_shaderResourceGroup) + { + return; + } + + RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass")); + const AZStd::vector& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter); + if (!passes.empty()) + { + Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(passes.front()); + const uint32_t MaxNumRoughnessMips = 8; + uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1; + + auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel")); + m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel); + } + + FullscreenTrianglePass::CompileResources(context); + } + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h new file mode 100644 index 0000000000..110673541e --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + //! This pass composites the screenspace reflection trace onto the reflection buffer. + class ReflectionScreenSpaceCompositePass + : public RPI::FullscreenTrianglePass + { + AZ_RPI_PASS(ReflectionScreenSpaceCompositePass); + + public: + AZ_RTTI(Render::ReflectionScreenSpaceCompositePass, "{88739CC9-C3F1-413A-A527-9916C697D93A}", FullscreenTrianglePass); + AZ_CLASS_ALLOCATOR(Render::ReflectionScreenSpaceCompositePass, SystemAllocator, 0); + + //! Creates a new pass without a PassTemplate + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + private: + explicit ReflectionScreenSpaceCompositePass(const RPI::PassDescriptor& descriptor); + + // Pass Overrides... + void CompileResources(const RHI::FrameGraphCompileContext& context) override; + }; + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index a759de77fa..a656558abf 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -267,6 +267,8 @@ set(FILES Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurChildPass.cpp Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurChildPass.h + Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp + Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.h Source/ScreenSpace/DeferredFogSettings.cpp From 00062430a2788e21f61186f78d199b56aae5f316 Mon Sep 17 00:00:00 2001 From: Santora Date: Sat, 12 Jun 2021 11:52:46 -0700 Subject: [PATCH 166/233] Updated the usage of ShaderReloadDebugTracker to include the address of the object. This is necessary for debugging where multiple assets may be reloading at the same time. Also added a Printf function for generic messages at the current indent level. This is specifically in support of: ATOM-14613 Baseviewer MatertialHotReloadTest fails to change the color after turning blending on and off ATOM-15728 Shader Hot Reload Fails in Debug Build --- .../RPI.Public/Shader/ShaderReloadDebugTracker.h | 14 ++++++++++++++ .../Code/Source/RPI.Public/Material/Material.cpp | 9 +++++---- .../RPI/Code/Source/RPI.Public/Shader/Shader.cpp | 2 +- .../Source/RPI.Reflect/Material/MaterialAsset.cpp | 2 +- .../RPI.Reflect/Material/MaterialTypeAsset.cpp | 2 +- .../Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp | 4 ++-- 6 files changed, 24 insertions(+), 9 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h index dfda93dca3..8220d25eb8 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h @@ -57,6 +57,20 @@ namespace AZ } #endif } + + //! Prints a generic message at the appropriate indent level. + template + static void Printf([[maybe_unused]] const char* format, [[maybe_unused]] Args... args) + { +#ifdef AZ_ENABLE_SHADER_RELOAD_DEBUG_TRACKER + if (IsEnabled()) + { + const AZStd::string message = AZStd::string::format(format, args...); + + AZ_TracePrintf("ShaderReloadDebug", "%*s %s \n", s_indent, "", message.c_str()); + } +#endif + } //! Use this utility to call BeginSection(), and automatically call EndSection() when the object goes out of scope. class ScopedSection final diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp index 4a2718b9d5..eac1ee42e5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp @@ -97,6 +97,7 @@ namespace AZ ShaderReloadNotificationBus::MultiHandler::BusDisconnect(); for (auto& shaderItem : m_shaderCollection) { + ShaderReloadDebugTracker::Printf("(Material has ShaderAsset %p)", shaderItem.GetShaderAsset().Get()); ShaderReloadNotificationBus::MultiHandler::BusConnect(shaderItem.GetShaderAsset().GetId()); } @@ -226,7 +227,7 @@ namespace AZ // AssetBus overrides... void Material::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnAssetReloaded %s", this, asset.GetHint().c_str()); Data::Asset newMaterialAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; @@ -241,7 +242,7 @@ namespace AZ // MaterialReloadNotificationBus overrides... void Material::OnMaterialAssetReinitialized(const Data::Asset& materialAsset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnMaterialAssetReinitialized %s", materialAsset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnMaterialAssetReinitialized %s", this, materialAsset.GetHint().c_str()); OnAssetReloaded(materialAsset); } @@ -249,7 +250,7 @@ namespace AZ // ShaderReloadNotificationBus overrides... void Material::OnShaderReinitialized([[maybe_unused]] const Shader& shader) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnShaderReinitialized %s", shader.GetAsset().GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderReinitialized %s", this, shader.GetAsset().GetHint().c_str()); // Note that it might not be strictly necessary to reinitialize the entire material, we might be able to get away with // just bumping the m_currentChangeId or some other minor updates. But it's pretty hard to know what exactly needs to be // updated to correctly handle the reload, so it's safer to just reinitialize the whole material. @@ -269,7 +270,7 @@ namespace AZ void Material::OnShaderVariantReinitialized(const Shader& shader, const ShaderVariantId& /*shaderVariantId*/, ShaderVariantStableId shaderVariantStableId) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnShaderVariantReinitialized %s variant %u", shader.GetAsset().GetHint().c_str(), shaderVariantStableId.GetIndex()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderVariantReinitialized %s variant %u", this, shader.GetAsset().GetHint().c_str(), shaderVariantStableId.GetIndex()); // Note that it would be better to check the shaderVariantId to see if that variant is relevant to this particular material before reinitializing it. // There could be hundreds or even thousands of variants for a shader, but only one of those variants will be used by any given material. So we could diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp index 7194c524ae..147f24d4f8 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp @@ -132,7 +132,7 @@ namespace AZ // AssetBus overrides void Shader::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Shader::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Shader::OnAssetReloaded %s", this, asset.GetHint().c_str()); if (asset->GetId() == m_asset->GetId()) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index 7e9e2ddb10..2e56c30652 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -119,7 +119,7 @@ namespace AZ void MaterialAsset::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("MaterialAsset::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialAsset::OnAssetReloaded %s", this, asset.GetHint().c_str()); Data::Asset newMaterialTypeAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp index 20adb63cb8..f7d0a83ac9 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp @@ -130,7 +130,7 @@ namespace AZ void MaterialTypeAsset::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("MaterialTypeAsset::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialTypeAsset::OnAssetReloaded %s", this, asset.GetHint().c_str()); // The order of asset reloads is non-deterministic. If the MaterialTypeAsset reloads before these // dependency assets, this will make sure the MaterialTypeAsset gets the latest ones when they reload. diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp index cf655d43f7..75d4a47bc0 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp @@ -381,7 +381,7 @@ namespace AZ // AssetBus overrides... void ShaderAsset::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("ShaderAsset::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderAsset::OnAssetReloaded %s", this, asset.GetHint().c_str()); Data::Asset shaderVariantAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; AZ_Assert(shaderVariantAsset->GetStableId() == RootShaderVariantStableId, @@ -396,7 +396,7 @@ namespace AZ /// ShaderVariantFinderNotificationBus overrides void ShaderAsset::OnShaderVariantTreeAssetReady(Data::Asset shaderVariantTreeAsset, bool isError) { - ShaderReloadDebugTracker::ScopedSection reloadSection("ShaderAsset::OnShaderVariantTreeAssetReady %s", shaderVariantTreeAsset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderAsset::OnShaderVariantTreeAssetReady %s", this, shaderVariantTreeAsset.GetHint().c_str()); AZStd::unique_lock lock(m_variantTreeMutex); if (isError) From bb083fde3c3e58458a94e1ffed1315b835fb6746 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Sat, 12 Jun 2021 17:28:50 -0700 Subject: [PATCH 167/233] Added ReflectiveCubeMap usage flag if the shadow is rendering in an EnvironmentCubeMap pipeline --- .../DirectionalLightFeatureProcessor.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index c4f4bc54b3..08bc443586 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -1070,7 +1072,18 @@ namespace AZ segment.m_pipelineViewTag = viewTag; if (!segment.m_view || segment.m_view->GetName() != viewName) { - segment.m_view = RPI::View::CreateView(viewName, RPI::View::UsageShadow); + RPI::View::UsageFlags usageFlags = RPI::View::UsageShadow; + + // if the shadow is rendering in an EnvironmentCubeMapPass it also needs to be a ReflectiveCubeMap view, + // to filter out shadows from objects that are excluded from the cubemap + RPI::PassClassFilter passFilter; + AZStd::vector cubeMapPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); + if (!cubeMapPasses.empty()) + { + usageFlags |= RPI::View::UsageReflectiveCubeMap; + } + + segment.m_view = RPI::View::CreateView(viewName, usageFlags); } } } From e72cae47b42fc742506e25a975b0bbc67f1488bd Mon Sep 17 00:00:00 2001 From: moudgils Date: Sat, 12 Jun 2021 21:16:22 -0700 Subject: [PATCH 168/233] Buffer update fixes for metal --- .../Code/Source/RHI/AsyncUploadQueue.cpp | 42 +++++++++++++------ .../Code/Source/RHI/BufferPoolResolver.cpp | 7 +++- .../Code/Source/RHI/MemoryPageAllocator.cpp | 2 +- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp index 594a4931b9..e9cf06967a 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp @@ -85,16 +85,34 @@ namespace AZ uint64_t AsyncUploadQueue::QueueUpload(const RHI::BufferStreamRequest& uploadRequest) { - uint64_t queueValue = m_uploadFence.Increment(); - - const MemoryView& memoryView = static_cast(*uploadRequest.m_buffer).GetMemoryView(); - RHI::Ptr buffer = memoryView.GetMemory(); - + Buffer& destBuffer = static_cast(*uploadRequest.m_buffer); + const MemoryView& destMemoryView = destBuffer.GetMemoryView(); + MTLStorageMode mtlStorageMode = destBuffer.GetMemoryView().GetStorageMode(); + RHI::BufferPool& bufferPool = static_cast(*destBuffer.GetPool()); + /* + if(mtlStorageMode == MTLStorageModeShared || mtlStorageMode == GetCPUGPUMemoryMode()) + { + RHI::BufferMapRequest mapRequest; + mapRequest.m_buffer = uploadRequest.m_buffer; + mapRequest.m_byteCount = uploadRequest.m_byteCount; + mapRequest.m_byteOffset = uploadRequest.m_byteOffset; + RHI::BufferMapResponse mapResponse; + bufferPool.MapBuffer(mapRequest, mapResponse); + ::memcpy(mapResponse.m_data, uploadRequest.m_sourceData, uploadRequest.m_byteCount); + bufferPool.UnmapBuffer(*uploadRequest.m_buffer); + if (uploadRequest.m_fenceToSignal) + { + uploadRequest.m_fenceToSignal->SignalOnCpu(); + } + return m_uploadFence.GetPendingValue(); + } + */ Fence* fenceToSignal = nullptr; uint64_t fenceToSignalValue = 0; - size_t byteCount = uploadRequest.m_byteCount; - size_t byteOffset = memoryView.GetOffset() + uploadRequest.m_byteOffset; + size_t byteOffset = destMemoryView.GetOffset() + uploadRequest.m_byteOffset; + uint64_t queueValue = m_uploadFence.Increment(); + const uint8_t* sourceData = reinterpret_cast(uploadRequest.m_sourceData); if (uploadRequest.m_fenceToSignal) @@ -125,11 +143,11 @@ namespace AZ } id blitEncoder = [framePacket->m_mtlCommandBuffer blitCommandEncoder]; - [blitEncoder copyFromBuffer:framePacket->m_stagingResource - sourceOffset:0 - toBuffer:buffer->GetGpuAddress>() - destinationOffset:byteOffset + pendingByteOffset - size:bytesToCopy]; + [blitEncoder copyFromBuffer: framePacket->m_stagingResource + sourceOffset: 0 + toBuffer: destMemoryView.GetGpuAddress>() + destinationOffset: byteOffset + pendingByteOffset + size: bytesToCopy]; [blitEncoder endEncoding]; blitEncoder = nil; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp index 2ca8303dc9..ce5ff64280 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp @@ -40,7 +40,7 @@ namespace AZ buffer->m_pendingResolves++; uploadRequest.m_attachmentBuffer = buffer; - uploadRequest.m_byteOffset = request.m_byteOffset; + uploadRequest.m_byteOffset = request.m_byteOffset;;//buffer->GetMemoryView().GetOffset() + request.m_byteOffset; uploadRequest.m_stagingBuffer = stagingBuffer; uploadRequest.m_byteSize = request.m_byteCount; @@ -64,9 +64,12 @@ namespace AZ AZ_Assert(stagingBuffer, "Staging Buffer is null."); AZ_Assert(destBuffer, "Attachment Buffer is null."); + //Inform the GPU that the CPU has modified the staging buffer. + //Platform::SynchronizeBufferOnCPU(stagingBuffer->GetMemoryView().GetGpuAddress>(), stagingBuffer->GetMemoryView().GetOffset(), stagingBuffer->GetMemoryView().GetSize()); + RHI::CopyBufferDescriptor copyDescriptor; copyDescriptor.m_sourceBuffer = stagingBuffer; - copyDescriptor.m_sourceOffset = 0; + copyDescriptor.m_sourceOffset = 0;//stagingBuffer->GetMemoryView().GetOffset(); copyDescriptor.m_destinationBuffer = destBuffer; copyDescriptor.m_destinationOffset = static_cast(packet.m_byteOffset); copyDescriptor.m_size = static_cast(packet.m_byteSize); diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp index 60581aeb2c..dedfb8157e 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp @@ -44,7 +44,7 @@ namespace AZ if (memoryView.IsValid()) { heapMemoryUsage.m_residentInBytes += m_descriptor.m_pageSizeInBytes; - memoryView.SetName("BufferPage"); + //memoryView.SetName(AZStd::string::format("BufferPage_%s", AZ::Uuid::CreateRandom().ToString().c_str())); } else { From 4e5d690584094014b3527970a19cb30f8d330ca2 Mon Sep 17 00:00:00 2001 From: moudgils Date: Sun, 13 Jun 2021 22:05:21 -0700 Subject: [PATCH 169/233] Fixes to buffer updates --- Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp | 6 ++++-- Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp | 6 +++--- Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp index e9cf06967a..29e0b19b75 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp @@ -89,7 +89,9 @@ namespace AZ const MemoryView& destMemoryView = destBuffer.GetMemoryView(); MTLStorageMode mtlStorageMode = destBuffer.GetMemoryView().GetStorageMode(); RHI::BufferPool& bufferPool = static_cast(*destBuffer.GetPool()); - /* + + // No need to use staging buffers since it's host memory. + // We just map, copy and then unmap. if(mtlStorageMode == MTLStorageModeShared || mtlStorageMode == GetCPUGPUMemoryMode()) { RHI::BufferMapRequest mapRequest; @@ -106,7 +108,7 @@ namespace AZ } return m_uploadFence.GetPendingValue(); } - */ + Fence* fenceToSignal = nullptr; uint64_t fenceToSignalValue = 0; size_t byteCount = uploadRequest.m_byteCount; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp index ce5ff64280..18cd51df3f 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp @@ -40,7 +40,7 @@ namespace AZ buffer->m_pendingResolves++; uploadRequest.m_attachmentBuffer = buffer; - uploadRequest.m_byteOffset = request.m_byteOffset;;//buffer->GetMemoryView().GetOffset() + request.m_byteOffset; + uploadRequest.m_byteOffset = buffer->GetMemoryView().GetOffset() + request.m_byteOffset; uploadRequest.m_stagingBuffer = stagingBuffer; uploadRequest.m_byteSize = request.m_byteCount; @@ -65,11 +65,11 @@ namespace AZ AZ_Assert(destBuffer, "Attachment Buffer is null."); //Inform the GPU that the CPU has modified the staging buffer. - //Platform::SynchronizeBufferOnCPU(stagingBuffer->GetMemoryView().GetGpuAddress>(), stagingBuffer->GetMemoryView().GetOffset(), stagingBuffer->GetMemoryView().GetSize()); + Platform::SynchronizeBufferOnCPU(stagingBuffer->GetMemoryView().GetGpuAddress>(), stagingBuffer->GetMemoryView().GetOffset(), stagingBuffer->GetMemoryView().GetSize()); RHI::CopyBufferDescriptor copyDescriptor; copyDescriptor.m_sourceBuffer = stagingBuffer; - copyDescriptor.m_sourceOffset = 0;//stagingBuffer->GetMemoryView().GetOffset(); + copyDescriptor.m_sourceOffset = stagingBuffer->GetMemoryView().GetOffset(); copyDescriptor.m_destinationBuffer = destBuffer; copyDescriptor.m_destinationOffset = static_cast(packet.m_byteOffset); copyDescriptor.m_size = static_cast(packet.m_byteSize); diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp index dedfb8157e..88aa4b4ed1 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp @@ -44,7 +44,7 @@ namespace AZ if (memoryView.IsValid()) { heapMemoryUsage.m_residentInBytes += m_descriptor.m_pageSizeInBytes; - //memoryView.SetName(AZStd::string::format("BufferPage_%s", AZ::Uuid::CreateRandom().ToString().c_str())); + memoryView.SetName(AZStd::string::format("BufferPage_%s", AZ::Uuid::CreateRandom().ToString().c_str())); } else { From f8f282998b8e3b26483dcbf69358fb1473738bd4 Mon Sep 17 00:00:00 2001 From: moudgils Date: Sun, 13 Jun 2021 23:36:56 -0700 Subject: [PATCH 170/233] Added support to call not call UseResources on samee resource multiple times. --- .../Metal/Code/Source/RHI/ArgumentBuffer.cpp | 25 +++++++++---------- .../Metal/Code/Source/RHI/ArgumentBuffer.h | 13 +++------- .../Code/Source/RHI/BufferPoolResolver.cpp | 3 +-- .../Code/Source/RHI/BufferPoolResolver.h | 1 - 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp index 49ff4146fc..c433a6f9cf 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp @@ -400,15 +400,13 @@ namespace AZ id mtlconstantBufferResource = m_constantBuffer.GetGpuAddress>(); if(RHI::CheckBitsAny(srgResourcesVisInfo.m_constantDataStageMask, RHI::ShaderStageMask::Compute)) { - uint16_t arrayIndex = resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArrayLen++; - resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArray[arrayIndex] = mtlconstantBufferResource; + resourcesToMakeResidentCompute[MTLResourceUsageRead].emplace(mtlconstantBufferResource); } else { MTLRenderStages mtlRenderStages = GetRenderStages(srgResourcesVisInfo.m_constantDataStageMask); - AZStd::pair key = AZStd::make_pair(MTLResourceUsageRead, mtlRenderStages); - uint16_t arrayIndex = resourcesToMakeResidentGraphics[key].m_resourceArrayLen++; - resourcesToMakeResidentGraphics[key].m_resourceArray[arrayIndex] = mtlconstantBufferResource; + AZStd::pair key = AZStd::make_pair(MTLResourceUsageRead, mtlRenderStages); + resourcesToMakeResidentGraphics[key].emplace(mtlconstantBufferResource); } } } @@ -440,16 +438,18 @@ namespace AZ //Call UseResource on all resources for Compute stage for (const auto& key : resourcesToMakeResidentCompute) { - [static_cast>(commandEncoder) useResources: key.second.m_resourceArray.data() - count: key.second.m_resourceArrayLen + AZStd::vector> resourcesToProcessVec(key.second.begin(), key.second.end()); + [static_cast>(commandEncoder) useResources: &resourcesToProcessVec[0] + count: resourcesToProcessVec.size() usage: key.first]; } //Call UseResource on all resources for Vertex and Fragment stages for (const auto& key : resourcesToMakeResidentGraphics) { - [static_cast>(commandEncoder) useResources: key.second.m_resourceArray.data() - count: key.second.m_resourceArrayLen + AZStd::vector> resourcesToProcessVec(key.second.begin(), key.second.end()); + [static_cast>(commandEncoder) useResources: &resourcesToProcessVec[0] + count: resourcesToProcessVec.size() usage: key.first.first stages: key.first.second]; } @@ -480,9 +480,9 @@ namespace AZ AZ_Assert(false, "Undefined Resource type"); } } - uint16_t arrayIndex = resourcesToMakeResidentMap[resourceUsage].m_resourceArrayLen++; + id mtlResourceToBind = resourceBindingData.m_resourcPtr->GetGpuAddress>(); - resourcesToMakeResidentMap[resourceUsage].m_resourceArray[arrayIndex] = mtlResourceToBind; + resourcesToMakeResidentMap[resourceUsage].emplace(mtlResourceToBind); } } @@ -516,9 +516,8 @@ namespace AZ } AZStd::pair key = AZStd::make_pair(resourceUsage, mtlRenderStages); - uint16_t arrayIndex = resourcesToMakeResidentMap[key].m_resourceArrayLen++; id mtlResourceToBind = resourceBindingData.m_resourcPtr->GetGpuAddress>(); - resourcesToMakeResidentMap[key].m_resourceArray[arrayIndex] = mtlResourceToBind; + resourcesToMakeResidentMap[key].emplace(mtlResourceToBind); } } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h index c4cfd17390..29d7d5e239 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h @@ -120,15 +120,10 @@ namespace AZ ResourceBindingsMap m_resourceBindings; static const int MaxEntriesInArgTable = 31; - struct MetalResourceArray - { - AZStd::array, MaxEntriesInArgTable> m_resourceArray; - uint16_t m_resourceArrayLen = 0; - }; - //Map to cache all the resources based on the usage as we can batch all the resources for a given usage - using ComputeResourcesToMakeResidentMap = AZStd::unordered_map; - //Map to cache all the resources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage - using GraphicsResourcesToMakeResidentMap = AZStd::unordered_map, MetalResourceArray>; + //Map to cache all the resources based on the usage as we can batch all the resources for a given usage. + using ComputeResourcesToMakeResidentMap = AZStd::unordered_map>>; + //Map to cache all the resources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage. + using GraphicsResourcesToMakeResidentMap = AZStd::unordered_map, AZStd::unordered_set>>; void CollectResourcesForCompute(id encoder, const ResourceBindingsSet& resourceBindingData, diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp index 18cd51df3f..b986b8ea75 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp @@ -42,7 +42,6 @@ namespace AZ uploadRequest.m_attachmentBuffer = buffer; uploadRequest.m_byteOffset = buffer->GetMemoryView().GetOffset() + request.m_byteOffset; uploadRequest.m_stagingBuffer = stagingBuffer; - uploadRequest.m_byteSize = request.m_byteCount; return stagingBuffer->GetMemoryView().GetCpuAddress(); } @@ -72,7 +71,7 @@ namespace AZ copyDescriptor.m_sourceOffset = stagingBuffer->GetMemoryView().GetOffset(); copyDescriptor.m_destinationBuffer = destBuffer; copyDescriptor.m_destinationOffset = static_cast(packet.m_byteOffset); - copyDescriptor.m_size = static_cast(packet.m_byteSize); + copyDescriptor.m_size = stagingBuffer->GetMemoryView().GetSize(); commandList.Submit(RHI::CopyItem(copyDescriptor)); device.QueueForRelease(stagingBuffer->GetMemoryView()); diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.h index c62d9494db..3e60bb31fa 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.h @@ -54,7 +54,6 @@ namespace AZ Buffer* m_attachmentBuffer = nullptr; RHI::Ptr m_stagingBuffer; size_t m_byteOffset = 0; - size_t m_byteSize = 0; }; AZStd::mutex m_uploadPacketsLock; From 59f3813b104f582b51872356d37460f2e6bab274 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Mon, 14 Jun 2021 08:45:00 +0200 Subject: [PATCH 171/233] [LYN-3481] EMotionFX crashes when reloading an Actor (#1184) (#1217) * [LYN-3481] Added new actor instance request and notification buses * [LYN-3481] Actor instance notifies bus about it being created or destroyed * [LYN-3481] Selection lists are now automatically removing destroyed actor instances * [LYN-3481] Morph targets window plugin reinitializing when used actor instance got destroyed * [LYN-3481] Removing the OnDeleteActorInstance() from the emfx event handler/manager and porting the recorder to the new actor instance bus * [LYN-3481] Removed the create actor instance calls from the event handler/manager * [LYN-3481] Fixing automated tests --- .../CommandSystem/Source/SelectionList.cpp | 7 +++ .../CommandSystem/Source/SelectionList.h | 7 ++- .../Code/EMotionFX/Source/ActorInstance.cpp | 13 ++--- .../Code/EMotionFX/Source/ActorInstanceBus.h | 54 +++++++++++++++++++ .../Code/EMotionFX/Source/EventHandler.h | 12 ----- .../Code/EMotionFX/Source/EventManager.cpp | 21 -------- .../Code/EMotionFX/Source/EventManager.h | 10 ---- .../Code/EMotionFX/Source/Recorder.cpp | 10 ++-- .../Code/EMotionFX/Source/Recorder.h | 8 +-- .../MorphTargetsWindowPlugin.cpp | 37 +++++++------ .../MorphTargetsWindowPlugin.h | 6 +++ .../Code/EMotionFX/emotionfx_files.cmake | 1 + 12 files changed, 107 insertions(+), 79 deletions(-) create mode 100644 Gems/EMotionFX/Code/EMotionFX/Source/ActorInstanceBus.h diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/SelectionList.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/SelectionList.cpp index a9f8cc063c..9beeebef86 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/SelectionList.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/SelectionList.cpp @@ -20,10 +20,12 @@ namespace CommandSystem SelectionList::SelectionList() { EMotionFX::ActorNotificationBus::Handler::BusConnect(); + EMotionFX::ActorInstanceNotificationBus::Handler::BusConnect(); } SelectionList::~SelectionList() { + EMotionFX::ActorInstanceNotificationBus::Handler::BusDisconnect(); EMotionFX::ActorNotificationBus::Handler::BusDisconnect(); } @@ -378,4 +380,9 @@ namespace CommandSystem RemoveActor(actor); } + + void SelectionList::OnActorInstanceDestroyed(EMotionFX::ActorInstance* actorInstance) + { + RemoveActorInstance(actorInstance); + } } // namespace CommandSystem diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/SelectionList.h b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/SelectionList.h index cd2eb3af5d..c85f8e601b 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/SelectionList.h +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/SelectionList.h @@ -15,6 +15,7 @@ #include "CommandSystemConfig.h" #include #include +#include #include #include #include @@ -27,7 +28,8 @@ namespace CommandSystem * specific time stamp in a scene. */ class COMMANDSYSTEM_API SelectionList - : EMotionFX::ActorNotificationBus::Handler + : private EMotionFX::ActorNotificationBus::Handler + , private EMotionFX::ActorInstanceNotificationBus::Handler { MCORE_MEMORYOBJECTCATEGORY(SelectionList, MCore::MCORE_DEFAULT_ALIGNMENT, MEMCATEGORY_COMMANDSYSTEM); @@ -400,6 +402,9 @@ namespace CommandSystem // ActorNotificationBus overrides void OnActorDestroyed(EMotionFX::Actor* actor) override; + // ActorInstanceNotificationBus overrides + void OnActorInstanceDestroyed(EMotionFX::ActorInstance* actorInstance) override; + AZStd::vector mSelectedNodes; /**< Array of selected nodes. */ AZStd::vector mSelectedActors; /**< The selected actors. */ AZStd::vector mSelectedActorInstances; /**< Array of selected actor instances. */ diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp index ec1b00bda4..8175d15496 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp @@ -34,6 +34,7 @@ #include "NodeGroup.h" #include "Recorder.h" #include "TransformData.h" +#include #include #include @@ -153,20 +154,14 @@ namespace EMotionFX // register it GetActorManager().RegisterActorInstance(this); - // automatically register the actor instance - GetEventManager().OnCreateActorInstance(this); - GetActorManager().GetScheduler()->RecursiveInsertActorInstance(this); + + ActorInstanceNotificationBus::Broadcast(&ActorInstanceNotificationBus::Events::OnActorInstanceCreated, this); } - // the destructor ActorInstance::~ActorInstance() { - // trigger the OnDeleteActorInstance event - GetEventManager().OnDeleteActorInstance(this); - - // remove it from the recording - GetRecorder().RemoveActorInstanceFromRecording(this); + ActorInstanceNotificationBus::Broadcast(&ActorInstanceNotificationBus::Events::OnActorInstanceDestroyed, this); // get rid of the motion system if (mMotionSystem) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstanceBus.h b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstanceBus.h new file mode 100644 index 0000000000..15b25ce0d3 --- /dev/null +++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstanceBus.h @@ -0,0 +1,54 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace EMotionFX +{ + class ActorInstance; + + /** + * EMotion FX Actor Instance Request Bus + * Used for making requests to actor instances. + */ + class ActorInstanceRequests + : public AZ::EBusTraits + { + public: + }; + + using ActorInstanceRequestBus = AZ::EBus; + + /** + * EMotion FX Actor Instance Notification Bus + * Used for monitoring events from actor instances. + */ + class ActorInstanceNotifications + : public AZ::EBusTraits + { + public: + // Enable multi-threaded access by locking primitive using a mutex when connecting handlers to the EBus or executing events. + using MutexType = AZStd::recursive_mutex; + + virtual void OnActorInstanceCreated([[maybe_unused]] ActorInstance* actorInstance) {} + + /** + * Called when any of the actor instances gets destructed. + * @param actorInstance The actorInstance that gets destructed. + */ + virtual void OnActorInstanceDestroyed([[maybe_unused]] ActorInstance* actorInstance) {} + }; + + using ActorInstanceNotificationBus = AZ::EBus; +} // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/EventHandler.h b/Gems/EMotionFX/Code/EMotionFX/Source/EventHandler.h index 1e6fb86156..2c8ae9d868 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/EventHandler.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/EventHandler.h @@ -51,7 +51,6 @@ namespace EMotionFX EVENT_TYPE_MOTION_INSTANCE_LAST_EVENT = EVENT_TYPE_ON_QUEUE_MOTION_INSTANCE, EVENT_TYPE_ON_DELETE_ACTOR, - EVENT_TYPE_ON_DELETE_ACTOR_INSTANCE, EVENT_TYPE_ON_SIMULATE_PHYSICS, EVENT_TYPE_ON_CUSTOM_EVENT, EVENT_TYPE_ON_DRAW_LINE, @@ -64,7 +63,6 @@ namespace EMotionFX EVENT_TYPE_ON_CREATE_MOTION_INSTANCE, EVENT_TYPE_ON_CREATE_MOTION_SYSTEM, EVENT_TYPE_ON_CREATE_ACTOR, - EVENT_TYPE_ON_CREATE_ACTOR_INSTANCE, EVENT_TYPE_ON_POST_CREATE_ACTOR, EVENT_TYPE_ON_DELETE_ANIM_GRAPH, EVENT_TYPE_ON_DELETE_ANIM_GRAPH_INSTANCE, @@ -298,15 +296,6 @@ namespace EMotionFX */ virtual void OnDeleteActor(Actor* actor) { MCORE_UNUSED(actor); } - /** - * The event that gets triggered once an ActorInstance object is being deleted. - * You could for example use this event to delete any allocations you have done inside the - * custom user data object linked with the ActorInstance object. - * You can get and set this data object with the ActorInstance::GetCustomData() and ActorInstance::SetCustomData(...) methods. - * @param actorInstance The actorInstance that is being deleted. - */ - virtual void OnDeleteActorInstance(ActorInstance* actorInstance) { MCORE_UNUSED(actorInstance); } - virtual void OnSimulatePhysics(float timeDelta) { MCORE_UNUSED(timeDelta); } virtual void OnCustomEvent(uint32 eventType, void* data) { MCORE_UNUSED(eventType); MCORE_UNUSED(data); } @@ -321,7 +310,6 @@ namespace EMotionFX virtual void OnCreateMotionInstance(MotionInstance* motionInstance) { MCORE_UNUSED(motionInstance); } virtual void OnCreateMotionSystem(MotionSystem* motionSystem) { MCORE_UNUSED(motionSystem); } virtual void OnCreateActor(Actor* actor) { MCORE_UNUSED(actor); } - virtual void OnCreateActorInstance(ActorInstance* actorInstance) { MCORE_UNUSED(actorInstance); } virtual void OnPostCreateActor(Actor* actor) { MCORE_UNUSED(actor); } // delete callbacks diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/EventManager.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/EventManager.cpp index bc9843c787..10c64b9433 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/EventManager.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/EventManager.cpp @@ -305,16 +305,6 @@ namespace EMotionFX } - void EventManager::OnDeleteActorInstance(ActorInstance* actorInstance) - { - const EventHandlerVector& eventHandlers = m_eventHandlersByEventType[EVENT_TYPE_ON_DELETE_ACTOR_INSTANCE]; - for (EventHandler* eventHandler : eventHandlers) - { - eventHandler->OnDeleteActorInstance(actorInstance); - } - } - - // draw a debug triangle void EventManager::OnDrawTriangle(const AZ::Vector3& posA, const AZ::Vector3& posB, const AZ::Vector3& posC, const AZ::Vector3& normalA, const AZ::Vector3& normalB, const AZ::Vector3& normalC, uint32 color) { @@ -670,17 +660,6 @@ namespace EMotionFX } - // create an actor instance - void EventManager::OnCreateActorInstance(ActorInstance* actorInstance) - { - const EventHandlerVector& eventHandlers = m_eventHandlersByEventType[EVENT_TYPE_ON_CREATE_ACTOR_INSTANCE]; - for (EventHandler* eventHandler : eventHandlers) - { - eventHandler->OnCreateActorInstance(actorInstance); - } - } - - // on post create actor void EventManager::OnPostCreateActor(Actor* actor) { diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/EventManager.h b/Gems/EMotionFX/Code/EMotionFX/Source/EventManager.h index 81767d5a66..f12d1de8cb 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/EventManager.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/EventManager.h @@ -286,15 +286,6 @@ namespace EMotionFX */ void OnDeleteActor(Actor* actor); - /** - * The event that gets triggered once an ActorInstance object is being deleted. - * You could for example use this event to delete any allocations you have done inside the - * custom user data object linked with the ActorInstance object. - * You can get and set this data object with the ActorInstance::GetCustomData() and ActorInstance::SetCustomData(...) methods. - * @param actorInstance The actorInstance that is being deleted. - */ - void OnDeleteActorInstance(ActorInstance* actorInstance); - void OnSimulatePhysics(float timeDelta); void OnCustomEvent(uint32 eventType, void* data); void OnDrawTriangle(const AZ::Vector3& posA, const AZ::Vector3& posB, const AZ::Vector3& posC, const AZ::Vector3& normalA, const AZ::Vector3& normalB, const AZ::Vector3& normalC, uint32 color); @@ -343,7 +334,6 @@ namespace EMotionFX void OnCreateMotionInstance(MotionInstance* motionInstance); void OnCreateMotionSystem(MotionSystem* motionSystem); void OnCreateActor(Actor* actor); - void OnCreateActorInstance(ActorInstance* actorInstance); void OnPostCreateActor(Actor* actor); // delete callbacks diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp index d12970bc04..8670851334 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp @@ -106,16 +106,12 @@ namespace EMotionFX mCurrentPlayTime = 0.0f; mObjects.SetMemoryCategory(EMFX_MEMCATEGORY_RECORDER); - - GetEMotionFX().GetEventManager()->AddEventHandler(this); + EMotionFX::ActorInstanceNotificationBus::Handler::BusConnect(); } Recorder::~Recorder() { - if (EventManager* eventManager = GetEMotionFX().GetEventManager()) - { - eventManager->RemoveEventHandler(this); - } + EMotionFX::ActorInstanceNotificationBus::Handler::BusDisconnect(); Clear(); } @@ -1448,7 +1444,7 @@ namespace EMotionFX Unlock(); } - void Recorder::OnDeleteActorInstance(ActorInstance* actorInstance) + void Recorder::OnActorInstanceDestroyed(EMotionFX::ActorInstance* actorInstance) { // Actor instances created by actor components do not use the command system and don't call a ClearRecorder command. // Thus, these actor instances will have to be removed from the recorder to avoid dangling data. diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.h b/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.h index 157ef2c88b..33ab3ce35d 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -47,7 +48,7 @@ namespace EMotionFX class EMFX_API Recorder : public BaseObject - , public EventHandler + , private EMotionFX::ActorInstanceNotificationBus::Handler { public: AZ_CLASS_ALLOCATOR_DECL @@ -319,9 +320,8 @@ namespace EMotionFX void RemoveActorInstanceFromRecording(ActorInstance* actorInstance); void RemoveAnimGraphFromRecording(AnimGraph* animGraph); - // EventHandler overrides - const AZStd::vector GetHandledEventTypes() const override { return {EMotionFX::EVENT_TYPE_ON_DELETE_ACTOR_INSTANCE}; } - void OnDeleteActorInstance(ActorInstance* actorInstance) override; + // ActorInstanceNotificationBus overrides + void OnActorInstanceDestroyed(EMotionFX::ActorInstance* actorInstance) override; void SampleAndApplyTransforms(float timeInSeconds, ActorInstance* actorInstance) const; void SampleAndApplyMainTransform(float timeInSeconds, ActorInstance* actorInstance) const; diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetsWindowPlugin.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetsWindowPlugin.cpp index 80bbb24928..a0b018d999 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetsWindowPlugin.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetsWindowPlugin.cpp @@ -17,25 +17,26 @@ #include "../../../../EMStudioSDK/Source/EMStudioCore.h" #include #include +#include #include #include "../../../../EMStudioSDK/Source/EMStudioManager.h" - namespace EMStudio { - // constructor MorphTargetsWindowPlugin::MorphTargetsWindowPlugin() : EMStudio::DockWidgetPlugin() { - mDialogStack = nullptr; - mCurrentActorInstance = nullptr; + mDialogStack = nullptr; + mCurrentActorInstance = nullptr; mStaticTextWidget = nullptr; - } + EMotionFX::ActorInstanceNotificationBus::Handler::BusConnect(); + } - // destructor MorphTargetsWindowPlugin::~MorphTargetsWindowPlugin() { + EMotionFX::ActorInstanceNotificationBus::Handler::BusDisconnect(); + // unregister the command callbacks and get rid of the memory for (auto callback : m_callbacks) { @@ -110,14 +111,16 @@ namespace EMStudio mMorphTargetGroups.clear(); } - // reinit the morph target dialog, e.g. if selection changes void MorphTargetsWindowPlugin::ReInit(bool forceReInit) { - // get the selected actorinstance - const CommandSystem::SelectionList& selection = GetCommandManager()->GetCurrentSelection(); - EMotionFX::ActorInstance* actorInstance = selection.GetSingleActorInstance(); + const CommandSystem::SelectionList& selection = GetCommandManager()->GetCurrentSelection(); + EMotionFX::ActorInstance* actorInstance = selection.GetSingleActorInstance(); + ReInit(actorInstance, forceReInit); + } + void MorphTargetsWindowPlugin::ReInit(EMotionFX::ActorInstance* actorInstance, bool forceReInit) + { // show hint if no/multiple actor instances is/are selected if (actorInstance == nullptr) { @@ -135,10 +138,7 @@ namespace EMStudio return; } - // get our selected actor instance and the corresponding actor - EMotionFX::Actor* actor = actorInstance->GetActor(); - - // only reinit the morph targets if actorinstance changed + // only reinit the morph targets if actor instance changed if (mCurrentActorInstance != actorInstance || forceReInit) { // set the current actor instance in any case @@ -150,7 +150,7 @@ namespace EMStudio AZStd::vector phonemeInstances; AZStd::vector defaultMorphTargetInstances; - // get the morph target setup + EMotionFX::Actor* actor = actorInstance->GetActor(); EMotionFX::MorphSetup* morphSetup = actor->GetMorphSetup(actorInstance->GetLODLevel()); if (morphSetup == nullptr) { @@ -278,6 +278,13 @@ namespace EMStudio } } + void MorphTargetsWindowPlugin::OnActorInstanceDestroyed(EMotionFX::ActorInstance* actorInstance) + { + if (mCurrentActorInstance == actorInstance) + { + ReInit(/*actorInstance=*/nullptr); + } + } //----------------------------------------------------------------------------------------- // Command callbacks diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetsWindowPlugin.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetsWindowPlugin.h index ec64af2789..1f5abba310 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetsWindowPlugin.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetsWindowPlugin.h @@ -16,6 +16,7 @@ #include #include "../../../../EMStudioSDK/Source/DockWidgetPlugin.h" #include +#include #include "MorphTargetGroupWidget.h" #include #include @@ -26,6 +27,7 @@ namespace EMStudio { class MorphTargetsWindowPlugin : public EMStudio::DockWidgetPlugin + , private EMotionFX::ActorInstanceNotificationBus::Handler { Q_OBJECT MCORE_MEMORYOBJECTCATEGORY(MorphTargetsWindowPlugin, MCore::MCORE_DEFAULT_ALIGNMENT, MEMCATEGORY_STANDARDPLUGINS); @@ -54,6 +56,7 @@ namespace EMStudio EMStudioPlugin* Clone() override; // update the morph targets window based on the current selection + void ReInit(EMotionFX::ActorInstance* actorInstance, bool forceReInit = false); void ReInit(bool forceReInit = false); // clear all widgets from the window @@ -70,6 +73,9 @@ namespace EMStudio void WindowReInit(bool visible); private: + // ActorInstanceNotificationBus overrides + void OnActorInstanceDestroyed(EMotionFX::ActorInstance* actorInstance) override; + // declare the callbacks MCORE_DEFINECOMMANDCALLBACK(CommandSelectCallback); MCORE_DEFINECOMMANDCALLBACK(CommandUnselectCallback); diff --git a/Gems/EMotionFX/Code/EMotionFX/emotionfx_files.cmake b/Gems/EMotionFX/Code/EMotionFX/emotionfx_files.cmake index 9f5fe617d4..9743ab5f15 100644 --- a/Gems/EMotionFX/Code/EMotionFX/emotionfx_files.cmake +++ b/Gems/EMotionFX/Code/EMotionFX/emotionfx_files.cmake @@ -15,6 +15,7 @@ set(FILES Source/ActorBus.h Source/ActorInstance.cpp Source/ActorInstance.h + Source/ActorInstanceBus.h Source/ActorManager.cpp Source/ActorManager.h Source/ActorUpdateScheduler.h From 8a6975131d9d103346ae2e47679e51b055d2454a Mon Sep 17 00:00:00 2001 From: John Jones-Steele Date: Mon, 14 Jun 2021 09:26:28 +0100 Subject: [PATCH 172/233] Modified the link.svg --- .../AzQtComponents/Images/Notifications/link.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Notifications/link.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Notifications/link.svg index dfd21d157f..6f5608c092 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Images/Notifications/link.svg +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Notifications/link.svg @@ -1,4 +1,4 @@ - - + + From 73d5617af354fe8dd18ea044c374645082041f9b Mon Sep 17 00:00:00 2001 From: Yuriy Toporovskyy Date: Mon, 14 Jun 2021 10:02:49 -0400 Subject: [PATCH 173/233] Full screen game preview for Atom viewport --- Code/Sandbox/Editor/CryEdit.cpp | 5 + Code/Sandbox/Editor/EditorViewportWidget.cpp | 144 +++++++++++++++++++ Code/Sandbox/Editor/EditorViewportWidget.h | 5 + Code/Sandbox/Editor/MainWindow.cpp | 16 ++- Code/Sandbox/Editor/Resource.h | 1 + Code/Sandbox/Editor/Settings.cpp | 6 + Code/Sandbox/Editor/ToolbarManager.cpp | 1 + 7 files changed, 174 insertions(+), 4 deletions(-) diff --git a/Code/Sandbox/Editor/CryEdit.cpp b/Code/Sandbox/Editor/CryEdit.cpp index 955a299172..815f986e1a 100644 --- a/Code/Sandbox/Editor/CryEdit.cpp +++ b/Code/Sandbox/Editor/CryEdit.cpp @@ -376,6 +376,11 @@ void CCryEditApp::RegisterActionHandlers() ON_COMMAND(ID_EDIT_FETCH, OnEditFetch) ON_COMMAND(ID_FILE_EXPORTTOGAMENOSURFACETEXTURE, OnFileExportToGameNoSurfaceTexture) ON_COMMAND(ID_VIEW_SWITCHTOGAME, OnViewSwitchToGame) + MainWindow::instance()->GetActionManager()->RegisterActionHandler(ID_VIEW_SWITCHTOGAME_FULLSCREEN, [this]() { + auto fs = gEnv->pConsole->GetCVar("ed_previewGameInFullscreen_once"); + fs->Set(1); + OnViewSwitchToGame(); + }); ON_COMMAND(ID_MOVE_OBJECT, OnMoveObject) ON_COMMAND(ID_RENAME_OBJ, OnRenameObj) ON_COMMAND(ID_EDITMODE_MOVE, OnEditmodeMove) diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index 5a444f0521..10a680c00c 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -82,6 +82,8 @@ #include "AnimationContext.h" #include "Objects/SelectionGroup.h" #include "Core/QtEditorApplication.h" +#include "MainWindow.h" +#include "LayoutWnd.h" // ComponentEntityEditorPlugin #include @@ -694,6 +696,11 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event) } } SetCurrentCursor(STD_CURSOR_GAME); + + if (ShouldPreviewFullscreen()) + { + StartFullscreenPreview(); + } } if (m_renderViewport) @@ -712,6 +719,11 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event) m_bInOrbitMode = false; m_bInZoomMode = false; + if (m_inFullscreenPreview) + { + StopFullscreenPreview(); + } + RestoreViewportAfterGameMode(); } @@ -1368,11 +1380,30 @@ void EditorViewportWidget::SetViewportId(int id) { CViewport::SetViewportId(id); + // First delete any existing layout + // This also deletes any existing render viewport widget (since it will be added to the layout + if (QLayout* l = layout()) + { + QLayoutItem* item; + while ((item = l->takeAt(0)) != 0) + { + if (QWidget* w = item->widget()) + { + delete w; + } + l->removeItem(item); + delete item; + } + delete l; + } + // Now that we have an ID, we can initialize our viewport. m_renderViewport = new AtomToolsFramework::RenderViewportWidget(this, false); if (!m_renderViewport->InitializeViewportContext(id)) { AZ_Warning("EditorViewportWidget", false, "Failed to initialize RenderViewportWidget's ViewportContext"); + delete m_renderViewport; + m_renderViewport = nullptr; return; } auto viewportContext = m_renderViewport->GetViewportContext(); @@ -3027,4 +3058,117 @@ float EditorViewportSettings::AngleStep() const return SandboxEditor::AngleSnappingSize(); } +bool EditorViewportWidget::ShouldPreviewFullscreen() +{ + CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout(); + if (!layout) + { + AZ_Assert(false, "CRenderViewport: No View Manager layout"); + return false; + } + + // Doesn't work with split layout (TODO: figure out why and make it work) + if (layout->GetLayout() != EViewLayout::ET_Layout0) { return false; } + + // Not supported in VR + if (gSettings.bEnableGameModeVR) { return false; } + + // If level not loaded, don't preview in fullscreen (preview shouldn't work at all without a level, but it does) + if (auto ge = GetIEditor()->GetGameEngine()) + { + if (!ge->IsLevelLoaded()) { return false; } + } + + // Check 'ed_previewGameInFullscreen_once' and 'ed_previewGameInFullscreen' cvars + if (gEnv->pConsole) + { + if (auto v = gEnv->pConsole->GetCVar("ed_previewGameInFullscreen_once")) + { + if (v->GetIVal() != 0) + { + v->Set(0); + return true; + } + } + + { + auto v = gEnv->pConsole->GetCVar("ed_previewGameInFullscreen"); + return v && v->GetIVal() != 0; // if it doesn't exist, assume its value is 0 + } + + return true; + } + else + { + return false; + } +} + +void EditorViewportWidget::StartFullscreenPreview() +{ + AZ_Assert(!m_inFullscreenPreview, AZ_FUNCTION_SIGNATURE " - called when already in full screen preview"); + m_inFullscreenPreview = true; + + QScreen* screen = QGuiApplication::primaryScreen(); + QRect screenGeometry = screen->geometry(); + + // Unparent this and show it, which turns it into a free floating window + // Also set style to frameless and disable resizing by user + setParent(nullptr); + setWindowFlag(Qt::FramelessWindowHint, true); + setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, true); + setFixedSize(screenGeometry.size()); + move(QPoint(screenGeometry.x(), screenGeometry.y())); + showMaximized(); + + // Hide the main window + MainWindow::instance()->hide(); +} + +void EditorViewportWidget::StopFullscreenPreview() +{ + AZ_Assert(m_inFullscreenPreview, AZ_FUNCTION_SIGNATURE " - called when not in full screen preview"); + m_inFullscreenPreview = false; + + // Unset frameless window flags + setWindowFlag(Qt::FramelessWindowHint, false); + setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, false); + + // Unset fixed size (note that 50x50 is the minimum set in the constructor) + setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + setMinimumSize(50, 50); + + // Attach this viewport to the primary view pane (whose index is 0). + if (CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout()) + { + if (CLayoutViewPane* viewPane = layout->GetViewPaneByIndex(0)) + { + // Force-reattach this viewport to its view pane by first detaching + viewPane->DetachViewport(); + viewPane->AttachViewport(this); + + // Set the main widget of the layout, which causes this widgets size to be bound to the layout + // and the viewport title bar to be displayed + layout->SetMainWidget(viewPane); + } + else + { + AZ_Assert(false, "CRenderViewport: No view pane with ID 0 (primary view pane)"); + } + } + else + { + AZ_Assert(false, "CRenderViewport: No View Manager layout"); + } + + // Set this as the selected viewport + GetIEditor()->GetViewManager()->SelectViewport(this); + + // Show this widget (setting flags may hide it) + showNormal(); + + // Show the main window + MainWindow::instance()->show(); +} + #include diff --git a/Code/Sandbox/Editor/EditorViewportWidget.h b/Code/Sandbox/Editor/EditorViewportWidget.h index 6b2db65847..53c96011b1 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.h +++ b/Code/Sandbox/Editor/EditorViewportWidget.h @@ -385,6 +385,11 @@ protected: }; void ResetToViewSourceType(const ViewSourceType& viewSourType); + bool ShouldPreviewFullscreen(); + void StartFullscreenPreview(); + void StopFullscreenPreview(); + + bool m_inFullscreenPreview = false; bool m_bRenderContextCreated = false; bool m_bInRotateMode = false; bool m_bInMoveMode = false; diff --git a/Code/Sandbox/Editor/MainWindow.cpp b/Code/Sandbox/Editor/MainWindow.cpp index ddc8ff5058..4b0b0e104a 100644 --- a/Code/Sandbox/Editor/MainWindow.cpp +++ b/Code/Sandbox/Editor/MainWindow.cpp @@ -949,6 +949,12 @@ void MainWindow::InitActions() .SetApplyHoverEffect() .SetCheckable(true) .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdatePlayGame); + am->AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, tr("Play &Game (Maximized)")) + .SetShortcut(tr("Ctrl+Shift+G")) + .SetStatusTip(tr("Activate the game input mode (maximized)")) + .SetIcon(Style::icon("Play")) + .SetApplyHoverEffect() + .SetCheckable(true); am->AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, tr("Play Controls")) .SetText(tr("Play Controls")); am->AddAction(ID_SWITCH_PHYSICS, tr("Simulate")) @@ -1266,10 +1272,12 @@ void MainWindow::OnGameModeChanged(bool inGameMode) { menuBar()->setDisabled(inGameMode); m_toolbarManager->SetEnabled(!inGameMode); - QAction* action = m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME); - action->blockSignals(true); // avoid a loop - action->setChecked(inGameMode); - action->blockSignals(false); + + // avoid a loop + AZStd::vector actions = { m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME), m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN) }; + for (auto action : actions) action->blockSignals(true); + for (auto action : actions) action->setChecked(inGameMode); + for (auto action : actions) action->blockSignals(false); } void MainWindow::OnEditorNotifyEvent(EEditorNotifyEvent ev) diff --git a/Code/Sandbox/Editor/Resource.h b/Code/Sandbox/Editor/Resource.h index bd6ae94184..8ef6c0b906 100644 --- a/Code/Sandbox/Editor/Resource.h +++ b/Code/Sandbox/Editor/Resource.h @@ -111,6 +111,7 @@ #define ID_EDIT_FETCH 33465 #define ID_FILE_EXPORTTOGAMENOSURFACETEXTURE 33473 #define ID_VIEW_SWITCHTOGAME 33477 +#define ID_VIEW_SWITCHTOGAME_FULLSCREEN 33478 #define ID_EDIT_DELETE 33480 #define ID_MOVE_OBJECT 33481 #define ID_RENAME_OBJ 33483 diff --git a/Code/Sandbox/Editor/Settings.cpp b/Code/Sandbox/Editor/Settings.cpp index 15d3e793a5..66271a9967 100644 --- a/Code/Sandbox/Editor/Settings.cpp +++ b/Code/Sandbox/Editor/Settings.cpp @@ -947,6 +947,12 @@ void SEditorSettings::PostInitApply() REGISTER_CVAR2_CB("ed_keepEditorActive", &keepEditorActive, 0, VF_NULL, "Keep the editor active, even if no focus is set", KeepEditorActiveChanged); REGISTER_CVAR2("g_TemporaryLevelName", &g_TemporaryLevelName, "temp_level", VF_NULL, "Temporary level named used for experimental levels."); + REGISTER_INT("ed_previewGameInFullscreen", 0, VF_DEV_ONLY, "Preview the game (Ctrl+G, \"Play Game\", etc.) in fullscreen. 0 = no, 1 = yes"); + gEnv->pConsole->GetCVar("ed_previewGameInFullscreen")->SetLimits(0, 1); + + REGISTER_INT("ed_previewGameInFullscreen_once", 0, VF_DEV_ONLY | VF_INVISIBLE, "Preview the game (Ctrl+G, \"Play Game\", etc.) in fullscreen once. 0 = no, 1 = yes"); + gEnv->pConsole->GetCVar("ed_previewGameInFullscreen_once")->SetLimits(0, 1); + CCryEditApp::instance()->KeepEditorActive(keepEditorActive > 0); } diff --git a/Code/Sandbox/Editor/ToolbarManager.cpp b/Code/Sandbox/Editor/ToolbarManager.cpp index 137057a4fa..0c68797b6a 100644 --- a/Code/Sandbox/Editor/ToolbarManager.cpp +++ b/Code/Sandbox/Editor/ToolbarManager.cpp @@ -603,6 +603,7 @@ AmazonToolbar ToolbarManager::GetPlayConsoleToolbar() const t.AddAction(ID_TOOLBAR_SEPARATOR, ORIGINAL_TOOLBAR_VERSION); t.AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, ORIGINAL_TOOLBAR_VERSION); t.AddAction(ID_VIEW_SWITCHTOGAME, TOOLBARS_WITH_PLAY_GAME); + t.AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, TOOLBARS_WITH_PLAY_GAME); t.AddAction(ID_TOOLBAR_SEPARATOR, ORIGINAL_TOOLBAR_VERSION); t.AddAction(ID_SWITCH_PHYSICS, TOOLBARS_WITH_PLAY_GAME); return t; From f8cf4ba08767ecb02b82b49b48786cf803fd1a0e Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Mon, 14 Jun 2021 17:34:29 +0200 Subject: [PATCH 174/233] [LYN-3416] Animation: Attachment component does not update it's location automatically (#1305) --- .../Code/Source/Animation/EditorAttachmentComponent.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp index f14340b4c9..e7f2a98a71 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp @@ -118,8 +118,7 @@ namespace AZ void EditorAttachmentComponent::Activate() { Base::Activate(); - m_boneFollower.Activate(GetEntity(), CreateAttachmentConfiguration(), - false); // Entity's don't animate in Editor + m_boneFollower.Activate(GetEntity(), CreateAttachmentConfiguration(), /*targetCanAnimate=*/true); } void EditorAttachmentComponent::Deactivate() From 074c3107cd08560fcae41c8e308e748335dbddc6 Mon Sep 17 00:00:00 2001 From: Yuriy Toporovskyy Date: Mon, 14 Jun 2021 11:54:11 -0400 Subject: [PATCH 175/233] Expose protected method required by full screen preview feature --- Code/Sandbox/Editor/LayoutWnd.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Code/Sandbox/Editor/LayoutWnd.h b/Code/Sandbox/Editor/LayoutWnd.h index 2af56f907c..66c9a9e889 100644 --- a/Code/Sandbox/Editor/LayoutWnd.h +++ b/Code/Sandbox/Editor/LayoutWnd.h @@ -113,6 +113,8 @@ public: //! Switch 2D viewports. void Cycle2DViewport(); + using AzQtComponents::ToolBarArea::SetMainWidget; + public slots: void ResetLayout(); From 37f70fb47cd9f5badb17022955473916cbe66160 Mon Sep 17 00:00:00 2001 From: Yuriy Toporovskyy Date: Mon, 14 Jun 2021 12:08:43 -0400 Subject: [PATCH 176/233] Address some PR feedback. - Better variable names - Close a parenthesis in a comment - Check equality against nullptr instead of 0 - Add comment to document method of clearing a QLayout - Format some things - Const correctness - Remove a TODO in a comment - Attempt at removing concatenation of string literal AZ_FUNCTION_SIGNATURE, which might not be a literal on every compiler/platform --- Code/Sandbox/Editor/EditorViewportWidget.cpp | 44 ++++++++++++-------- Code/Sandbox/Editor/EditorViewportWidget.h | 2 +- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index 10a680c00c..ba181aa99b 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -1381,20 +1381,21 @@ void EditorViewportWidget::SetViewportId(int id) CViewport::SetViewportId(id); // First delete any existing layout - // This also deletes any existing render viewport widget (since it will be added to the layout - if (QLayout* l = layout()) + // This also deletes any existing render viewport widget (since it will be added to the layout) + // Below is the typical method of clearing a QLayout, see e.g. https://doc.qt.io/qt-5/qlayout.html#takeAt + if (QLayout* thisLayout = layout()) { QLayoutItem* item; - while ((item = l->takeAt(0)) != 0) + while ((item = thisLayout->takeAt(0)) != nullptr) { - if (QWidget* w = item->widget()) + if (QWidget* widget = item->widget()) { - delete w; + delete widget; } - l->removeItem(item); + thisLayout->removeItem(item); delete item; } - delete l; + delete thisLayout; } // Now that we have an ID, we can initialize our viewport. @@ -3058,7 +3059,7 @@ float EditorViewportSettings::AngleStep() const return SandboxEditor::AngleSnappingSize(); } -bool EditorViewportWidget::ShouldPreviewFullscreen() +bool EditorViewportWidget::ShouldPreviewFullscreen() const { CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout(); if (!layout) @@ -3067,16 +3068,25 @@ bool EditorViewportWidget::ShouldPreviewFullscreen() return false; } - // Doesn't work with split layout (TODO: figure out why and make it work) - if (layout->GetLayout() != EViewLayout::ET_Layout0) { return false; } + // Doesn't work with split layout + if (layout->GetLayout() != EViewLayout::ET_Layout0) + { + return false; + } // Not supported in VR - if (gSettings.bEnableGameModeVR) { return false; } + if (gSettings.bEnableGameModeVR) + { + return false; + } // If level not loaded, don't preview in fullscreen (preview shouldn't work at all without a level, but it does) if (auto ge = GetIEditor()->GetGameEngine()) { - if (!ge->IsLevelLoaded()) { return false; } + if (!ge->IsLevelLoaded()) + { + return false; + } } // Check 'ed_previewGameInFullscreen_once' and 'ed_previewGameInFullscreen' cvars @@ -3106,11 +3116,11 @@ bool EditorViewportWidget::ShouldPreviewFullscreen() void EditorViewportWidget::StartFullscreenPreview() { - AZ_Assert(!m_inFullscreenPreview, AZ_FUNCTION_SIGNATURE " - called when already in full screen preview"); + AZ_Assert(!m_inFullscreenPreview, "EditorViewportWidget::StartFullscreenPreview called when already in full screen preview"); m_inFullscreenPreview = true; - QScreen* screen = QGuiApplication::primaryScreen(); - QRect screenGeometry = screen->geometry(); + const QScreen* screen = QGuiApplication::primaryScreen(); + const QRect screenGeometry = screen->geometry(); // Unparent this and show it, which turns it into a free floating window // Also set style to frameless and disable resizing by user @@ -3121,13 +3131,13 @@ void EditorViewportWidget::StartFullscreenPreview() move(QPoint(screenGeometry.x(), screenGeometry.y())); showMaximized(); - // Hide the main window + // This must be done after unparenting this widget above MainWindow::instance()->hide(); } void EditorViewportWidget::StopFullscreenPreview() { - AZ_Assert(m_inFullscreenPreview, AZ_FUNCTION_SIGNATURE " - called when not in full screen preview"); + AZ_Assert(m_inFullscreenPreview, "EditorViewportWidget::StartFullscreenPreview called when not in full screen preview"); m_inFullscreenPreview = false; // Unset frameless window flags diff --git a/Code/Sandbox/Editor/EditorViewportWidget.h b/Code/Sandbox/Editor/EditorViewportWidget.h index 53c96011b1..511a7910c6 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.h +++ b/Code/Sandbox/Editor/EditorViewportWidget.h @@ -385,7 +385,7 @@ protected: }; void ResetToViewSourceType(const ViewSourceType& viewSourType); - bool ShouldPreviewFullscreen(); + bool ShouldPreviewFullscreen() const; void StartFullscreenPreview(); void StopFullscreenPreview(); From 5becf25a79d194286169d5f83d17d2d411731f32 Mon Sep 17 00:00:00 2001 From: Yuriy Toporovskyy Date: Mon, 14 Jun 2021 12:14:58 -0400 Subject: [PATCH 177/233] Address some PR feedback. - Better comment explaining signal blocking - Clang format --- Code/Sandbox/Editor/MainWindow.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Code/Sandbox/Editor/MainWindow.cpp b/Code/Sandbox/Editor/MainWindow.cpp index 4b0b0e104a..1ad97f31de 100644 --- a/Code/Sandbox/Editor/MainWindow.cpp +++ b/Code/Sandbox/Editor/MainWindow.cpp @@ -1273,11 +1273,24 @@ void MainWindow::OnGameModeChanged(bool inGameMode) menuBar()->setDisabled(inGameMode); m_toolbarManager->SetEnabled(!inGameMode); - // avoid a loop + // block signals on the switch to game actions before setting the checked state, as + // setting the checked state triggers the action, which will re-enter this function + // and result in an infinite loop AZStd::vector actions = { m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME), m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN) }; - for (auto action : actions) action->blockSignals(true); - for (auto action : actions) action->setChecked(inGameMode); - for (auto action : actions) action->blockSignals(false); + for (auto action : actions) + { + action->blockSignals(true); + } + + for (auto action : actions) + { + action->setChecked(inGameMode); + } + + for (auto action : actions) + { + action->blockSignals(false); + } } void MainWindow::OnEditorNotifyEvent(EEditorNotifyEvent ev) From 7ef292d2102a3212d0150d8ed1e9aaaa7b2bf60b Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Mon, 14 Jun 2021 10:27:03 -0700 Subject: [PATCH 178/233] Removed VK_KHR_ray_query extension requirement for enabling ray tracing --- .../RHI/Vulkan/External/glad/2.0.0-beta/include/glad/vulkan.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Gems/Atom/RHI/Vulkan/External/glad/2.0.0-beta/include/glad/vulkan.h b/Gems/Atom/RHI/Vulkan/External/glad/2.0.0-beta/include/glad/vulkan.h index a6ee9e8634..33d4b64082 100644 --- a/Gems/Atom/RHI/Vulkan/External/glad/2.0.0-beta/include/glad/vulkan.h +++ b/Gems/Atom/RHI/Vulkan/External/glad/2.0.0-beta/include/glad/vulkan.h @@ -12691,8 +12691,7 @@ static int glad_vk_find_extensions_vulkan( VkPhysicalDevice physical_device) { #endif GLAD_VK_KHR_push_descriptor = glad_vk_has_extension("VK_KHR_push_descriptor", extension_count, extensions); GLAD_VK_KHR_ray_tracing = (glad_vk_has_extension("VK_KHR_acceleration_structure", extension_count, extensions) - && glad_vk_has_extension("VK_KHR_ray_tracing_pipeline", extension_count, extensions) - && glad_vk_has_extension("VK_KHR_ray_query", extension_count, extensions)); + && glad_vk_has_extension("VK_KHR_ray_tracing_pipeline", extension_count, extensions)); GLAD_VK_KHR_relaxed_block_layout = glad_vk_has_extension("VK_KHR_relaxed_block_layout", extension_count, extensions); GLAD_VK_KHR_sampler_mirror_clamp_to_edge = glad_vk_has_extension("VK_KHR_sampler_mirror_clamp_to_edge", extension_count, extensions); GLAD_VK_KHR_sampler_ycbcr_conversion = glad_vk_has_extension("VK_KHR_sampler_ycbcr_conversion", extension_count, extensions); From b31de1da3304067149ac309acb63d280744953c3 Mon Sep 17 00:00:00 2001 From: jckand-amzn Date: Mon, 14 Jun 2021 13:46:53 -0500 Subject: [PATCH 179/233] LYN-4518: Updating expected actions for test_Menus_ViewMenuOptions_Work --- .../PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py | 2 +- AutomatedTesting/Gem/PythonTests/editor/test_Menus.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py index 173d658b3c..3f94a23696 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py @@ -49,7 +49,7 @@ class TestViewMenuOptions(EditorTestHelper): view_menu_options = [ ("Center on Selection",), ("Show Quick Access Bar",), - ("Viewport", "Wireframe"), + ("Viewport", "Configure Layout"), ("Viewport", "Go to Position"), ("Viewport", "Center on Selection"), ("Viewport", "Go to Location"), diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py index 2b3fdcbdf3..26231e33d7 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py @@ -89,7 +89,7 @@ class TestMenus(object): expected_lines = [ "Center on Selection Action triggered", "Show Quick Access Bar Action triggered", - "Wireframe Action triggered", + "Configure Layout Action triggered", "Go to Position Action triggered", "Center on Selection Action triggered", "Go to Location Action triggered", From 6f61454be4fbe3bd9b32c16773ec50b41c9e9911 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Mon, 14 Jun 2021 14:20:39 -0500 Subject: [PATCH 180/233] Added support for remapping entity refs across slice instances (#1284) * Fixed memory assert on shutdown. Cleaned up the entity pointers on serialization, so that they no longer leak themselves, their asset references, or anything else within them. * Added support for remapping entity refs across slice instances. Slice instances can have components with entity references that have been modified to reference entities in other slice instances, or even in the parent. This change detects and remaps those references so that they work correctly. --- .../SerializeContextTools/SliceConverter.cpp | 192 ++++++++++++++---- .../SerializeContextTools/SliceConverter.h | 46 +++-- 2 files changed, 185 insertions(+), 53 deletions(-) diff --git a/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp index 9fba060960..e7dc49d5b3 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.cpp +++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp @@ -177,9 +177,10 @@ namespace AZ AZ::Entity* rootEntity = reinterpret_cast(classPtr); bool convertResult = ConvertSliceToPrefab(context, outputPath, isDryRun, rootEntity); - // Clear out the references to any nested slices so that the nested assets get unloaded correctly at the end of - // the conversion. - ClearSliceAssetReferences(rootEntity); + + // Delete the root entity pointer. Otherwise, it will leak itself along with all of the slice asset references held + // within it. + delete rootEntity; return convertResult; }; @@ -229,8 +230,12 @@ namespace AZ return false; } - // Get all of the entities from the slice. + // Get all of the entities from the slice. We're taking ownership of them, so we also remove them from the slice component + // without deleting them. + constexpr bool deleteEntities = false; + constexpr bool removeEmptyInstances = true; SliceComponent::EntityList sliceEntities = sliceComponent->GetNewEntities(); + sliceComponent->RemoveAllEntities(deleteEntities, removeEmptyInstances); AZ_Printf("Convert-Slice", " Slice contains %zu entities.\n", sliceEntities.size()); // Create the Prefab with the entities from the slice. @@ -273,6 +278,12 @@ namespace AZ } } + // Save off a mapping of the slice's metadata entity ID as well, even though we never converted the entity itself. + // This will help us better detect entity ID mapping errors for nested slice instances. + AZ::Entity* metadataEntity = sliceComponent->GetMetadataEntity(); + constexpr bool isMetadataEntity = true; + m_aliasIdMapper.emplace(metadataEntity->GetId(), SliceEntityMappingInfo(templateId, "MetadataEntity", isMetadataEntity)); + // Update the prefab template with the fixed-up data in our prefab instance. AzToolsFramework::Prefab::PrefabDom prefabDom; bool storeResult = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*sourceInstance, prefabDom); @@ -402,6 +413,21 @@ namespace AZ "Convert-Slice", " Attaching %zu instances of nested slice '%s'.\n", instances.size(), nestedPrefabPath.Native().c_str()); + // Before processing any further, save off all the known entity IDs from all the instances and how they map back to + // the base nested prefab that they've come from (i.e. this one). As we proceed up the chain of nesting, this will + // build out a hierarchical list of owning instances for each entity that we can trace upwards to know where to add + // the entity into our nested prefab instance. + // This step needs to occur *before* converting the instances themselves, because while converting instances, they + // might have entity ID references that point to other instances. By having the full instance entity ID map in place + // before conversion, we'll be able to fix them up appropriately. + + for (auto& instance : instances) + { + AZStd::string instanceAlias = GetInstanceAlias(instance); + UpdateSliceEntityInstanceMappings(instance.GetEntityIdToBaseMap(), instanceAlias); + } + + // Now that we have all the entity ID mappings, convert all the instances. for (auto& instance : instances) { bool instanceConvertResult = ConvertSliceInstance(instance, sliceAsset, nestedTemplate, sourceInstance); @@ -415,6 +441,28 @@ namespace AZ return true; } + AZStd::string SliceConverter::GetInstanceAlias(const AZ::SliceComponent::SliceInstance& instance) + { + // When creating the new instance, we would like to have deterministic instance aliases. Prefabs that depend on this one + // will have patches that reference the alias, so if we reconvert this slice a second time, we would like it to produce + // the same results. To get a deterministic and unique alias, we rely on the slice instance. The slice instance contains + // a map of slice entity IDs to unique instance entity IDs. We'll just consistently use the first entry in the map as the + // unique instance ID. + AZStd::string instanceAlias; + auto entityIdMap = instance.GetEntityIdMap(); + if (!entityIdMap.empty()) + { + instanceAlias = AZStd::string::format("Instance_%s", entityIdMap.begin()->second.ToString().c_str()); + } + else + { + AZ_Error("Convert-Slice", false, " Couldn't create deterministic instance alias."); + instanceAlias = AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str()); + } + return instanceAlias; + } + + bool SliceConverter::ConvertSliceInstance( AZ::SliceComponent::SliceInstance& instance, AZ::Data::Asset& sliceAsset, @@ -438,27 +486,7 @@ namespace AZ auto instanceToTemplateInterface = AZ::Interface::Get(); auto prefabSystemComponentInterface = AZ::Interface::Get(); - // When creating the new instance, we would like to have deterministic instance aliases. Prefabs that depend on this one - // will have patches that reference the alias, so if we reconvert this slice a second time, we would like it to produce - // the same results. To get a deterministic and unique alias, we rely on the slice instance. The slice instance contains - // a map of slice entity IDs to unique instance entity IDs. We'll just consistently use the first entry in the map as the - // unique instance ID. - AZStd::string instanceAlias; - auto entityIdMap = instance.GetEntityIdMap(); - if (!entityIdMap.empty()) - { - instanceAlias = AZStd::string::format("Instance_%s", entityIdMap.begin()->second.ToString().c_str()); - } - else - { - instanceAlias = AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str()); - } - - // Before processing any further, save off all the known entity IDs from this instance and how they map back to the base - // nested prefab that they've come from (i.e. this one). As we proceed up the chain of nesting, this will build out a - // hierarchical list of owning instances for each entity that we can trace upwards to know where to add the entity into - // our nested prefab instance. - UpdateSliceEntityInstanceMappings(instance.GetEntityIdToBaseMap(), instanceAlias); + AZStd::string instanceAlias = GetInstanceAlias(instance); // Create a new unmodified prefab Instance for the nested slice instance. auto nestedInstance = AZStd::make_unique(); @@ -619,6 +647,10 @@ namespace AZ SetParentEntity(containerEntity->get(), topLevelInstance->GetContainerEntityId(), onlySetIfInvalid); } + // After doing all of the above, run through entity references in any of the patched entities, and fix up the entity IDs to + // match the new ones in our prefabs. + RemapIdReferences(m_aliasIdMapper, topLevelInstance, nestedInstance.get(), instantiated, dependentSlice->GetSerializeContext()); + // Add the nested instance itself to the top-level prefab. To do this, we need to add it to our top-level instance, // create a patch out of it, and patch the top-level prefab template. @@ -750,17 +782,6 @@ namespace AZ AZ_Error("Convert-Slice", disconnected, "Asset Processor failed to disconnect successfully."); } - void SliceConverter::ClearSliceAssetReferences(AZ::Entity* rootEntity) - { - SliceComponent* sliceComponent = AZ::EntityUtils::FindFirstDerivedComponent(rootEntity); - // Make a copy of the slice list and remove all of them from the loaded component. - AZ::SliceComponent::SliceList slices = sliceComponent->GetSlices(); - for (auto& slice : slices) - { - sliceComponent->RemoveSlice(&slice); - } - } - void SliceConverter::UpdateSliceEntityInstanceMappings( const AZ::SliceComponent::EntityIdToEntityIdMap& sliceEntityIdMap, const AZStd::string& currentInstanceAlias) { @@ -789,9 +810,108 @@ namespace AZ AZ_Assert(oldId == newId, "The same entity instance ID has unexpectedly appeared twice in the same nested prefab."); } } + else + { + AZ_Warning("Convert-Slice", false, " Couldn't find an entity ID conversion for %s.", oldId.ToString().c_str()); + } } } + void SliceConverter::RemapIdReferences( + const AZStd::unordered_map& idMapper, + AzToolsFramework::Prefab::Instance* topLevelInstance, + AzToolsFramework::Prefab::Instance* nestedInstance, + SliceComponent::InstantiatedContainer* instantiatedEntities, + SerializeContext* context) + { + // Given a set of instantiated entities, run through all of them, look for entity references, and replace the entity IDs with + // new ones that match up with our prefabs. + + IdUtils::Remapper::ReplaceIdsAndIdRefs( + instantiatedEntities, + [idMapper, &topLevelInstance, &nestedInstance]( + const EntityId& sourceId, bool isEntityId, [[maybe_unused]] const AZStd::function& idGenerator) -> EntityId + { + EntityId newId = sourceId; + + // Only convert valid entity references. Actual entity IDs have already been taken care of elsewhere, so ignore them. + if (!isEntityId && sourceId.IsValid()) + { + auto entityEntry = idMapper.find(sourceId); + + // Since we've already remapped transform hierarchies to include container entities, it's possible that our entity + // reference is pointing to a container, which means it won't be in our slice mapping table. In that case, just + // return it as-is. + if (entityEntry == idMapper.end()) + { + return sourceId; + } + + // We've got a slice->prefab mapping entry, so now we need to use it. + auto& mappingStruct = entityEntry->second; + + if (mappingStruct.m_nestedInstanceAliases.empty()) + { + // If we don't have a chain of nested instance aliases, then this entity reference is either within the + // current nested instance or it's pointing to an entity in the top-level instance. We'll try them both + // to look for a match. + + EntityId prefabId = nestedInstance->GetEntityId(mappingStruct.m_entityAlias); + if (!prefabId.IsValid()) + { + prefabId = topLevelInstance->GetEntityId(mappingStruct.m_entityAlias); + } + + if (prefabId.IsValid()) + { + newId = prefabId; + } + else + { + AZ_Error("Convert-Slice", false, " Couldn't find source ID %s", sourceId.ToString().c_str()); + } + } + else + { + // We *do* have a chain of nested instance aliases. This chain could either be relative to the nested instance + // or the top-level instance. We can tell which one it is by which one can find the first nested instance + // alias. + + AzToolsFramework::Prefab::Instance* entityInstance = nestedInstance; + auto it = mappingStruct.m_nestedInstanceAliases.rbegin(); + if (!entityInstance->FindNestedInstance(*it).has_value()) + { + entityInstance = topLevelInstance; + } + + // Now that we've got a starting point, iterate through the chain of nested instance aliases to find the + // correct instance to get the entity ID for. We have to go from slice IDs -> entity aliases -> entity IDs + // because prefab instance creation can change some of our entity IDs along the way. + for (; it != mappingStruct.m_nestedInstanceAliases.rend(); it++) + { + auto foundInstance = entityInstance->FindNestedInstance(*it); + if (foundInstance.has_value()) + { + entityInstance = &(foundInstance->get()); + } + else + { + AZ_Assert(false, "Couldn't find nested instance %s", it->c_str()); + } + } + + EntityId prefabId = entityInstance->GetEntityId(mappingStruct.m_entityAlias); + if (prefabId.IsValid()) + { + newId = prefabId; + } + } + } + + return newId; + }, + context); + } } // namespace SerializeContextTools } // namespace AZ diff --git a/Code/Tools/SerializeContextTools/SliceConverter.h b/Code/Tools/SerializeContextTools/SliceConverter.h index ee0bb0a539..31c8306477 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.h +++ b/Code/Tools/SerializeContextTools/SliceConverter.h @@ -42,6 +42,28 @@ namespace AZ bool ConvertSliceFiles(Application& application); private: + // When converting slice entities, especially for nested slices, we need to keep track of the original + // entity ID, the entity alias it uses in the prefab, and which template and nested instance path it maps to. + // As we encounter each instanced entity ID, we can look it up in this structure and use this to determine how to properly + // add it to the correct place in the hierarchy. + struct SliceEntityMappingInfo + { + SliceEntityMappingInfo( + AzToolsFramework::Prefab::TemplateId templateId, + AzToolsFramework::Prefab::EntityAlias entityAlias, + bool isMetadataEntity = false) + : m_templateId(templateId) + , m_entityAlias(entityAlias) + , m_isMetadataEntity(isMetadataEntity) + { + } + + AzToolsFramework::Prefab::TemplateId m_templateId; + AzToolsFramework::Prefab::EntityAlias m_entityAlias; + AZStd::vector m_nestedInstanceAliases; + bool m_isMetadataEntity{ false }; + }; + bool ConnectToAssetProcessor(); void DisconnectFromAssetProcessor(); @@ -58,27 +80,17 @@ namespace AZ void SetParentEntity(const AZ::Entity& entity, const AZ::EntityId& parentId, bool onlySetIfInvalid); void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId); bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId); - void ClearSliceAssetReferences(AZ::Entity* rootEntity); void UpdateSliceEntityInstanceMappings( const AZ::SliceComponent::EntityIdToEntityIdMap& sliceEntityIdMap, const AZStd::string& currentInstanceAlias); + AZStd::string GetInstanceAlias(const AZ::SliceComponent::SliceInstance& instance); - // When converting slice entities, especially for nested slices, we need to keep track of the original - // entity ID, the entity alias it uses in the prefab, and which template and nested instance path it maps to. - // As we encounter each instanced entity ID, we can look it up in this structure and use this to determine how to properly - // add it to the correct place in the hierarchy. - struct SliceEntityMappingInfo - { - SliceEntityMappingInfo(AzToolsFramework::Prefab::TemplateId templateId, AzToolsFramework::Prefab::EntityAlias entityAlias) - : m_templateId(templateId) - , m_entityAlias(entityAlias) - { - } - - AzToolsFramework::Prefab::TemplateId m_templateId; - AzToolsFramework::Prefab::EntityAlias m_entityAlias; - AZStd::vector m_nestedInstanceAliases; - }; + void RemapIdReferences( + const AZStd::unordered_map& idMapper, + AzToolsFramework::Prefab::Instance* topLevelInstance, + AzToolsFramework::Prefab::Instance* nestedInstance, + SliceComponent::InstantiatedContainer* instantiatedEntities, + SerializeContext* context); // Track all of the entity IDs created and associate them with enough conversion information to know how to place the // entities in the correct place in the prefab hierarchy and fix up parent entity ID mappings to work with the nested From 406792606b1c335e138d4caf3770b6b19be737b7 Mon Sep 17 00:00:00 2001 From: Santora Date: Mon, 14 Jun 2021 13:04:15 -0700 Subject: [PATCH 181/233] I missed one spot that needs to print the address. --- Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp index eac1ee42e5..d9691ca175 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp @@ -261,7 +261,7 @@ namespace AZ { // TODO: I think we should make Shader handle OnShaderAssetReinitialized and treat it just like the shader reloaded. - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnShaderAssetReinitialized %s", shaderAsset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderAssetReinitialized %s", this, shaderAsset.GetHint().c_str()); // Note that it might not be strictly necessary to reinitialize the entire material, we might be able to get away with // just bumping the m_currentChangeId or some other minor updates. But it's pretty hard to know what exactly needs to be // updated to correctly handle the reload, so it's safer to just reinitialize the whole material. From 84492dee48b1c0cb3a99a7ab5eef586cc81373b5 Mon Sep 17 00:00:00 2001 From: Yuriy Toporovskyy Date: Mon, 14 Jun 2021 16:19:18 -0400 Subject: [PATCH 182/233] Address some PR feedback - Use AZ::IConsole instead of deprecated Cry IConsole. - Create fullscreen preview widget on the same screen where the main window is found, instead of on the 'primary' screen - Remove "ed_previewGameInFullscreen", which had the effect of always doing a full screen preview. If this functionality is desired, it should be re-added as a registry key instead of a cvar --- Code/Sandbox/Editor/CryEdit.cpp | 6 +++-- Code/Sandbox/Editor/EditorViewportWidget.cpp | 25 +++++++------------- Code/Sandbox/Editor/Settings.cpp | 9 +++---- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/Code/Sandbox/Editor/CryEdit.cpp b/Code/Sandbox/Editor/CryEdit.cpp index 815f986e1a..536821ca6c 100644 --- a/Code/Sandbox/Editor/CryEdit.cpp +++ b/Code/Sandbox/Editor/CryEdit.cpp @@ -53,6 +53,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include // AzFramework #include @@ -356,6 +357,8 @@ CCryEditDoc* CCryDocManager::OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToM for (int i = idStart; i <= idEnd; ++i) \ ON_COMMAND(i, method); +AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once); + void CCryEditApp::RegisterActionHandlers() { ON_COMMAND(ID_APP_ABOUT, OnAppAbout) @@ -377,8 +380,7 @@ void CCryEditApp::RegisterActionHandlers() ON_COMMAND(ID_FILE_EXPORTTOGAMENOSURFACETEXTURE, OnFileExportToGameNoSurfaceTexture) ON_COMMAND(ID_VIEW_SWITCHTOGAME, OnViewSwitchToGame) MainWindow::instance()->GetActionManager()->RegisterActionHandler(ID_VIEW_SWITCHTOGAME_FULLSCREEN, [this]() { - auto fs = gEnv->pConsole->GetCVar("ed_previewGameInFullscreen_once"); - fs->Set(1); + ed_previewGameInFullscreen_once = true; OnViewSwitchToGame(); }); ON_COMMAND(ID_MOVE_OBJECT, OnMoveObject) diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index ba181aa99b..b02aafbe32 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include // AzFramework @@ -3059,6 +3060,8 @@ float EditorViewportSettings::AngleStep() const return SandboxEditor::AngleSnappingSize(); } +AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once); + bool EditorViewportWidget::ShouldPreviewFullscreen() const { CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout(); @@ -3089,23 +3092,10 @@ bool EditorViewportWidget::ShouldPreviewFullscreen() const } } - // Check 'ed_previewGameInFullscreen_once' and 'ed_previewGameInFullscreen' cvars - if (gEnv->pConsole) + // Check 'ed_previewGameInFullscreen_once' + if (ed_previewGameInFullscreen_once) { - if (auto v = gEnv->pConsole->GetCVar("ed_previewGameInFullscreen_once")) - { - if (v->GetIVal() != 0) - { - v->Set(0); - return true; - } - } - - { - auto v = gEnv->pConsole->GetCVar("ed_previewGameInFullscreen"); - return v && v->GetIVal() != 0; // if it doesn't exist, assume its value is 0 - } - + ed_previewGameInFullscreen_once = true; return true; } else @@ -3119,7 +3109,8 @@ void EditorViewportWidget::StartFullscreenPreview() AZ_Assert(!m_inFullscreenPreview, "EditorViewportWidget::StartFullscreenPreview called when already in full screen preview"); m_inFullscreenPreview = true; - const QScreen* screen = QGuiApplication::primaryScreen(); + // Pick the screen on which the main window lies to use as the screen for the full screen preview + const QScreen* screen = MainWindow::instance()->screen(); const QRect screenGeometry = screen->geometry(); // Unparent this and show it, which turns it into a free floating window diff --git a/Code/Sandbox/Editor/Settings.cpp b/Code/Sandbox/Editor/Settings.cpp index 66271a9967..f805e2e84b 100644 --- a/Code/Sandbox/Editor/Settings.cpp +++ b/Code/Sandbox/Editor/Settings.cpp @@ -26,6 +26,7 @@ #include #include #include +#include // AzFramework #include @@ -926,6 +927,8 @@ void SEditorSettings::Load() } ////////////////////////////////////////////////////////////////////////// +AZ_CVAR(bool, ed_previewGameInFullscreen_once, false, nullptr, AZ::ConsoleFunctorFlags::IsInvisible, "Preview the game (Ctrl+G, \"Play Game\", etc.) in fullscreen once"); + void SEditorSettings::PostInitApply() { if (!gEnv || !gEnv->pConsole) @@ -947,12 +950,6 @@ void SEditorSettings::PostInitApply() REGISTER_CVAR2_CB("ed_keepEditorActive", &keepEditorActive, 0, VF_NULL, "Keep the editor active, even if no focus is set", KeepEditorActiveChanged); REGISTER_CVAR2("g_TemporaryLevelName", &g_TemporaryLevelName, "temp_level", VF_NULL, "Temporary level named used for experimental levels."); - REGISTER_INT("ed_previewGameInFullscreen", 0, VF_DEV_ONLY, "Preview the game (Ctrl+G, \"Play Game\", etc.) in fullscreen. 0 = no, 1 = yes"); - gEnv->pConsole->GetCVar("ed_previewGameInFullscreen")->SetLimits(0, 1); - - REGISTER_INT("ed_previewGameInFullscreen_once", 0, VF_DEV_ONLY | VF_INVISIBLE, "Preview the game (Ctrl+G, \"Play Game\", etc.) in fullscreen once. 0 = no, 1 = yes"); - gEnv->pConsole->GetCVar("ed_previewGameInFullscreen_once")->SetLimits(0, 1); - CCryEditApp::instance()->KeepEditorActive(keepEditorActive > 0); } From 6c05adddec4a1c056fd7e52a28aeccd7f289cec1 Mon Sep 17 00:00:00 2001 From: gallowj Date: Mon, 14 Jun 2021 15:41:50 -0500 Subject: [PATCH 183/233] This material did not look correct, sneaking it in --- .../Objects/PlayfulTeapot_playfulteapot.material | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Objects/PlayfulTeapot_playfulteapot.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Objects/PlayfulTeapot_playfulteapot.material index 27540c587e..da35fda141 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Objects/PlayfulTeapot_playfulteapot.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Objects/PlayfulTeapot_playfulteapot.material @@ -14,13 +14,14 @@ }, "clearCoat": { "enable": true, - "normalMap": "EngineAssets/Textures/perlinNoiseNormal_ddn.tif" + "normalMap": "EngineAssets/Textures/perlinNoiseNormal_ddn.tif", + "normalStrength": 0.10000000149011612 }, "general": { "applySpecularAA": true }, "metallic": { - "factor": 0.5 + "factor": 0.10000000149011612 }, "normal": { "factor": 0.05000000074505806, @@ -31,6 +32,9 @@ }, "roughness": { "factor": 0.0 + }, + "specularF0": { + "enableMultiScatterCompensation": true } } -} +} \ No newline at end of file From 11c3a7532198708ff3fb5821aabd289ec8ea4676 Mon Sep 17 00:00:00 2001 From: amzn-hdoke <61443753+hdoke@users.noreply.github.com> Date: Mon, 14 Jun 2021 13:54:04 -0700 Subject: [PATCH 184/233] Enable Client Auth unit test on Linux (#1312) --- scripts/build/Platform/Linux/build_config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index ee6da77b29..903ac52568 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -83,7 +83,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest" + "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest" } }, "test_profile_nounity": { @@ -95,7 +95,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest" + "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest" } }, "asset_profile": { From 86d0acf76c18b373f81a48f6725605f2489be3c0 Mon Sep 17 00:00:00 2001 From: sharmajs-amzn <82233357+sharmajs-amzn@users.noreply.github.com> Date: Mon, 14 Jun 2021 13:55:37 -0700 Subject: [PATCH 185/233] fixes for invalid drive letter casing for scanfolder (#1281) --- .../assetprocessor_static_files.cmake | 1 + .../AssetManager/assetScanFolderInfo.cpp | 42 +++++++++++++++++++ .../native/AssetManager/assetScanFolderInfo.h | 14 +------ 3 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.cpp diff --git a/Code/Tools/AssetProcessor/assetprocessor_static_files.cmake b/Code/Tools/AssetProcessor/assetprocessor_static_files.cmake index 194e99b247..1e78d1dc6c 100644 --- a/Code/Tools/AssetProcessor/assetprocessor_static_files.cmake +++ b/Code/Tools/AssetProcessor/assetprocessor_static_files.cmake @@ -19,6 +19,7 @@ set(FILES native/AssetManager/AssetRequestHandler.cpp native/AssetManager/AssetRequestHandler.h native/AssetManager/assetScanFolderInfo.h + native/AssetManager/assetScanFolderInfo.cpp native/AssetManager/assetScanner.cpp native/AssetManager/assetScanner.h native/AssetManager/assetScannerWorker.cpp diff --git a/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.cpp b/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.cpp new file mode 100644 index 0000000000..f849b4ee49 --- /dev/null +++ b/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.cpp @@ -0,0 +1,42 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +namespace AssetProcessor +{ + ScanFolderInfo::ScanFolderInfo( + QString path, + QString displayName, + QString portableKey, + bool isRoot, + bool recurseSubFolders, + AZStd::vector platforms, + int order, + AZ::s64 scanFolderID, + bool canSaveNewAssets) + : m_scanPath(path) + , m_displayName(displayName) + , m_portableKey (portableKey) + , m_isRoot(isRoot) + , m_recurseSubFolders(recurseSubFolders) + , m_order(order) + , m_scanFolderID(scanFolderID) + , m_platforms(platforms) + , m_canSaveNewAssets(canSaveNewAssets) + { + m_scanPath = AssetUtilities::NormalizeFilePath(m_scanPath); + // note that m_scanFolderID is 0 unless its filled in from the DB. + } + +} // end namespace AssetProcessor diff --git a/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.h b/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.h index 5767822451..5fbda237d7 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.h +++ b/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.h @@ -33,19 +33,7 @@ namespace AssetProcessor AZStd::vector platforms = AZStd::vector{}, int order = 0, AZ::s64 scanFolderID = 0, - bool canSaveNewAssets = false) - : m_scanPath(path) - , m_displayName(displayName) - , m_portableKey (portableKey) - , m_isRoot(isRoot) - , m_recurseSubFolders(recurseSubFolders) - , m_order(order) - , m_scanFolderID(scanFolderID) - , m_platforms(platforms) - , m_canSaveNewAssets(canSaveNewAssets) - { - // note that m_scanFolderID is 0 unless its filled in from the DB. - } + bool canSaveNewAssets = false); ScanFolderInfo() = default; ScanFolderInfo(const ScanFolderInfo& other) = default; From 948075fa61a9a08f1ef36b059602eeaabce69a94 Mon Sep 17 00:00:00 2001 From: AMZN-nggieber <52797929+AMZN-nggieber@users.noreply.github.com> Date: Mon, 14 Jun 2021 15:56:54 -0700 Subject: [PATCH 186/233] Gem Catalog Allows Filtering by Gem Selected Status Live (#1274) --- .../Source/GemCatalog/GemCatalogScreen.cpp | 2 + .../Source/GemCatalog/GemFilterWidget.cpp | 111 ++++++++++++++++-- .../Source/GemCatalog/GemFilterWidget.h | 9 +- .../Source/GemCatalog/GemItemDelegate.cpp | 20 ---- .../Source/GemCatalog/GemListHeaderWidget.cpp | 10 ++ .../Source/GemCatalog/GemModel.cpp | 15 +++ .../Source/GemCatalog/GemModel.h | 2 + .../GemCatalog/GemSortFilterProxyModel.cpp | 23 ++++ .../GemCatalog/GemSortFilterProxyModel.h | 13 ++ .../Source/UpdateProjectCtrl.cpp | 6 +- 10 files changed, 179 insertions(+), 32 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 4424767c0b..2c92af4e51 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -82,6 +82,8 @@ namespace O3DE::ProjectManager m_headerWidget->ReinitForProject(); + connect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter); + // Select the first entry after everything got correctly sized QTimer::singleShot(200, [=]{ QModelIndex firstModelIndex = m_gemListView->model()->index(0,0); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp index a6a4e95ff9..7c6150ff99 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp @@ -26,6 +26,7 @@ namespace O3DE::ProjectManager const QVector& elementNames, const QVector& elementCounts, bool showAllLessButton, + bool collapsed, int defaultShowCount, QWidget* parent) : QWidget(parent) @@ -40,6 +41,7 @@ namespace O3DE::ProjectManager QHBoxLayout* collapseLayout = new QHBoxLayout(); m_collapseButton = new QPushButton(); m_collapseButton->setCheckable(true); + m_collapseButton->setChecked(collapsed); m_collapseButton->setFlat(true); m_collapseButton->setFocusPolicy(Qt::NoFocus); m_collapseButton->setFixedWidth(s_collapseButtonSize); @@ -178,6 +180,11 @@ namespace O3DE::ProjectManager return m_buttonGroup; } + bool FilterCategoryWidget::IsCollapsed() + { + return m_collapseButton->isChecked(); + } + GemFilterWidget::GemFilterWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent) : QScrollArea(parent) , m_filterProxyModel(filterProxyModel) @@ -193,20 +200,106 @@ namespace O3DE::ProjectManager QWidget* mainWidget = new QWidget(); setWidget(mainWidget); - m_mainLayout = new QVBoxLayout(); - m_mainLayout->setAlignment(Qt::AlignTop); - mainWidget->setLayout(m_mainLayout); + QVBoxLayout* mainLayout = new QVBoxLayout(); + mainLayout->setAlignment(Qt::AlignTop); + mainWidget->setLayout(mainLayout); QLabel* filterByLabel = new QLabel("Filter by"); filterByLabel->setStyleSheet("font-size: 16px;"); - m_mainLayout->addWidget(filterByLabel); + mainLayout->addWidget(filterByLabel); + + QWidget* filterSection = new QWidget(this); + mainLayout->addWidget(filterSection); + + m_filterLayout = new QVBoxLayout(); + m_filterLayout->setAlignment(Qt::AlignTop); + m_filterLayout->setContentsMargins(0, 0, 0, 0); + filterSection->setLayout(m_filterLayout); + ResetGemStatusFilter(); AddGemOriginFilter(); AddTypeFilter(); AddPlatformFilter(); AddFeatureFilter(); } + void GemFilterWidget::ResetGemStatusFilter() + { + QVector elementNames; + QVector elementCounts; + const int totalGems = m_gemModel->rowCount(); + const int selectedGemTotal = m_gemModel->TotalAddedGems(); + + elementNames.push_back(GemSortFilterProxyModel::GetGemStatusString(GemSortFilterProxyModel::GemStatus::Unselected)); + elementCounts.push_back(totalGems - selectedGemTotal); + + elementNames.push_back(GemSortFilterProxyModel::GetGemStatusString(GemSortFilterProxyModel::GemStatus::Selected)); + elementCounts.push_back(selectedGemTotal); + + bool wasCollapsed = false; + if (m_statusFilter) + { + wasCollapsed = m_statusFilter->IsCollapsed(); + } + + FilterCategoryWidget* filterWidget = + new FilterCategoryWidget("Status", elementNames, elementCounts, /*showAllLessButton=*/false, /*collapsed*/wasCollapsed); + if (m_statusFilter) + { + m_filterLayout->replaceWidget(m_statusFilter, filterWidget); + } + else + { + m_filterLayout->addWidget(filterWidget); + } + + m_statusFilter->deleteLater(); + m_statusFilter = filterWidget; + + const GemSortFilterProxyModel::GemStatus currentFilterState = m_filterProxyModel->GetGemStatus(); + const QList buttons = m_statusFilter->GetButtonGroup()->buttons(); + for (int statusFilterIndex = 0; statusFilterIndex < buttons.size(); ++statusFilterIndex) + { + const GemSortFilterProxyModel::GemStatus gemStatus = static_cast(statusFilterIndex); + QAbstractButton* button = buttons[statusFilterIndex]; + + if (static_cast(statusFilterIndex) == currentFilterState) + { + button->setChecked(true); + } + + connect( + button, &QAbstractButton::toggled, this, + [=](bool checked) + { + GemSortFilterProxyModel::GemStatus filterStatus = m_filterProxyModel->GetGemStatus(); + if (checked) + { + if (filterStatus == GemSortFilterProxyModel::GemStatus::NoFilter) + { + filterStatus = gemStatus; + } + else + { + filterStatus = GemSortFilterProxyModel::GemStatus::NoFilter; + } + } + else + { + if (filterStatus != gemStatus) + { + filterStatus = static_cast(!gemStatus); + } + else + { + filterStatus = GemSortFilterProxyModel::GemStatus::NoFilter; + } + } + m_filterProxyModel->SetGemStatus(filterStatus); + }); + } + } + void GemFilterWidget::AddGemOriginFilter() { QVector elementNames; @@ -233,7 +326,7 @@ namespace O3DE::ProjectManager } FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Provider", elementNames, elementCounts, /*showAllLessButton=*/false); - m_mainLayout->addWidget(filterWidget); + m_filterLayout->addWidget(filterWidget); const QList buttons = filterWidget->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) @@ -283,7 +376,7 @@ namespace O3DE::ProjectManager } FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Type", elementNames, elementCounts, /*showAllLessButton=*/false); - m_mainLayout->addWidget(filterWidget); + m_filterLayout->addWidget(filterWidget); const QList buttons = filterWidget->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) @@ -333,7 +426,7 @@ namespace O3DE::ProjectManager } FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Supported Platforms", elementNames, elementCounts, /*showAllLessButton=*/false); - m_mainLayout->addWidget(filterWidget); + m_filterLayout->addWidget(filterWidget); const QList buttons = filterWidget->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) @@ -388,8 +481,8 @@ namespace O3DE::ProjectManager } FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Features", elementNames, elementCounts, - /*showAllLessButton=*/true, /*defaultShowCount=*/5); - m_mainLayout->addWidget(filterWidget); + /*showAllLessButton=*/true, false, /*defaultShowCount=*/5); + m_filterLayout->addWidget(filterWidget); const QList buttons = filterWidget->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h index 017eadc020..520370eb44 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h @@ -37,11 +37,14 @@ namespace O3DE::ProjectManager const QVector& elementNames, const QVector& elementCounts, bool showAllLessButton = true, + bool collapsed = false, int defaultShowCount = 4, QWidget* parent = nullptr); QButtonGroup* GetButtonGroup(); + bool IsCollapsed(); + private: void UpdateCollapseState(); void UpdateSeeMoreLess(); @@ -66,14 +69,18 @@ namespace O3DE::ProjectManager explicit GemFilterWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr); ~GemFilterWidget() = default; + public slots: + void ResetGemStatusFilter(); + private: void AddGemOriginFilter(); void AddTypeFilter(); void AddPlatformFilter(); void AddFeatureFilter(); - QVBoxLayout* m_mainLayout = nullptr; + QVBoxLayout* m_filterLayout = nullptr; GemModel* m_gemModel = nullptr; GemSortFilterProxyModel* m_filterProxyModel = nullptr; + FilterCategoryWidget* m_statusFilter = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index 8aa68fb7a2..03787de7e8 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -204,7 +204,6 @@ namespace O3DE::ProjectManager painter->save(); const QRect buttonRect = CalcButtonRect(contentRect); QPoint circleCenter; - QString buttonText; const bool isAdded = GemModel::IsAdded(modelIndex); if (isAdded) @@ -213,34 +212,15 @@ namespace O3DE::ProjectManager painter->setPen(m_buttonEnabledColor); circleCenter = buttonRect.center() + QPoint(buttonRect.width() / 2 - s_buttonBorderRadius + 1, 1); - buttonText = "Added"; } else { circleCenter = buttonRect.center() + QPoint(-buttonRect.width() / 2 + s_buttonBorderRadius, 1); - buttonText = "Get"; } // Rounded rect painter->drawRoundedRect(buttonRect, s_buttonBorderRadius, s_buttonBorderRadius); - // Text - QFont font; - QRect textRect = GetTextRect(font, buttonText, s_buttonFontSize); - if (isAdded) - { - textRect = QRect(buttonRect.left(), buttonRect.top(), buttonRect.width() - s_buttonCircleRadius * 2.0, buttonRect.height()); - } - else - { - textRect = QRect(buttonRect.left() + s_buttonCircleRadius * 2.0, buttonRect.top(), buttonRect.width() - s_buttonCircleRadius * 2.0, buttonRect.height()); - } - - font.setPixelSize(s_buttonFontSize); - painter->setFont(font); - painter->setPen(m_textColor); - painter->drawText(textRect, Qt::AlignCenter, buttonText); - // Circle painter->setBrush(m_textColor); painter->drawEllipse(circleCenter, s_buttonCircleRadius, s_buttonCircleRadius); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp index bc287e3c61..ad1b57a27c 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp @@ -15,6 +15,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -74,6 +75,15 @@ namespace O3DE::ProjectManager gemSummaryLabel->setStyleSheet("font-size: 12px;"); columnHeaderLayout->addWidget(gemSummaryLabel); + QSpacerItem* horizontalSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); + columnHeaderLayout->addSpacerItem(horizontalSpacer); + + QLabel* gemSelectedLabel = new QLabel(tr("Selected")); + gemSelectedLabel->setStyleSheet("font-size: 12px;"); + columnHeaderLayout->addWidget(gemSelectedLabel); + + columnHeaderLayout->addSpacing(60); + vLayout->addLayout(columnHeaderLayout); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 6c09c95572..5dc40723c9 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -235,4 +235,19 @@ namespace O3DE::ProjectManager } return result; } + + int GemModel::TotalAddedGems() const + { + int result = 0; + for (int row = 0; row < rowCount(); ++row) + { + const QModelIndex modelIndex = index(row, 0); + if (IsAdded(modelIndex)) + { + ++result; + } + } + return result; + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 77f973a91c..2e05472cdf 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -63,6 +63,8 @@ namespace O3DE::ProjectManager QVector GatherGemsToBeAdded() const; QVector GatherGemsToBeRemoved() const; + int TotalAddedGems() const; + private: enum UserRole { diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp index d8f41c077e..c1360cabc6 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp @@ -37,6 +37,16 @@ namespace O3DE::ProjectManager return false; } + // Gem status + if (m_gemStatusFilter != GemStatus::NoFilter) + { + const GemStatus sourceGemStatus = static_cast(GemModel::IsAdded(sourceIndex)); + if (m_gemStatusFilter != sourceGemStatus) + { + return false; + } + } + // Gem origins if (m_gemOriginFilter) { @@ -125,6 +135,19 @@ namespace O3DE::ProjectManager return true; } + QString GemSortFilterProxyModel::GetGemStatusString(GemStatus status) + { + switch (status) + { + case Unselected: + return "Unselected"; + case Selected: + return "Selected"; + default: + return ""; + } + } + void GemSortFilterProxyModel::InvalidateFilter() { invalidate(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h index f24a724ecf..fcde226f40 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h @@ -29,8 +29,17 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: + enum GemStatus + { + NoFilter = -1, + Unselected, + Selected + }; + GemSortFilterProxyModel(GemModel* sourceModel, QObject* parent = nullptr); + static QString GetGemStatusString(GemStatus status); + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; GemModel* GetSourceModel() const { return m_sourceModel; } @@ -38,6 +47,9 @@ namespace O3DE::ProjectManager void SetSearchString(const QString& searchString) { m_searchString = searchString; InvalidateFilter(); } + GemStatus GetGemStatus() const { return m_gemStatusFilter; } + void SetGemStatus(GemStatus gemStatus) { m_gemStatusFilter = gemStatus; InvalidateFilter(); } + GemInfo::GemOrigins GetGemOrigins() const { return m_gemOriginFilter; } void SetGemOrigins(const GemInfo::GemOrigins& gemOrigins) { m_gemOriginFilter = gemOrigins; InvalidateFilter(); } @@ -61,6 +73,7 @@ namespace O3DE::ProjectManager AzQtComponents::SelectionProxyModel* m_selectionProxyModel = nullptr; QString m_searchString; + GemStatus m_gemStatusFilter = GemStatus::NoFilter; GemInfo::GemOrigins m_gemOriginFilter = {}; GemInfo::Platforms m_platformFilter = {}; GemInfo::Types m_typeFilter = {}; diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index be1f0e5529..65f73accd1 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -189,11 +189,13 @@ namespace O3DE::ProjectManager { if (m_stack->currentIndex() == ScreenOrder::Gems) { - m_header->setSubTitle(QString(tr("Configure Gems for \"%1\"")).arg(m_projectInfo.m_projectName)); - m_nextButton->setText(tr("Confirm")); + m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.m_projectName)); + m_header->setSubTitle(QString(tr("Configure Gems"))); + m_nextButton->setText(tr("Finalize")); } else { + m_header->setTitle(""); m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.m_projectName)); m_nextButton->setText(tr("Save")); } From ecded991b57be2db905ce4c15d7af9d2fb9c297f Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Mon, 14 Jun 2021 17:02:22 -0700 Subject: [PATCH 187/233] Display error when unable to start Python Added AzFramework Application, logging, unit tests --- Code/Framework/AzCore/AzCore/Utils/Utils.cpp | 7 + Code/Framework/AzCore/AzCore/Utils/Utils.h | 3 + Code/Sandbox/Editor/CryEdit.cpp | 2 +- Code/Tools/ProjectManager/CMakeLists.txt | 63 +++++- .../Linux/PAL_linux_tests_files.cmake | 15 ++ .../Linux/ProjectManager_Test_Traits_Linux.h | 15 ++ .../ProjectManager_Test_Traits_Platform.h | 15 ++ .../Platform/Mac/PAL_mac_tests_files.cmake | 15 ++ .../Mac/ProjectManager_Test_Traits_Mac.h | 15 ++ .../Mac/ProjectManager_Test_Traits_Platform.h | 15 ++ .../Windows/PAL_windows_tests_files.cmake | 15 ++ .../ProjectManager_Test_Traits_Platform.h | 15 ++ .../ProjectManager_Test_Traits_Windows.h | 15 ++ .../Resources/ProjectManager.qss | 5 + .../ProjectManager/Source/Application.cpp | 186 ++++++++++++++++++ .../Tools/ProjectManager/Source/Application.h | 48 +++++ .../Source/EngineSettingsScreen.cpp | 2 +- .../Source/ProjectButtonWidget.cpp | 2 +- .../Source/ProjectManagerWindow.cpp | 27 +-- .../Source/ProjectManagerWindow.h | 8 +- .../Source/ProjectSettingsScreen.cpp | 2 +- .../ProjectManager/Source/ProjectsScreen.cpp | 4 +- .../ProjectManager/Source/PythonBindings.cpp | 14 +- .../ProjectManager/Source/PythonBindings.h | 4 + .../Source/PythonBindingsInterface.h | 6 + Code/Tools/ProjectManager/Source/main.cpp | 85 ++------ .../project_manager_app_files.cmake | 17 ++ .../project_manager_files.cmake | 7 +- .../project_manager_tests_files.cmake | 17 ++ .../ProjectManager/tests/ApplicationTests.cpp | 47 +++++ Code/Tools/ProjectManager/tests/main.cpp | 35 ++++ 31 files changed, 605 insertions(+), 121 deletions(-) create mode 100644 Code/Tools/ProjectManager/Platform/Linux/PAL_linux_tests_files.cmake create mode 100644 Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Linux.h create mode 100644 Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Platform.h create mode 100644 Code/Tools/ProjectManager/Platform/Mac/PAL_mac_tests_files.cmake create mode 100644 Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Mac.h create mode 100644 Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Platform.h create mode 100644 Code/Tools/ProjectManager/Platform/Windows/PAL_windows_tests_files.cmake create mode 100644 Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Platform.h create mode 100644 Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Windows.h create mode 100644 Code/Tools/ProjectManager/Source/Application.cpp create mode 100644 Code/Tools/ProjectManager/Source/Application.h create mode 100644 Code/Tools/ProjectManager/project_manager_app_files.cmake create mode 100644 Code/Tools/ProjectManager/project_manager_tests_files.cmake create mode 100644 Code/Tools/ProjectManager/tests/ApplicationTests.cpp create mode 100644 Code/Tools/ProjectManager/tests/main.cpp diff --git a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp index 7025b3977e..e3647ec2cb 100644 --- a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp +++ b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp @@ -175,4 +175,11 @@ namespace AZ::Utils path /= ".o3de"; return path.Native(); } + + AZ::IO::FixedMaxPathString GetO3deLogsDirectory() + { + AZ::IO::FixedMaxPath path = GetO3deManifestDirectory(); + path /= "Logs"; + return path.Native(); + } } diff --git a/Code/Framework/AzCore/AzCore/Utils/Utils.h b/Code/Framework/AzCore/AzCore/Utils/Utils.h index d082a3ebe0..8fb6f05742 100644 --- a/Code/Framework/AzCore/AzCore/Utils/Utils.h +++ b/Code/Framework/AzCore/AzCore/Utils/Utils.h @@ -97,6 +97,9 @@ namespace AZ //! Retrieves the full path where the manifest file lives, i.e. "/.o3de/o3de_manifest.json" AZ::IO::FixedMaxPathString GetEngineManifestPath(); + //! Retrieves the full directory to the O3DE logs directory, i.e. "/.o3de/Logs" + AZ::IO::FixedMaxPathString GetO3deLogsDirectory(); + //! Retrieves the App root path to use on the current platform //! If the optional is not engaged the AppRootPath should be calculated based //! on the location of the bootstrap.cfg file diff --git a/Code/Sandbox/Editor/CryEdit.cpp b/Code/Sandbox/Editor/CryEdit.cpp index a972a4bd9b..67e228a3ce 100644 --- a/Code/Sandbox/Editor/CryEdit.cpp +++ b/Code/Sandbox/Editor/CryEdit.cpp @@ -2891,7 +2891,7 @@ void CCryEditApp::OpenProjectManager(const AZStd::string& screen) { // provide the current project path for in case we want to update the project AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath(); - const AZStd::string commandLineOptions = AZStd::string::format(" --screen %s --project_path %s", screen.c_str(), projectPath.c_str()); + const AZStd::string commandLineOptions = AZStd::string::format(" --screen %s --project-path %s", screen.c_str(), projectPath.c_str()); bool launchSuccess = AzFramework::ProjectManager::LaunchProjectManager(commandLineOptions); if (!launchSuccess) { diff --git a/Code/Tools/ProjectManager/CMakeLists.txt b/Code/Tools/ProjectManager/CMakeLists.txt index aeb7be9793..434ce1424e 100644 --- a/Code/Tools/ProjectManager/CMakeLists.txt +++ b/Code/Tools/ProjectManager/CMakeLists.txt @@ -20,12 +20,11 @@ if (NOT python_package_name) message(WARNING "Python was not found in the package assocation list. Did someone call ly_associate_package(xxxxxxx Python) ?") endif() + ly_add_target( - NAME ProjectManager APPLICATION - OUTPUT_NAME o3de + NAME ProjectManager.Static STATIC NAMESPACE AZ AUTOMOC - AUTORCC FILES_CMAKE project_manager_files.cmake Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake @@ -47,6 +46,60 @@ ly_add_target( 3rdParty::pybind11 AZ::AzCore AZ::AzFramework - AZ::AzToolsFramework AZ::AzQtComponents -) \ No newline at end of file +) + +ly_add_target( + NAME ProjectManager APPLICATION + OUTPUT_NAME o3de + NAMESPACE AZ + AUTORCC + FILES_CMAKE + project_manager_app_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + 3rdParty::Qt::Core + 3rdParty::Qt::Concurrent + 3rdParty::Qt::Widgets + 3rdParty::Python + 3rdParty::pybind11 + AZ::AzCore + AZ::AzFramework + AZ::AzQtComponents + AZ::ProjectManager.Static +) + +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + ly_add_target( + NAME ProjectManager.Tests EXECUTABLE + NAMESPACE AZ + AUTORCC + FILES_CMAKE + project_manager_tests_files.cmake + Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + Platform/${PAL_PLATFORM_NAME} + BUILD_DEPENDENCIES + PRIVATE + 3rdParty::Qt::Core + 3rdParty::Qt::Concurrent + 3rdParty::Qt::Widgets + 3rdParty::Python + 3rdParty::pybind11 + AZ::AzTest + AZ::AzFramework + AZ::AzFrameworkTestShared + AZ::ProjectManager.Static + ) + + ly_add_googletest( + NAME AZ::ProjectManager.Tests + TEST_COMMAND $ --unittest + ) + +endif() diff --git a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_tests_files.cmake b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_tests_files.cmake new file mode 100644 index 0000000000..e79da7183d --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_tests_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + ProjectManager_Test_Traits_Platform.h + ProjectManager_Test_Traits_Linux.h +) diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Linux.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Linux.h new file mode 100644 index 0000000000..c8b428a1c2 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Linux.h @@ -0,0 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS true diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Platform.h new file mode 100644 index 0000000000..639ef8a387 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Platform.h @@ -0,0 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include diff --git a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_tests_files.cmake b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_tests_files.cmake new file mode 100644 index 0000000000..a2d480de40 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_tests_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + ProjectManager_Test_Traits_Platform.h + ProjectManager_Test_Traits_Mac.h +) diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Mac.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Mac.h new file mode 100644 index 0000000000..053db745ea --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Mac.h @@ -0,0 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS false diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Platform.h new file mode 100644 index 0000000000..af8817998f --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Platform.h @@ -0,0 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include diff --git a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_tests_files.cmake b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_tests_files.cmake new file mode 100644 index 0000000000..00d9da3db3 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_tests_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + ProjectManager_Test_Traits_Platform.h + ProjectManager_Test_Traits_Windows.h +) diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Platform.h new file mode 100644 index 0000000000..915a86644a --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Platform.h @@ -0,0 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Windows.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Windows.h new file mode 100644 index 0000000000..053db745ea --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Windows.h @@ -0,0 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS false diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 80470591a8..efc01802b7 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -7,6 +7,11 @@ QMainWindow { margin:0; } +#ScreensCtrl { + min-width:1200px; + min-height:800px; +} + QPushButton:focus { outline: none; border:1px solid #1e70eb; diff --git a/Code/Tools/ProjectManager/Source/Application.cpp b/Code/Tools/ProjectManager/Source/Application.cpp new file mode 100644 index 0000000000..c566e76ef1 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/Application.cpp @@ -0,0 +1,186 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace O3DE::ProjectManager +{ + Application::~Application() + { + TearDown(); + } + + bool Application::Init(bool interactive) + { + constexpr const char* applicationName { "O3DE" }; + + QApplication::setOrganizationName(applicationName); + QApplication::setOrganizationDomain("o3de.org"); + + QCoreApplication::setApplicationName(applicationName); + QCoreApplication::setApplicationVersion("1.0"); + + // Use the LogComponent for non-dev logging log + RegisterComponentDescriptor(AzFramework::LogComponent::CreateDescriptor()); + + // set the log alias to .o3de/Logs instead of the default user/logs + AZ::IO::FixedMaxPath path = AZ::Utils::GetO3deLogsDirectory(); + + // DevWriteStorage is where the event log is written during development + m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::FilePathKey_DevWriteStorage, path.LexicallyNormal().Native()); + + // Save event logs to .o3de/Logs/eventlogger/EventLogO3DE.azsl + m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::BuildTargetNameKey, applicationName); + + Start(AzFramework::Application::Descriptor()); + + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + + QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); + + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); + AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware); + + // Create the actual Qt Application - this needs to happen before using QMessageBox + m_app.reset(new QApplication(*GetArgC(), *GetArgV())); + + if(!InitLog(applicationName)) + { + AZ_Warning("ProjectManager", false, "Failed to init logging"); + } + + m_pythonBindings = AZStd::make_unique(GetEngineRoot()); + if (!m_pythonBindings || !m_pythonBindings->PythonStarted()) + { + if (interactive) + { + QMessageBox::critical(nullptr, QObject::tr("Failed to start Python"), + QObject::tr("This tool requires an O3DE engine with a Python runtime, " + "but either Python is missing or mis-configured. Please rename " + "your python/runtime folder to python/runtime_bak, then run " + "python/get_python.bat to restore the Python runtime folder.")); + } + return false; + } + + const AZ::CommandLine* commandLine = GetCommandLine(); + AZ_Assert(commandLine, "Failed to get command line"); + + ProjectManagerScreen startScreen = ProjectManagerScreen::Projects; + if (size_t screenSwitchCount = commandLine->GetNumSwitchValues("screen"); screenSwitchCount > 0) + { + QString screenOption = commandLine->GetSwitchValue("screen", screenSwitchCount - 1).c_str(); + ProjectManagerScreen screen = ProjectUtils::GetProjectManagerScreen(screenOption); + if (screen != ProjectManagerScreen::Invalid) + { + startScreen = screen; + } + } + + AZ::IO::FixedMaxPath projectPath; + if (size_t projectSwitchCount = commandLine->GetNumSwitchValues("project-path"); projectSwitchCount > 0) + { + projectPath = commandLine->GetSwitchValue("project-path", projectSwitchCount - 1).c_str(); + } + + m_mainWindow.reset(new ProjectManagerWindow(nullptr, projectPath, startScreen)); + + return true; + } + + bool Application::InitLog(const char* logName) + { + if (!m_entity) + { + // override the log alias to the O3de Logs directory instead of the default project user/Logs folder + AZ::IO::FixedMaxPath path = AZ::Utils::GetO3deLogsDirectory(); + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + AZ_Assert(fileIO, "Failed to get FileIOBase instance"); + + fileIO->SetAlias("@log@", path.LexicallyNormal().Native().c_str()); + + // this entity exists because we need a home for LogComponent + // and cannot use the system entity because we need to be able to call SetLogFileBaseName + // so the log will be named O3DE.log + m_entity = aznew AZ::Entity("Application Entity"); + if (m_entity) + { + AzFramework::LogComponent* logger = aznew AzFramework::LogComponent(); + AZ_Assert(logger, "Failed to create LogComponent"); + logger->SetLogFileBaseName(logName); + m_entity->AddComponent(logger); + m_entity->Init(); + m_entity->Activate(); + } + } + + return m_entity != nullptr; + } + + void Application::TearDown() + { + if (m_entity) + { + m_entity->Deactivate(); + delete m_entity; + m_entity = nullptr; + } + + m_pythonBindings.reset(); + m_mainWindow.reset(); + m_app.reset(); + } + + bool Application::Run() + { + // Set up the Style Manager + AzQtComponents::StyleManager styleManager(qApp); + styleManager.initialize(qApp, GetEngineRoot()); + + // setup stylesheets and hot reloading + AZ::IO::FixedMaxPath engineRoot(GetEngineRoot()); + QDir rootDir(engineRoot.c_str()); + const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources"); + const auto qrcPath = QStringLiteral(":/ProjectManager/style"); + AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRoot); + + // set stylesheet after creating the main window or their styles won't get updated + AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:ProjectManager.qss")); + + // the decoration wrapper is intended to remember window positioning and sizing + auto wrapper = new AzQtComponents::WindowDecorationWrapper(); + wrapper->setGuest(m_mainWindow.data()); + wrapper->show(); + m_mainWindow->show(); + + qApp->setQuitOnLastWindowClosed(true); + + // Run the application + return qApp->exec(); + } + +} diff --git a/Code/Tools/ProjectManager/Source/Application.h b/Code/Tools/ProjectManager/Source/Application.h new file mode 100644 index 0000000000..d4e94e8dd4 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/Application.h @@ -0,0 +1,48 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include +#include +#endif + +namespace AZ +{ + class Entity; +} + +namespace O3DE::ProjectManager +{ + class Application + : public AzFramework::Application + { + public: + using AzFramework::Application::Application; + virtual ~Application(); + + bool Init(bool interactive = true); + bool Run(); + void TearDown(); + + private: + bool InitLog(const char* logName); + + AZStd::unique_ptr m_pythonBindings; + QSharedPointer m_app; + QSharedPointer m_mainWindow; + + AZ::Entity* m_entity = nullptr; + }; +} diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp index 6342041da4..cf597745ea 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp @@ -25,7 +25,7 @@ namespace O3DE::ProjectManager EngineSettingsScreen::EngineSettingsScreen(QWidget* parent) : ScreenWidget(parent) { - auto* layout = new QVBoxLayout(this); + auto* layout = new QVBoxLayout(); layout->setAlignment(Qt::AlignTop); setObjectName("engineSettingsScreen"); diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp index 761572ffa5..3bde0a310d 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp @@ -33,7 +33,7 @@ namespace O3DE::ProjectManager { setObjectName("labelButton"); - QVBoxLayout* vLayout = new QVBoxLayout(this); + QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setContentsMargins(0, 0, 0, 0); vLayout->setSpacing(5); diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp index cb1398cc61..59be0aaa35 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp @@ -13,21 +13,11 @@ #include #include -#include -#include -#include -#include -#include - -#include - namespace O3DE::ProjectManager { - ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath, const AZ::IO::PathView& projectPath, ProjectManagerScreen startScreen) + ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& projectPath, ProjectManagerScreen startScreen) : QMainWindow(parent) { - m_pythonBindings = AZStd::make_unique(engineRootPath); - setWindowTitle(tr("O3DE Project Manager")); ScreensCtrl* screensCtrl = new ScreensCtrl(); @@ -44,15 +34,6 @@ namespace O3DE::ProjectManager setCentralWidget(screensCtrl); - // setup stylesheets and hot reloading - QDir rootDir = QString::fromUtf8(engineRootPath.Native().data(), aznumeric_cast(engineRootPath.Native().size())); - const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources"); - const auto qrcPath = QStringLiteral(":/ProjectManager/style"); - AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRootPath); - - // set stylesheet after creating the screens or their styles won't get updated - AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("style:ProjectManager.qss")); - // always push the projects screen first so we have something to come back to if (startScreen != ProjectManagerScreen::Projects) { @@ -66,10 +47,4 @@ namespace O3DE::ProjectManager emit screensCtrl->NotifyCurrentProject(path); } } - - ProjectManagerWindow::~ProjectManagerWindow() - { - m_pythonBindings.reset(); - } - } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h index 758af8fc00..db2b1fd304 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h @@ -13,7 +13,7 @@ #if !defined(Q_MOC_RUN) #include -#include +#include #include #endif @@ -25,12 +25,8 @@ namespace O3DE::ProjectManager Q_OBJECT public: - explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath, const AZ::IO::PathView& projectPath, + explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& projectPath, ProjectManagerScreen startScreen = ProjectManagerScreen::Projects); - ~ProjectManagerWindow(); - - private: - AZStd::unique_ptr m_pythonBindings; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp index 26711753d4..b198724353 100644 --- a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp @@ -37,7 +37,7 @@ namespace O3DE::ProjectManager // if we don't set this in a frame (just use a sub-layout) all the content will align incorrectly horizontally QFrame* projectSettingsFrame = new QFrame(this); projectSettingsFrame->setObjectName("projectSettings"); - m_verticalLayout = new QVBoxLayout(this); + m_verticalLayout = new QVBoxLayout(); // you cannot remove content margins in qss m_verticalLayout->setContentsMargins(0, 0, 0, 0); diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index 8e41e52643..6633558406 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -85,7 +85,7 @@ namespace O3DE::ProjectManager QFrame* frame = new QFrame(this); frame->setObjectName("firstTimeContent"); { - QVBoxLayout* layout = new QVBoxLayout(this); + QVBoxLayout* layout = new QVBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->setAlignment(Qt::AlignTop); frame->setLayout(layout); @@ -100,7 +100,7 @@ namespace O3DE::ProjectManager "available by downloading our sample project.")); layout->addWidget(introLabel); - QHBoxLayout* buttonLayout = new QHBoxLayout(this); + QHBoxLayout* buttonLayout = new QHBoxLayout(); buttonLayout->setAlignment(Qt::AlignLeft); buttonLayout->setSpacing(s_spacerSize); diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index bb6c05a472..0e00319b6b 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -226,7 +226,7 @@ namespace O3DE::ProjectManager PythonBindings::PythonBindings(const AZ::IO::PathView& enginePath) : m_enginePath(enginePath) { - StartPython(); + m_pythonStarted = StartPython(); } PythonBindings::~PythonBindings() @@ -234,6 +234,11 @@ namespace O3DE::ProjectManager StopPython(); } + bool PythonBindings::PythonStarted() + { + return m_pythonStarted && Py_IsInitialized(); + } + bool PythonBindings::StartPython() { if (Py_IsInitialized()) @@ -246,7 +251,7 @@ namespace O3DE::ProjectManager AZStd::string pyBasePath = Platform::GetPythonHomePath(PY_PACKAGE, m_enginePath.c_str()); if (!AZ::IO::SystemFile::Exists(pyBasePath.c_str())) { - AZ_Assert(false, "Python home path must exist. path:%s", pyBasePath.c_str()); + AZ_Error("python", false, "Python home path does not exist: %s", pyBasePath.c_str()); return false; } @@ -351,6 +356,11 @@ namespace O3DE::ProjectManager AZ::Outcome PythonBindings::ExecuteWithLockErrorHandling(AZStd::function executionCallback) { + if (!Py_IsInitialized()) + { + return AZ::Failure("Python is not initialized"); + } + AZStd::lock_guard lock(m_lock); pybind11::gil_scoped_release release; pybind11::gil_scoped_acquire acquire; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index 5700ede850..065867a130 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -34,6 +34,8 @@ namespace O3DE::ProjectManager ~PythonBindings() override; // PythonBindings overrides + bool PythonStarted() override; + // Engine AZ::Outcome GetEngineInfo() override; bool SetEngineInfo(const EngineInfo& engineInfo) override; @@ -70,6 +72,8 @@ namespace O3DE::ProjectManager bool StopPython(); + bool m_pythonStarted = false; + AZ::IO::FixedMaxPath m_enginePath; pybind11::handle m_engineTemplate; AZStd::recursive_mutex m_lock; diff --git a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h index bc20d8e3f0..6d72bfee0c 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h +++ b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h @@ -34,6 +34,12 @@ namespace O3DE::ProjectManager IPythonBindings() = default; virtual ~IPythonBindings() = default; + /** + * Get whether Python was started or not. All Python functionality will fail if Python + * failed to start. + * @return true if Python was started successfully, false on failure + */ + virtual bool PythonStarted() = 0; // Engine diff --git a/Code/Tools/ProjectManager/Source/main.cpp b/Code/Tools/ProjectManager/Source/main.cpp index c597b8a729..1f4cadfb14 100644 --- a/Code/Tools/ProjectManager/Source/main.cpp +++ b/Code/Tools/ProjectManager/Source/main.cpp @@ -10,85 +10,26 @@ * */ -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -using namespace O3DE::ProjectManager; +#include +#include int main(int argc, char* argv[]) { - QApplication::setOrganizationName("O3DE"); - QApplication::setOrganizationDomain("o3de.org"); - QCoreApplication::setApplicationName("ProjectManager"); - QCoreApplication::setApplicationVersion("1.0"); - - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); - QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); - QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); - AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware); - - AZ::AllocatorInstance::Create(); int runSuccess = 0; - { - QApplication app(argc, argv); - - // Need to use settings registry to get EngineRootFolder - AZ::IO::FixedMaxPath engineRootPath; - { - AZ::ComponentApplication componentApplication; - auto settingsRegistry = AZ::SettingsRegistry::Get(); - settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); - } - - AzQtComponents::StyleManager styleManager(&app); - styleManager.initialize(&app, engineRootPath); - // Get the initial start screen if one is provided via command line - constexpr char optionPrefix[] = "--"; - AZ::CommandLine commandLine(optionPrefix); - commandLine.Parse(argc, argv); + // Call before using any Qt, or the app may not be able to locate Qt libs + AzQtComponents::PrepareQtPaths(); - ProjectManagerScreen startScreen = ProjectManagerScreen::Projects; - if(commandLine.HasSwitch("screen")) - { - QString screenOption = commandLine.GetSwitchValue("screen", 0).c_str(); - ProjectManagerScreen screen = ProjectUtils::GetProjectManagerScreen(screenOption); - if (screen != ProjectManagerScreen::Invalid) - { - startScreen = screen; - } - } - - AZ::IO::FixedMaxPath projectPath; - if (commandLine.HasSwitch("project-path")) - { - projectPath = commandLine.GetSwitchValue("project-path", 0).c_str(); - } - - ProjectManagerWindow window(nullptr, engineRootPath, projectPath, startScreen); - window.show(); - - // somethings is preventing us from moving the window to the center of the - // primary screen - likely an Az style or component helper - constexpr int width = 1200; - constexpr int height = 800; - window.resize(width, height); - - runSuccess = app.exec(); + O3DE::ProjectManager::Application application(&argc, &argv); + if (!application.Init()) + { + AZ_Error("ProjectManager", false, "Failed to initialize"); + runSuccess = 1; + } + else + { + runSuccess = application.Run() ? 0 : 1; } - AZ::AllocatorInstance::Destroy(); return runSuccess; } diff --git a/Code/Tools/ProjectManager/project_manager_app_files.cmake b/Code/Tools/ProjectManager/project_manager_app_files.cmake new file mode 100644 index 0000000000..223683ddd9 --- /dev/null +++ b/Code/Tools/ProjectManager/project_manager_app_files.cmake @@ -0,0 +1,17 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Resources/ProjectManager.rc + Resources/ProjectManager.qrc + Resources/ProjectManager.qss + Source/main.cpp +) diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index 633824f995..587b5907bb 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -1,4 +1,5 @@ # +# # All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or # its licensors. # @@ -10,10 +11,8 @@ # set(FILES - Resources/ProjectManager.rc - Resources/ProjectManager.qrc - Resources/ProjectManager.qss - Source/main.cpp + Source/Application.h + Source/Application.cpp Source/ScreenDefs.h Source/ScreenFactory.h Source/ScreenFactory.cpp diff --git a/Code/Tools/ProjectManager/project_manager_tests_files.cmake b/Code/Tools/ProjectManager/project_manager_tests_files.cmake new file mode 100644 index 0000000000..e1e84a43a7 --- /dev/null +++ b/Code/Tools/ProjectManager/project_manager_tests_files.cmake @@ -0,0 +1,17 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Resources/ProjectManager.qrc + Resources/ProjectManager.qss + tests/ApplicationTests.cpp + tests/main.cpp +) diff --git a/Code/Tools/ProjectManager/tests/ApplicationTests.cpp b/Code/Tools/ProjectManager/tests/ApplicationTests.cpp new file mode 100644 index 0000000000..c98b1a3a6f --- /dev/null +++ b/Code/Tools/ProjectManager/tests/ApplicationTests.cpp @@ -0,0 +1,47 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + class ProjectManagerApplicationTests + : public ::UnitTest::ScopedAllocatorSetupFixture + { + public: + + ProjectManagerApplicationTests() + { + m_application = AZStd::make_unique(); + } + + ~ProjectManagerApplicationTests() + { + m_application.reset(); + } + + AZStd::unique_ptr m_application; + }; + +#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS + TEST_F(ProjectManagerApplicationTests, DISABLED_Application_Init_Succeeds) +#else + TEST_F(ProjectManagerApplicationTests, Application_Init_Succeeds) +#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS + { + // we don't want to interact with actual GUI or display it + EXPECT_TRUE(m_application->Init(/*interactive=*/false)); + } +} diff --git a/Code/Tools/ProjectManager/tests/main.cpp b/Code/Tools/ProjectManager/tests/main.cpp new file mode 100644 index 0000000000..191bef846a --- /dev/null +++ b/Code/Tools/ProjectManager/tests/main.cpp @@ -0,0 +1,35 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +DECLARE_AZ_UNIT_TEST_MAIN(); + +int runDefaultRunner(int argc, char* argv[]) +{ + INVOKE_AZ_UNIT_TEST_MAIN(nullptr) + return 0; +} + +int main(int argc, char* argv[]) +{ + if (argc == 1) + { + // if no parameters are provided, add the --unittests parameter + constexpr int defaultArgc = 2; + char unittest_arg[] = "--unittests"; // Conversion from string literal to char* is not allowed per ISO C++11 + char* defaultArgv[defaultArgc] = { argv[0], unittest_arg }; + return runDefaultRunner(defaultArgc, defaultArgv); + } + INVOKE_AZ_UNIT_TEST_MAIN(nullptr); + return 0; +} From 5165f6ad0495cbc310f64c6294a2fe797f84f578 Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Mon, 14 Jun 2021 19:09:27 -0500 Subject: [PATCH 188/233] [ATOM-15769] Setting minimum angle to 0.5 degrees on disk lights to avoid visual artifacts that occur at values closer to 0. Also updating the outer cone angle max to be 90 degrees since it's measured from the center point, not all the way across. (#1260) --- .../Code/Source/CoreLights/EditorAreaLightComponent.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp index 69bec21a6c..992b6319d8 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp @@ -112,13 +112,13 @@ namespace AZ ->DataElement(Edit::UIHandlers::Default, &AreaLightComponentConfig::m_enableShutters, "Enable shutters", "Restrict the light to a specific beam angle depending on shape.") ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::ShuttersMustBeEnabled) ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_innerShutterAngleDegrees, "Inner angle", "The inner angle of the shutters where the light beam begins to be occluded.") - ->Attribute(Edit::Attributes::Min, 0.0f) - ->Attribute(Edit::Attributes::Max, 180.0f) + ->Attribute(Edit::Attributes::Min, 0.5f) + ->Attribute(Edit::Attributes::Max, 90.0f) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShutters) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShuttersDisabled) ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_outerShutterAngleDegrees, "Outer angle", "The outer angle of the shutters where the light beam is completely occluded.") - ->Attribute(Edit::Attributes::Min, 0.0f) - ->Attribute(Edit::Attributes::Max, 180.0f) + ->Attribute(Edit::Attributes::Min, 0.5f) + ->Attribute(Edit::Attributes::Max, 90.0f) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShutters) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShuttersDisabled) From 0198f6121ba871022667cfb31e00ca6a1fef864b Mon Sep 17 00:00:00 2001 From: rgba16f <82187279+rgba16f@users.noreply.github.com> Date: Mon, 14 Jun 2021 20:29:37 -0500 Subject: [PATCH 189/233] Rebind the DebugDisplayRequestBus Instance to handle drawing in GameMode. (#1275) --- Code/Sandbox/Editor/EditorViewportWidget.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index 7e087d452b..e627d7d7c0 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -514,18 +514,28 @@ void EditorViewportWidget::Update() // Disable rendering to avoid recursion into Update() PushDisableRendering(); + + //get debug display interface for the viewport + AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; + AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, GetViewportId()); + AZ_Assert(debugDisplayBus, "Invalid DebugDisplayRequestBus."); + + AzFramework::DebugDisplayRequests* debugDisplay = + AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); + + // draw debug visualizations - if (m_debugDisplay) + if (debugDisplay) { - const AZ::u32 prevState = m_debugDisplay->GetState(); - m_debugDisplay->SetState( + const AZ::u32 prevState = debugDisplay->GetState(); + debugDisplay->SetState( e_Mode3D | e_AlphaBlended | e_FillModeSolid | e_CullModeBack | e_DepthWriteOn | e_DepthTestOn); AzFramework::EntityDebugDisplayEventBus::Broadcast( &AzFramework::EntityDebugDisplayEvents::DisplayEntityViewport, - AzFramework::ViewportInfo{ GetViewportId() }, *m_debugDisplay); + AzFramework::ViewportInfo{ GetViewportId() }, *debugDisplay); - m_debugDisplay->SetState(prevState); + debugDisplay->SetState(prevState); } QtViewport::Update(); From de4605d6b72cd3729ae5dfab9880f70c89657c7b Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Mon, 14 Jun 2021 18:31:43 -0700 Subject: [PATCH 190/233] Added comment. --- .../ReflectionScreenSpaceCompositePass.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp index fe2030ca7e..ab09d7175a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp @@ -42,6 +42,9 @@ namespace AZ if (!passes.empty()) { Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(passes.front()); + + // compute the max mip level based on the available mips in the previous frame image, and capping it + // to stay within a range that has reasonable data const uint32_t MaxNumRoughnessMips = 8; uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1; From 1f31bac6401b72f2edbcae809be87ce0832cc1ce Mon Sep 17 00:00:00 2001 From: evanchia Date: Mon, 14 Jun 2021 20:19:19 -0700 Subject: [PATCH 191/233] fixing linux sanity tests --- Tools/LyTestTools/ly_test_tools/__init__.py | 1 + scripts/ctest/ctest_entrypoint.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Tools/LyTestTools/ly_test_tools/__init__.py b/Tools/LyTestTools/ly_test_tools/__init__.py index d534f5e0d2..fe987deb67 100755 --- a/Tools/LyTestTools/ly_test_tools/__init__.py +++ b/Tools/LyTestTools/ly_test_tools/__init__.py @@ -28,6 +28,7 @@ WINDOWS = sys.platform.startswith('win') HOST_OS_PLATFORM = 'unknown' HOST_OS_EDITOR = 'unknown' HOST_OS_DEDICATED_SERVER = 'unknown' +HOST_OS_GENERIC_EXECUTABLE = 'unknown' LAUNCHERS = {} for launcher_option in ALL_LAUNCHER_OPTIONS: LAUNCHERS[launcher_option] = None diff --git a/scripts/ctest/ctest_entrypoint.sh b/scripts/ctest/ctest_entrypoint.sh index 74e322630c..ea23ae271d 100755 --- a/scripts/ctest/ctest_entrypoint.sh +++ b/scripts/ctest/ctest_entrypoint.sh @@ -14,7 +14,7 @@ # CURRENT_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE:-0}" )" >/dev/null 2>&1 && pwd )" -DEV_DIR="$( dirname "$CURRENT_SCRIPT_DIR" )" +DEV_DIR=$( dirname "$( dirname "$CURRENT_SCRIPT_DIR" )" ) PYTHON=$DEV_DIR/python/python.sh CTEST_SCRIPT=$CURRENT_SCRIPT_DIR/ctest_driver.py From 1f6bb14ed3680cb721ec7c276b754073cc1a7498 Mon Sep 17 00:00:00 2001 From: Roman <69218254+amzn-rhhong@users.noreply.github.com> Date: Mon, 14 Jun 2021 20:38:37 -0700 Subject: [PATCH 192/233] [EMFX][ATOM] crash during mesh reload (#1301) * Fixed a crash when entering game mode, removing a mesh and re-adding mesh. --- .../Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp | 5 +++++ .../EMotionFXAtom/Code/Source/ActorAsset.cpp | 3 ++- .../EMotionFXAtom/Code/Source/AtomActorInstance.cpp | 3 +-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp index 343370ae35..a87d272448 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp @@ -629,6 +629,11 @@ namespace AZ Data::Asset lodAsset; modelLodCreator.End(lodAsset); + if (!lodAsset.IsReady()) + { + // [GFX TODO] During mesh reload the modelLodCreator could report errors and result in the lodAsset not ready. + return nullptr; + } modelCreator.AddLodAsset(AZStd::move(lodAsset)); lodIndex++; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp index 9f68a7d12c..1c4657d8df 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp @@ -311,7 +311,8 @@ namespace AZ Data::Asset modelAsset = actor->GetMeshAsset(); if (!modelAsset.IsReady()) { - AZ_Error("CreateSkinnedMeshInputFromActor", false, "Attempting to create skinned mesh input buffers for an actor that doesn't have a loaded model."); + AZ_Warning("CreateSkinnedMeshInputFromActor", false, "Check if the actor has a mesh added. Right click the source file in the asset browser, click edit settings, " + "and navigate to the Meshes tab. Add a mesh if it's missing."); return nullptr; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index 532c8720b5..18a21c93a6 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -456,9 +456,8 @@ namespace AZ void AtomActorInstance::Create() { Destroy(); - m_skinnedMeshInputBuffers = GetRenderActor()->FindOrCreateSkinnedMeshInputBuffers(); - AZ_Error("AtomActorInstance", m_skinnedMeshInputBuffers, "Failed to get SkinnedMeshInputBuffers from Actor."); + AZ_Warning("AtomActorInstance", m_skinnedMeshInputBuffers, "Failed to create SkinnedMeshInputBuffers from Actor. It is likely that this actor doesn't have any meshes"); if (m_skinnedMeshInputBuffers) { m_boneTransforms = CreateBoneTransformBufferFromActorInstance(m_actorInstance, GetSkinningMethod()); From 40cdcb63b8784e4504c377bf0330da022882a2cf Mon Sep 17 00:00:00 2001 From: John Jones-Steele Date: Tue, 15 Jun 2021 16:13:02 +0100 Subject: [PATCH 193/233] Finished work on LYN-4195 --- .../Include/Private/Editor/UI/AWSCoreEditorMenu.h | 3 +++ .../Code/Source/Editor/UI/AWSCoreEditorMenu.cpp | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h index c892f86b66..5fd01453e2 100644 --- a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h +++ b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h @@ -46,6 +46,7 @@ namespace AWSCore void InitializeAWSDocActions(); void InitializeAWSGlobalDocsSubMenu(); void InitializeAWSFeatureGemActions(); + void AddSpaceForIcon(QMenu* menu); // AWSCoreEditorRequestBus interface implementation void SetAWSClientAuthEnabled() override; @@ -55,5 +56,7 @@ namespace AWSCore // To improve experience, use process watcher to keep track of ongoing tool process AZStd::unique_ptr m_resourceMappingToolWatcher; + + const int m_sizeOfIcon = 16; }; } // namespace AWSCore diff --git a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp index 3375f85a7a..2bf387de27 100644 --- a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp +++ b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp @@ -43,6 +43,7 @@ namespace AWSCore InitializeResourceMappingToolAction(); this->addSeparator(); InitializeAWSFeatureGemActions(); + AddSpaceForIcon(this); AWSCoreEditorRequestBus::Handler::BusConnect(); } @@ -136,6 +137,8 @@ namespace AWSCore globalDocsMenu->addAction(AddExternalLinkAction(AWSAndScriptCanvasActionText, AWSAndScriptCanvasUrl, ":/Notifications/link.svg")); globalDocsMenu->addAction(AddExternalLinkAction(AWSAndComponentsActionText, AWSAndComponentsUrl, ":/Notifications/link.svg")); globalDocsMenu->addAction(AddExternalLinkAction(CallAWSResourcesActionText, CallAWSResourcesUrl, ":/Notifications/link.svg")); + + AddSpaceForIcon(globalDocsMenu); } void AWSCoreEditorMenu::InitializeAWSFeatureGemActions() @@ -170,6 +173,8 @@ namespace AWSCore AWSClientAuthPlatformSpecificActionText, AWSClientAuthPlatformSpecificUrl, ":/Notifications/link.svg")); subMenu->addAction(AddExternalLinkAction( AWSClientAuthAPIReferenceActionText, AWSClientAuthAPIReferenceUrl, ":/Notifications/link.svg")); + + AddSpaceForIcon(subMenu); } void AWSCoreEditorMenu::SetAWSMetricsEnabled() @@ -197,7 +202,9 @@ namespace AWSCore [configFilePath](){ QDesktopServices::openUrl(QUrl::fromLocalFile(configFilePath.c_str())); }); + subMenu->addAction(settingsAction); + AddSpaceForIcon(subMenu); } QMenu* AWSCoreEditorMenu::SetAWSFeatureSubMenu(const AZStd::string& menuText) @@ -217,4 +224,11 @@ namespace AWSCore } return nullptr; } + + void AWSCoreEditorMenu::AddSpaceForIcon(QMenu *menu) + { + QSize size = menu->sizeHint(); + size.setWidth(size.width() + m_sizeOfIcon); + menu->setFixedSize(size); + } } // namespace AWSCore From 74e922a4013a367269797f2948143b92c05ce5b8 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Tue, 15 Jun 2021 17:44:57 +0200 Subject: [PATCH 195/233] Compile fix in the editor actor component for release build (#1327) --- .../Integration/Editor/Components/EditorActorComponent.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index 5c085e96f7..039815fa44 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -459,8 +459,7 @@ namespace EMotionFX void EditorActorComponent::OnAssetReady(AZ::Data::Asset asset) { m_actorAsset = asset; - Actor* actor = m_actorAsset->GetActor(); - AZ_Assert(m_actorAsset.IsReady() && actor, "Actor asset should be loaded and actor valid."); + AZ_Assert(m_actorAsset.IsReady() && m_actorAsset->GetActor(), "Actor asset should be loaded and actor valid."); CheckActorCreation(); } From cdce00bf3578fda62969d9062babae698c5df7d2 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Tue, 15 Jun 2021 10:58:09 -0500 Subject: [PATCH 196/233] [ATOM-13770] [Shaders] - Root Constants need to be padded to be 16 byte (#1326) aligned. AZSLc v1.7.22 now has command line option "--pad-root-const": Automatically append padding data to the root constant CB to keep it aligned to 16-byte boundary. Signed-off-by: garrieta --- cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake | 2 +- cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index f85048d13e..3908d21ecf 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -26,7 +26,7 @@ ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) -ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform TARGETS azslc PACKAGE_HASH 772b7a2d9cc68aa1da4f0ee7db57ee1b4e7a8f20b81961fc5849af779582f4df) +ly_associate_package(PACKAGE_NAME azslc-1.7.22-rev1-multiplatform TARGETS azslc PACKAGE_HASH 71b4545d221d4fcd564ccc121c249a8f8f164bcc616faf146f926c3d5c78d527) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 1a2cfa4049..19e71f726c 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -26,7 +26,7 @@ ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) -ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform TARGETS azslc PACKAGE_HASH 772b7a2d9cc68aa1da4f0ee7db57ee1b4e7a8f20b81961fc5849af779582f4df) +ly_associate_package(PACKAGE_NAME azslc-1.7.22-rev1-multiplatform TARGETS azslc PACKAGE_HASH 71b4545d221d4fcd564ccc121c249a8f8f164bcc616faf146f926c3d5c78d527) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) From 0241538c4721cdc3ae91097fcc1c683590fd0ed9 Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Tue, 15 Jun 2021 09:19:29 -0700 Subject: [PATCH 197/233] Fix Android Startup Error related to bootstrap's project_path being in the target deployed bootstrap path (#1322) * Add '/Amazon/AzCore/Bootstrap/project_path' to setregbuilder.assetprocessor.setreg/Amazon/AssetBuilder/Excludes --- Registry/setregbuilder.assetprocessor.setreg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Registry/setregbuilder.assetprocessor.setreg b/Registry/setregbuilder.assetprocessor.setreg index 4be46a9d51..5dc6f42446 100644 --- a/Registry/setregbuilder.assetprocessor.setreg +++ b/Registry/setregbuilder.assetprocessor.setreg @@ -22,7 +22,8 @@ // members or entries will be recursively ignored as well. "Excludes": [ - "/Amazon/AzCore/Runtime" + "/Amazon/AzCore/Runtime", + "/Amazon/AzCore/Bootstrap/project_path" ] } } From baab0cb43be02f6200616ad07659aa5bec2052cc Mon Sep 17 00:00:00 2001 From: John Date: Tue, 15 Jun 2021 17:33:57 +0100 Subject: [PATCH 198/233] Disable EntityIdGeneration test --- Code/Framework/AzCore/Tests/Components.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Code/Framework/AzCore/Tests/Components.cpp b/Code/Framework/AzCore/Tests/Components.cpp index 7801f69375..1d173abbab 100644 --- a/Code/Framework/AzCore/Tests/Components.cpp +++ b/Code/Framework/AzCore/Tests/Components.cpp @@ -1555,6 +1555,8 @@ namespace UnitTest } } + // Temporary disabled. This will be re-enabled in the short term upon completion of SPEC-7384 and + // fixed in the long term upon completion of SPEC-4849 TEST_F(Components, EntityIdGeneration) { // Generate 1 million ids across 100 threads, and ensure that none collide From 58ad67dc44ac410e64965ff2f34e397e07ad0865 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 15 Jun 2021 17:44:00 +0100 Subject: [PATCH 199/233] Disable EntityIdGeneration test --- Code/Framework/AzCore/Tests/Components.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/Tests/Components.cpp b/Code/Framework/AzCore/Tests/Components.cpp index 1d173abbab..9d9bd46d3d 100644 --- a/Code/Framework/AzCore/Tests/Components.cpp +++ b/Code/Framework/AzCore/Tests/Components.cpp @@ -1557,7 +1557,7 @@ namespace UnitTest // Temporary disabled. This will be re-enabled in the short term upon completion of SPEC-7384 and // fixed in the long term upon completion of SPEC-4849 - TEST_F(Components, EntityIdGeneration) + TEST_F(Components, DISABLED_EntityIdGeneration) { // Generate 1 million ids across 100 threads, and ensure that none collide AZStd::concurrent_unordered_set entityIds; From 62f3c93c684c7cb6a594693f3849d225cba12016 Mon Sep 17 00:00:00 2001 From: Aaron Ruiz Mora Date: Tue, 15 Jun 2021 17:51:15 +0100 Subject: [PATCH 200/233] Fixed HelpPageURL links in physics components (#1328) --- Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp | 2 +- Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp | 2 +- Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp | 2 +- Gems/PhysX/Code/Source/EditorColliderComponent.cpp | 2 +- Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp | 2 +- Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp index 873ef7248d..0133690cea 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp @@ -44,7 +44,7 @@ namespace Blast ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute( AZ::Edit::Attributes::HelpPageURL, - "https://docs.aws.amazon.com/lumberyard/latest/userguide/component-blast-actor.html") + "https://docs.o3de.org/docs/user-guide/components/reference/blast-family/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement( AZ::Edit::UIHandlers::Default, &EditorBlastFamilyComponent::m_blastAsset, "Blast asset", diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp index 77788b6aee..4a6f41331b 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp @@ -67,7 +67,7 @@ namespace Blast ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute( AZ::Edit::Attributes::HelpPageURL, - "https://docs.aws.amazon.com/lumberyard/latest/userguide/component-blast-actor.html") + "https://docs.o3de.org/docs/user-guide/components/reference/blast-family-mesh-data/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement( AZ::Edit::UIHandlers::CheckBox, &EditorBlastMeshDataComponent::m_showMeshAssets, diff --git a/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp b/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp index 7da677a7eb..7eb07f46d7 100644 --- a/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp +++ b/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp @@ -52,7 +52,7 @@ namespace NvCloth ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Cloth.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Cloth.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.aws.amazon.com/lumberyard/latest/userguide/component-cloth.html") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.o3de.org/docs/user-guide/components/reference/cloth/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->UIElement(AZ::Edit::UIHandlers::CheckBox, "Simulate in editor", diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index 0671e3e77e..3cb931d148 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -194,7 +194,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCollider.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/PhysXCollider.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "http://docs.aws.amazon.com/console/lumberyard/component/physx/collider") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.o3de.org/docs/user-guide/components/reference/physx-collider/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_configuration, "Collider Configuration", "Configuration of the collider") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) diff --git a/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp b/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp index 770200cda0..b783ea3185 100644 --- a/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp @@ -176,7 +176,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/ForceRegion.png") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/ForceRegion.png") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.aws.amazon.com/console/lumberyard/physx/force-region") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.o3de.org/docs/user-guide/components/reference/physx-force-region/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::RequiredService, AZ_CRC("PhysXTriggerService", 0x3a117d7b)) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_visibleInEditor, "Visible", "Always show the component in viewport") diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp index f68c4d17d8..588b1d918f 100644 --- a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp @@ -309,7 +309,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/PhysXRigidBody.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.aws.amazon.com/console/lumberyard/components/physx/rigid-body") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.o3de.org/docs/user-guide/components/reference/physx-rigid-body-physics/") ->DataElement(0, &EditorRigidBodyComponent::m_config, "Configuration", "Configuration for rigid body physics.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorRigidBodyComponent::CreateEditorWorldRigidBody) From 04bf6c3689391a8e02df2617cc10bce2de517ee1 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 15 Jun 2021 10:14:02 -0700 Subject: [PATCH 201/233] Separate out session validation into its own packet --- .../AutoGen/Multiplayer.AutoPackets.xml | 5 +- .../Source/MultiplayerSystemComponent.cpp | 51 +++++++++++-------- .../Code/Source/MultiplayerSystemComponent.h | 1 + 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml index ce8931107f..3abd2a86d9 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml @@ -9,13 +9,16 @@ - + + + + diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index e4256a875c..dc86a07945 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -213,6 +213,8 @@ namespace Multiplayer reinterpret_cast(connection->GetUserData())->SetProviderTicket(config.m_playerSessionId); } + connection->SendReliablePacket(MultiplayerPackets::ValidateSession(config.m_playerSessionId.c_str())); + return true; } @@ -414,22 +416,6 @@ namespace Multiplayer [[maybe_unused]] MultiplayerPackets::Connect& packet ) { - // Validate our session with the provider if any - if (AZ::Interface::Get() != nullptr) - { - AzFramework::PlayerConnectionConfig config; - config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); - config.m_playerSessionId = packet.GetTicket(); - if(!AZ::Interface::Get()->ValidatePlayerJoinSession(config)) - { - auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); }; - m_networkInterface->GetConnectionSet().VisitConnections(visitor); - return true; - } - - reinterpret_cast(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str()); - } - if (connection->SendReliablePacket(MultiplayerPackets::Accept(InvalidHostId, sv_map))) { // Sync our console @@ -455,6 +441,32 @@ namespace Multiplayer return true; } + bool MultiplayerSystemComponent::HandleRequest + ( + [[maybe_unused]] AzNetworking::IConnection* connection, + [[maybe_unused]] const IPacketHeader& packetHeader, + [[maybe_unused]] MultiplayerPackets::ValidateSession& packet + ) + { + // Validate our session with the provider if any + if (AZ::Interface::Get() != nullptr) + { + AzFramework::PlayerConnectionConfig config; + config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); + config.m_playerSessionId = packet.GetTicket(); + if(!AZ::Interface::Get()->ValidatePlayerJoinSession(config)) + { + auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); }; + m_networkInterface->GetConnectionSet().VisitConnections(visitor); + return false; + } + + reinterpret_cast(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str()); + } + + return true; + } + bool MultiplayerSystemComponent::HandleRequest ( AzNetworking::IConnection* connection, @@ -587,12 +599,7 @@ namespace Multiplayer if (connection->GetConnectionRole() == ConnectionRole::Connector) { AZLOG_INFO("New outgoing connection to remote address: %s", connection->GetRemoteAddress().GetString().c_str()); - AZ::CVarFixedString providerTicket; - if (connection->GetUserData() != nullptr) - { - providerTicket = reinterpret_cast(connection->GetUserData())->GetProviderTicket(); - } - connection->SendReliablePacket(MultiplayerPackets::Connect(0, providerTicket)); + connection->SendReliablePacket(MultiplayerPackets::Connect(0)); } else { diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 0efef3ebe4..2a3fe73ff4 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -82,6 +82,7 @@ namespace Multiplayer bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Connect& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Accept& packet); + bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ValidateSession& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::SyncConsole& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ConsoleCommand& packet); From aade48e7513bd91cc7e71f678ee3cb286702ceec Mon Sep 17 00:00:00 2001 From: yuriy0 Date: Tue, 15 Jun 2021 13:36:35 -0400 Subject: [PATCH 202/233] Allow smaller values and increments for editor camera speed, allow custom speed values --- Code/Sandbox/Editor/ViewportTitleDlg.cpp | 3 ++- Code/Sandbox/Editor/ViewportTitleDlg.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Code/Sandbox/Editor/ViewportTitleDlg.cpp b/Code/Sandbox/Editor/ViewportTitleDlg.cpp index 48075597cd..2c4b29773d 100644 --- a/Code/Sandbox/Editor/ViewportTitleDlg.cpp +++ b/Code/Sandbox/Editor/ViewportTitleDlg.cpp @@ -181,7 +181,8 @@ void CViewportTitleDlg::SetupCameraDropdownMenu() auto comboBoxTextChanged = static_cast(&QComboBox::currentTextChanged); SetSpeedComboBox(cameraMoveSpeed); - m_cameraSpeed->setInsertPolicy(QComboBox::NoInsert); + m_cameraSpeed->setInsertPolicy(QComboBox::InsertAtBottom); + m_cameraSpeed->setDuplicatesEnabled(false); connect(m_cameraSpeed, comboBoxTextChanged, this, &CViewportTitleDlg::OnUpdateMoveSpeedText); connect(m_cameraSpeed->lineEdit(), &QLineEdit::returnPressed, this, &CViewportTitleDlg::OnSpeedComboBoxEnter); diff --git a/Code/Sandbox/Editor/ViewportTitleDlg.h b/Code/Sandbox/Editor/ViewportTitleDlg.h index 255354dcbb..7978277ce0 100644 --- a/Code/Sandbox/Editor/ViewportTitleDlg.h +++ b/Code/Sandbox/Editor/ViewportTitleDlg.h @@ -117,11 +117,11 @@ protected: // Speed combobox/lineEdit settings double m_minSpeed = 0.01; double m_maxSpeed = 100.0; - double m_speedStep = 0.01; + double m_speedStep = 0.001; int m_numDecimals = 3; // Speed presets - float m_speedPresetValues[3] = { 0.1f, 1.0f, 10.0f }; + float m_speedPresetValues[4] = { 0.01f, 0.1f, 1.0f, 10.0f }; double m_fieldWidthMultiplier = 1.8; From 2f5cffe67969a4cc2902ac49c9d278ab4153f93b Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Tue, 15 Jun 2021 13:46:53 -0400 Subject: [PATCH 203/233] Added r_ViewportPos cvar for set the position of a viewport of a game/server launchers --- Code/LauncherUnified/Launcher.cpp | 5 +++++ .../Platform/Android/Launcher_Android.cpp | 2 ++ .../LauncherUnified/Platform/Linux/Launcher_Linux.cpp | 2 ++ Code/LauncherUnified/Platform/Mac/Launcher_Mac.mm | 2 ++ .../Platform/Windows/Launcher_Windows.cpp | 11 +++++++++++ Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm | 2 ++ 6 files changed, 24 insertions(+) diff --git a/Code/LauncherUnified/Launcher.cpp b/Code/LauncherUnified/Launcher.cpp index c73cdd1981..48747b50a7 100644 --- a/Code/LauncherUnified/Launcher.cpp +++ b/Code/LauncherUnified/Launcher.cpp @@ -46,6 +46,8 @@ extern "C" void CreateStaticModules(AZStd::vector& modulesOut); # define REMOTE_ASSET_PROCESSOR #endif +void CVar_OnViewportPosition(const AZ::Vector2& value); + namespace { void OnViewportResize(const AZ::Vector2& value); @@ -60,6 +62,9 @@ namespace AzFramework::WindowSize newSize = AzFramework::WindowSize(aznumeric_cast(value.GetX()), aznumeric_cast(value.GetY())); AzFramework::WindowRequestBus::Broadcast(&AzFramework::WindowRequestBus::Events::ResizeClientArea, newSize); } + + AZ_CVAR(AZ::Vector2, r_viewportPos, AZ::Vector2::CreateZero(), CVar_OnViewportPosition, AZ::ConsoleFunctorFlags::DontReplicate, + "The default position for the launcher viewport, 0 0 means top left corner of your main desktop"); void ExecuteConsoleCommandFile(AzFramework::Application& application) { diff --git a/Code/LauncherUnified/Platform/Android/Launcher_Android.cpp b/Code/LauncherUnified/Platform/Android/Launcher_Android.cpp index 6455373e58..fa26b9d204 100644 --- a/Code/LauncherUnified/Platform/Android/Launcher_Android.cpp +++ b/Code/LauncherUnified/Platform/Android/Launcher_Android.cpp @@ -433,3 +433,5 @@ void android_main(android_app* appState) MAIN_EXIT_FAILURE(appState, GetReturnCodeString(status)); } } + +void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {} diff --git a/Code/LauncherUnified/Platform/Linux/Launcher_Linux.cpp b/Code/LauncherUnified/Platform/Linux/Launcher_Linux.cpp index 0c422877b1..3d283ce77f 100644 --- a/Code/LauncherUnified/Platform/Linux/Launcher_Linux.cpp +++ b/Code/LauncherUnified/Platform/Linux/Launcher_Linux.cpp @@ -113,3 +113,5 @@ int main(int argc, char** argv) return static_cast(status); } + +void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {} diff --git a/Code/LauncherUnified/Platform/Mac/Launcher_Mac.mm b/Code/LauncherUnified/Platform/Mac/Launcher_Mac.mm index 1a491d66ad..2886bb16b1 100644 --- a/Code/LauncherUnified/Platform/Mac/Launcher_Mac.mm +++ b/Code/LauncherUnified/Platform/Mac/Launcher_Mac.mm @@ -63,3 +63,5 @@ int main(int argc, char* argv[]) } #endif // AZ_TESTS_ENABLED + +void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {} diff --git a/Code/LauncherUnified/Platform/Windows/Launcher_Windows.cpp b/Code/LauncherUnified/Platform/Windows/Launcher_Windows.cpp index 8f8d310475..494779dcf8 100644 --- a/Code/LauncherUnified/Platform/Windows/Launcher_Windows.cpp +++ b/Code/LauncherUnified/Platform/Windows/Launcher_Windows.cpp @@ -69,3 +69,14 @@ int APIENTRY WinMain([[maybe_unused]] HINSTANCE hInstance, [[maybe_unused]] HINS return static_cast(status); } + +void CVar_OnViewportPosition(const AZ::Vector2& value) +{ + if (HWND windowHandle = GetActiveWindow()) + { + SetWindowPos(windowHandle, nullptr, + value.GetX(), + value.GetY(), + 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE); + } +} diff --git a/Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm b/Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm index 52c616fe64..05857842a7 100644 --- a/Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm +++ b/Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm @@ -23,3 +23,5 @@ int main(int argc, char* argv[]) [pool release]; return 0; } + +void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {} From a446d397c62f81e7d6ade5655b673fcdae2b8baa Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Tue, 15 Jun 2021 14:05:35 -0400 Subject: [PATCH 204/233] Minor name refactoring --- Code/LauncherUnified/Launcher.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Code/LauncherUnified/Launcher.cpp b/Code/LauncherUnified/Launcher.cpp index 48747b50a7..09a9e2f990 100644 --- a/Code/LauncherUnified/Launcher.cpp +++ b/Code/LauncherUnified/Launcher.cpp @@ -50,12 +50,12 @@ void CVar_OnViewportPosition(const AZ::Vector2& value); namespace { - void OnViewportResize(const AZ::Vector2& value); + void CVar_OnViewportResize(const AZ::Vector2& value); - AZ_CVAR(AZ::Vector2, r_viewportSize, AZ::Vector2::CreateZero(), OnViewportResize, AZ::ConsoleFunctorFlags::DontReplicate, + AZ_CVAR(AZ::Vector2, r_viewportSize, AZ::Vector2::CreateZero(), CVar_OnViewportResize, AZ::ConsoleFunctorFlags::DontReplicate, "The default size for the launcher viewport, 0 0 means full screen"); - void OnViewportResize(const AZ::Vector2& value) + void CVar_OnViewportResize(const AZ::Vector2& value) { AzFramework::NativeWindowHandle windowHandle = nullptr; AzFramework::WindowSystemRequestBus::BroadcastResult(windowHandle, &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle); From adc09435f48ac2b6adfbfb6b288b9ca9d0134ff4 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 15 Jun 2021 11:08:59 -0700 Subject: [PATCH 205/233] Cleanup validation failure logic --- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index dc86a07945..aa5e0d7467 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -456,8 +456,7 @@ namespace Multiplayer config.m_playerSessionId = packet.GetTicket(); if(!AZ::Interface::Get()->ValidatePlayerJoinSession(config)) { - auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); }; - m_networkInterface->GetConnectionSet().VisitConnections(visitor); + connection->Disconnect(DisconnectReason::TerminatedByServer, TerminationEndpoint::Local); return false; } From a9c55c1070dff2fbfd7c0c5d5afc03a40362bca2 Mon Sep 17 00:00:00 2001 From: yuriy0 Date: Tue, 15 Jun 2021 14:09:30 -0400 Subject: [PATCH 206/233] Update actor render bounding box (#991) * Extend MeshFeatureProcessor to allow changing the mesh bbox, which requires re-compute the culling data for that mesh * Update actor mesh bbox when EMFX actor instance bbox changes. Also use the actor instance global bbox to compute the local bbox for the skinned render mesh, instead of the using the static bounds based bbox, as not every actor instance is going to be using the static bounds bbox. * Store per-instance mesh AABB in the right place. In the MeshInstanceData, which is unique per instance, instead of in the Model, which is shared between all instances. For greater clarity, also remove Model::m_aabb and the corresponding getter and setter, as it isn't immediately obvious whether this gets the model asset bbox or the mesh instance bbox. Callers should instead be explicit about which bbox they want. * Bug fix: model asset is not necessarily ready in AcquireMesh * Remove now-unused forward declaration * Update MockMeshFeatureProcessor with SetLocalAabb/GetLocalAabb --- .../Atom/Feature/Mesh/MeshFeatureProcessor.h | 5 ++++ .../Mesh/MeshFeatureProcessorInterface.h | 4 +++ .../Code/Mocks/MockMeshFeatureProcessor.h | 2 ++ .../Code/Source/Mesh/MeshFeatureProcessor.cpp | 30 +++++++++++++++++-- .../Include/Atom/RPI.Public/Model/Model.h | 5 +--- .../Code/Source/RPI.Public/Model/Model.cpp | 16 +++++----- .../Source/RPI.Public/Model/ModelLodUtils.cpp | 2 +- .../Source/Mesh/MeshComponentController.cpp | 6 ++-- .../Code/Source/AtomActorInstance.cpp | 12 ++++++-- 9 files changed, 62 insertions(+), 20 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h index 0d61ef82d1..24c0658c61 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h @@ -95,6 +95,8 @@ namespace AZ TransformServiceFeatureProcessorInterface::ObjectId m_objectId; + Aabb m_aabb = Aabb::CreateNull(); + bool m_cullBoundsNeedsUpdate = false; bool m_cullableNeedsRebuild = false; bool m_objectSrgNeedsUpdate = true; @@ -160,6 +162,9 @@ namespace AZ Transform GetTransform(const MeshHandle& meshHandle) override; Vector3 GetNonUniformScale(const MeshHandle& meshHandle) override; + void SetLocalAabb(const MeshHandle& meshHandle, const AZ::Aabb& localAabb) override; + AZ::Aabb GetLocalAabb(const MeshHandle& meshHandle) const override; + void SetSortKey(const MeshHandle& meshHandle, RHI::DrawItemSortKey sortKey) override; RHI::DrawItemSortKey GetSortKey(const MeshHandle& meshHandle) override; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h index fb5bff5584..fdc6ea3cc8 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h @@ -86,6 +86,10 @@ namespace AZ virtual Transform GetTransform(const MeshHandle& meshHandle) = 0; //! Gets the non-uniform scale for a given mesh handle. virtual Vector3 GetNonUniformScale(const MeshHandle& meshHandle) = 0; + //! Sets the local space bbox for a given mesh handle. You don't need to call this for static models, only skinned/animated models + virtual void SetLocalAabb(const MeshHandle& meshHandle, const AZ::Aabb& localAabb) = 0; + //! Gets the local space bbox for a given mesh handle. Unless SetLocalAabb has been called before, this will be the bbox of the model asset + virtual AZ::Aabb GetLocalAabb(const MeshHandle& meshHandle) const = 0; //! Sets the sort key for a given mesh handle. virtual void SetSortKey(const MeshHandle& meshHandle, RHI::DrawItemSortKey sortKey) = 0; //! Gets the sort key for a given mesh handle. diff --git a/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h index 418ee0cfb8..e6635906e9 100644 --- a/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h @@ -33,6 +33,8 @@ namespace UnitTest MOCK_METHOD2(SetMaterialAssignmentMap, void(const MeshHandle&, const AZ::Render::MaterialAssignmentMap&)); MOCK_METHOD1(GetTransform, AZ::Transform(const MeshHandle&)); MOCK_METHOD1(GetNonUniformScale, AZ::Vector3(const MeshHandle&)); + MOCK_METHOD2(SetLocalAabb, void(const MeshHandle&, const AZ::Aabb&)); + MOCK_CONST_METHOD1(GetLocalAabb, AZ::Aabb(const MeshHandle&)); MOCK_METHOD2(SetSortKey, void (const MeshHandle&, AZ::RHI::DrawItemSortKey)); MOCK_METHOD1(GetSortKey, AZ::RHI::DrawItemSortKey(const MeshHandle&)); MOCK_METHOD2(SetLodOverride, void(const MeshHandle&, AZ::RPI::Cullable::LodOverride)); diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index b087269089..6fe6cf1f88 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -304,6 +304,30 @@ namespace AZ } } + void MeshFeatureProcessor::SetLocalAabb(const MeshHandle& meshHandle, const AZ::Aabb& localAabb) + { + if (meshHandle.IsValid()) + { + MeshDataInstance& meshData = *meshHandle; + meshData.m_aabb = localAabb; + meshData.m_cullBoundsNeedsUpdate = true; + meshData.m_objectSrgNeedsUpdate = true; + } + }; + + AZ::Aabb MeshFeatureProcessor::GetLocalAabb(const MeshHandle& meshHandle) const + { + if (meshHandle.IsValid()) + { + return meshHandle->m_aabb; + } + else + { + AZ_Assert(false, "Invalid mesh handle"); + return Aabb::CreateNull(); + } + } + Transform MeshFeatureProcessor::GetTransform(const MeshHandle& meshHandle) { if (meshHandle.IsValid()) @@ -603,6 +627,8 @@ namespace AZ SetRayTracingData(); } + m_aabb = model->GetModelAsset()->GetAabb(); + m_cullableNeedsRebuild = true; m_cullBoundsNeedsUpdate = true; m_objectSrgNeedsUpdate = true; @@ -996,7 +1022,7 @@ namespace AZ RPI::Cullable::CullData& cullData = m_cullable.m_cullData; RPI::Cullable::LodData& lodData = m_cullable.m_lodData; - const Aabb& localAabb = m_model->GetAabb(); + const Aabb& localAabb = m_aabb; lodData.m_lodSelectionRadius = 0.5f*localAabb.GetExtents().GetMaxElement(); const size_t modelLodCount = m_model->GetLodCount(); @@ -1077,7 +1103,7 @@ namespace AZ Vector3 center; float radius; - Aabb localAabb = m_model->GetAabb(); + Aabb localAabb = m_aabb; localAabb.MultiplyByScale(nonUniformScale); localAabb.GetTransformedAabb(localToWorld).GetAsSphere(center, radius); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h index 514e3e37a5..b403a86f00 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h @@ -32,6 +32,7 @@ namespace AZ : public Data::InstanceData { friend class ModelSystem; + public: AZ_INSTANCE_DATA(Model, "{C30F5522-B381-4B38-BBAF-6E0B1885C8B9}"); AZ_CLASS_ALLOCATOR(Model, AZ::SystemAllocator, 0); @@ -53,8 +54,6 @@ namespace AZ //! Returns whether a buffer upload is pending. bool IsUploadPending() const; - const AZ::Aabb& GetAabb() const; - const Data::Asset& GetModelAsset() const; //! Checks a ray for intersection against this model. The ray must be in the same coordinate space as the model. @@ -105,8 +104,6 @@ namespace AZ // Tracks whether buffers have all been streamed up to the GPU. bool m_isUploadPending = false; - - AZ::Aabb m_aabb; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp index 17ff2c64c8..8809225150 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp @@ -62,8 +62,6 @@ namespace AZ { AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); - m_aabb = modelAsset.GetAabb(); - m_lods.resize(modelAsset.GetLodAssets().size()); for (size_t lodIndex = 0; lodIndex < m_lods.size(); ++lodIndex) @@ -127,11 +125,6 @@ namespace AZ return m_isUploadPending; } - const AZ::Aabb& Model::GetAabb() const - { - return m_aabb; - } - const Data::Asset& Model::GetModelAsset() const { return m_modelAsset; @@ -140,9 +133,16 @@ namespace AZ bool Model::LocalRayIntersection(const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, float& distanceNormalized, AZ::Vector3& normal) const { AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); + + if (!GetModelAsset()) + { + AZ_Assert(false, "Invalid Model - not created from a ModelAsset?"); + return false; + } + float start; float end; - const int result = Intersect::IntersectRayAABB2(rayStart, rayDir.GetReciprocal(), m_aabb, start, end); + const int result = Intersect::IntersectRayAABB2(rayStart, rayDir.GetReciprocal(), GetModelAsset()->GetAabb(), start, end); if (Intersect::ISECT_RAY_AABB_NONE != result) { if (ModelAsset* modelAssetPtr = m_modelAsset.Get()) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp index b213e84bca..a4e5d1ba10 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp @@ -49,7 +49,7 @@ namespace AZ With that percentage we can determine which Lod we want to use. */ - Aabb modelAabb = model.GetAabb(); + Aabb modelAabb = model.GetModelAsset()->GetAabb(); modelAabb.Translate(position); Vector3 center; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index e7eecd3c7f..90d3763067 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -460,10 +460,10 @@ namespace AZ Aabb MeshComponentController::GetLocalBounds() { - const Data::Instance model = GetModel(); - if (model) + if (m_meshHandle.IsValid() && m_meshFeatureProcessor) { - Aabb aabb = model->GetAabb(); + Aabb aabb = m_meshFeatureProcessor->GetLocalAabb(m_meshHandle); + aabb.MultiplyByScale(m_cachedNonUniformScale); return aabb; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index 532c8720b5..ff4b82ce92 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -83,12 +83,20 @@ namespace AZ void AtomActorInstance::UpdateBounds() { // Update RenderActorInstance world bounding box - // The bounding box is moving with the actor instance. It is static in the way that it does not change shape. + // The bounding box is moving with the actor instance. // The entity and actor transforms are kept in sync already. m_worldAABB = AZ::Aabb::CreateFromMinMax(m_actorInstance->GetAABB().GetMin(), m_actorInstance->GetAABB().GetMax()); // Update RenderActorInstance local bounding box - m_localAABB = AZ::Aabb::CreateFromMinMax(m_actorInstance->GetStaticBasedAABB().GetMin(), m_actorInstance->GetStaticBasedAABB().GetMax()); + // NB: computing the local bbox from the world bbox makes the local bbox artifically larger than it should be + // instead EMFX should support getting the local bbox from the actor instance directly + m_localAABB = m_worldAABB.GetTransformedAabb(m_transformInterface->GetWorldTM().GetInverse()); + + // Update bbox on mesh instance if it exists + if (m_meshFeatureProcessor && m_meshHandle && m_meshHandle->IsValid() && m_skinnedMeshInstance) + { + m_meshFeatureProcessor->SetLocalAabb(*m_meshHandle, m_localAABB); + } AZ::Interface::Get()->RefreshEntityLocalBoundsUnion(m_entityId); } From cac210f25bc7b071903e38a9f15265e4c605c006 Mon Sep 17 00:00:00 2001 From: jromnoa Date: Tue, 15 Jun 2021 11:28:58 -0700 Subject: [PATCH 207/233] add check for files in a given path before creating full paths to the files, add output when json.loads() call fails --- scripts/build/tools/upload_to_s3.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/build/tools/upload_to_s3.py b/scripts/build/tools/upload_to_s3.py index 132929d83e..3694014056 100755 --- a/scripts/build/tools/upload_to_s3.py +++ b/scripts/build/tools/upload_to_s3.py @@ -84,12 +84,14 @@ def get_files_to_upload(base_dir, regex, search_subdirectories): # ('C:\path\to\base_dir\', ['Subfolder1', 'Subfolder2'], ['file1', 'file2']) subdirectory_file_path = subdirectory[0] subdirectory_files = subdirectory[2] - subdirectory_file_paths = _build_file_paths(subdirectory_file_path, subdirectory_files) - files.extend(subdirectory_file_paths) + if subdirectory_files: + subdirectory_file_paths = _build_file_paths(subdirectory_file_path, subdirectory_files) + files.extend(subdirectory_file_paths) try: regex = json.loads(regex) # strip the surround quotes, if they exist except: + print(f'WARNING: failed to call json.loads() for regex: "{regex}"') pass # Get all file names matching the regular expression, those file will be uploaded to S3 regex_files_to_upload = [x for x in files if re.match(regex, x)] From 04f045bfd2cc592629860fb621f540e181ee5978 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Tue, 15 Jun 2021 15:58:16 -0400 Subject: [PATCH 208/233] Recoding parts of old Networking that used Bullet external physics code --- .../Replica/Interest/BvDynamicTree.cpp | 1277 ------------ .../GridMate/Replica/Interest/BvDynamicTree.h | 878 -------- .../Interest/ProximityInterestHandler.cpp | 597 ------ .../Interest/ProximityInterestHandler.h | 314 --- .../GridMate/GridMate/gridmate_files.cmake | 4 - Code/Framework/GridMate/Tests/Interest.cpp | 1823 ----------------- .../GridMate/Tests/gridmate_test_files.cmake | 1 - 7 files changed, 4894 deletions(-) delete mode 100644 Code/Framework/GridMate/GridMate/Replica/Interest/BvDynamicTree.cpp delete mode 100644 Code/Framework/GridMate/GridMate/Replica/Interest/BvDynamicTree.h delete mode 100644 Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.cpp delete mode 100644 Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.h delete mode 100644 Code/Framework/GridMate/Tests/Interest.cpp diff --git a/Code/Framework/GridMate/GridMate/Replica/Interest/BvDynamicTree.cpp b/Code/Framework/GridMate/GridMate/Replica/Interest/BvDynamicTree.cpp deleted file mode 100644 index e72bd9b12c..0000000000 --- a/Code/Framework/GridMate/GridMate/Replica/Interest/BvDynamicTree.cpp +++ /dev/null @@ -1,1277 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -// Modifications copyright Amazon.com, Inc. or its affiliates. - -///BvDynamicTree implementation by Nathanael Presson -#include - -namespace GridMate -{ - // - struct btDbvtNodeEnumerator : BvDynamicTree::ICollideCollector - { - BvDynamicTree::ConstNodeArrayType nodes; - void Process(const BvDynamicTree::NodeType* n) { nodes.push_back(n); } - }; - - // - static AZ_FORCE_INLINE int indexof(const BvDynamicTree::NodeType* node) - { - return (node->m_parent->m_childs[1]==node); - } - - // - static AZ_FORCE_INLINE BvDynamicTree::VolumeType merge( const BvDynamicTree::VolumeType& a, const BvDynamicTree::VolumeType& b) - { - BvDynamicTree::VolumeType res; - Merge(a,b,res); - return res; - } - - // volume+edge lengths - static AZ_FORCE_INLINE float size(const BvDynamicTree::VolumeType& a) - { - const AZ::Vector3 edges = a.GetExtents(); - return edges.GetX()*edges.GetY()*edges.GetZ() + edges.Dot(AZ::Vector3::CreateOne()); - } - - // - static void getmaxdepth(const BvDynamicTree::NodeType* node,int depth,int& maxdepth) - { - if(node->IsInternal()) - { - getmaxdepth(node->m_childs[0],depth+1,maxdepth); - getmaxdepth(node->m_childs[0],depth+1,maxdepth); - } - else - maxdepth= AZ::GetMax(maxdepth,depth); - } - - - - //========================================================================= - // insertleaf - // [3/4/2009] - //========================================================================= - void - BvDynamicTree::insertleaf( NodeType* root, NodeType* leaf) - { - if(!m_root) - { - m_root = leaf; - leaf->m_parent = 0; - } - else - { - if(!root->IsLeaf()) - { - do { - root=root->m_childs[Select( leaf->m_volume, - root->m_childs[0]->m_volume, - root->m_childs[1]->m_volume)]; - } while(!root->IsLeaf()); - } - NodeType* prev = root->m_parent; - NodeType* node = createnode(prev,leaf->m_volume,root->m_volume,0); - if(prev) - { - prev->m_childs[indexof(root)] = node; - node->m_childs[0] = root;root->m_parent=node; - node->m_childs[1] = leaf;leaf->m_parent=node; - do { - if(!prev->m_volume.Contains(node->m_volume)) - Merge(prev->m_childs[0]->m_volume,prev->m_childs[1]->m_volume,prev->m_volume); - else - break; - node=prev; - } while(0!=(prev=node->m_parent)); - } - else - { - node->m_childs[0] = root;root->m_parent=node; - node->m_childs[1] = leaf;leaf->m_parent=node; - m_root = node; - } - } - } - - //========================================================================= - // removeleaf - // [3/4/2009] - //========================================================================= - BvDynamicTree::NodeType* - BvDynamicTree::removeleaf( NodeType* leaf) - { - if(leaf==m_root) - { - m_root=0; - return 0; - } - else - { - NodeType* parent=leaf->m_parent; - NodeType* prev=parent->m_parent; - NodeType* sibling=parent->m_childs[1-indexof(leaf)]; - if(prev) - { - prev->m_childs[indexof(parent)]=sibling; - sibling->m_parent=prev; - deletenode(parent); - while(prev) - { - const VolumeType pb=prev->m_volume; - Merge(prev->m_childs[0]->m_volume,prev->m_childs[1]->m_volume,prev->m_volume); - if(NotEqual(pb,prev->m_volume)) - { - prev=prev->m_parent; - } else break; - } - return prev?prev:m_root; - } - else - { - m_root=sibling; - sibling->m_parent=0; - deletenode(parent); - return m_root; - } - } - } - - - //========================================================================= - // fetchleaves - // [3/4/2009] - //========================================================================= - void - BvDynamicTree::fetchleaves(NodeType* root,NodeArrayType& leaves,int depth) - { - if(root->IsInternal()&&depth) - { - fetchleaves(root->m_childs[0],leaves,depth-1); - fetchleaves(root->m_childs[1],leaves,depth-1); - deletenode(root); - } - else - { - leaves.push_back(root); - } - } - - //========================================================================= - // split - // [3/4/2009] - //========================================================================= - void - BvDynamicTree::split(const NodeArrayType& leaves, NodeArrayType& left, NodeArrayType& right, const AZ::Vector3& org, const AZ::Vector3& axis) - { - left.resize(0); - right.resize(0); - for(size_t i = 0, ni = leaves.size(); i < ni; ++i) - { - if (axis.Dot(leaves[i]->m_volume.GetCenter() - org) < 0.0f) - { - left.push_back(leaves[i]); - } - else - { - right.push_back(leaves[i]); - } - } - } - - //========================================================================= - // bounds - // [3/4/2009] - //========================================================================= - BvDynamicTree::VolumeType - BvDynamicTree::bounds(const NodeArrayType& leaves) - { - VolumeType volume=leaves[0]->m_volume; - for(size_t i=1,ni=leaves.size();im_volume,volume); - } - return volume; - } - - //========================================================================= - // bottomup - // [3/4/2009] - //========================================================================= - void - BvDynamicTree::bottomup( NodeArrayType& leaves ) - { - while(leaves.size()>1) - { - float minsize = std::numeric_limits::max(); - int minidx[2]={-1,-1}; - for(unsigned int i=0;im_volume,leaves[j]->m_volume)); - if(szm_volume,n[1]->m_volume,0); - p->m_childs[0] = n[0]; - p->m_childs[1] = n[1]; - n[0]->m_parent = p; - n[1]->m_parent = p; - leaves[minidx[0]] = p; - //leaves.swap(minidx[1],leaves.size()-1); - leaves[minidx[1]] = leaves.back(); - leaves.pop_back(); - } - } - - //========================================================================= - // topdown - // [3/4/2009] - //========================================================================= - BvDynamicTree::NodeType* - BvDynamicTree::topdown(NodeArrayType& leaves,int bu_treshold) - { - static const AZ::Vector3 axis[]= { AZ::Vector3(1.0f,0.0f,0.0f), AZ::Vector3(0.0f,1.0f,0.0f), AZ::Vector3(0.0f,0.0f,1.0f)}; - if(leaves.size()>1) - { - if(leaves.size()>(unsigned int)bu_treshold) - { - const VolumeType vol=bounds(leaves); - const AZ::Vector3 org=vol.GetCenter(); - NodeArrayType sets[2]; - int bestaxis=-1; - int bestmidp=(int)leaves.size(); - int splitcount[3][2]={{0,0},{0,0},{0,0}}; - - for(unsigned int i=0;im_volume.GetCenter()-org; - for(int j=0;j<3;++j) - { - ++splitcount[j][x.Dot(axis[j]) > 0.0f ? 1 : 0]; - } - } - for(unsigned int i=0;i<3;++i) - { - if((splitcount[i][0]>0)&&(splitcount[i][1]>0)) - { - // todo just remove the sign bit... - const int midp = (int)fabsf((float)(splitcount[i][0]-splitcount[i][1])); - if(midp=0) - { - sets[0].reserve(splitcount[bestaxis][0]); - sets[1].reserve(splitcount[bestaxis][1]); - split(leaves,sets[0],sets[1],org,axis[bestaxis]); - } - else - { - sets[0].reserve(leaves.size()/2+1); - sets[1].reserve(leaves.size()/2); - for(size_t i=0,ni=leaves.size();im_childs[0] = topdown(sets[0],bu_treshold); - node->m_childs[1] = topdown(sets[1],bu_treshold); - node->m_childs[0]->m_parent=node; - node->m_childs[1]->m_parent=node; - return(node); - } - else - { - bottomup(leaves); - return(leaves[0]); - } - } - return(leaves[0]); - } - - //========================================================================= - // sort - // [3/4/2009] - //========================================================================= - AZ_FORCE_INLINE BvDynamicTree::NodeType* - BvDynamicTree::sort(NodeType* n,NodeType*& r) - { - BvDynamicTree::NodeType* p=n->m_parent; - AZ_Assert(n->IsInternal(), "We can call this only for internal nodes!"); - if(p>n) - { - const int i=indexof(n); - const int j=1-i; - NodeType* s=p->m_childs[j]; - NodeType* q=p->m_parent; - AZ_Assert(n==p->m_childs[i], ""); - if(q) q->m_childs[indexof(p)]=n; else r=n; - s->m_parent=n; - p->m_parent=n; - n->m_parent=q; - p->m_childs[0]=n->m_childs[0]; - p->m_childs[1]=n->m_childs[1]; - n->m_childs[0]->m_parent=p; - n->m_childs[1]->m_parent=p; - n->m_childs[i]=p; - n->m_childs[j]=s; - AZStd::swap(p->m_volume,n->m_volume); - return(p); - } - return(n); - } - - #if 0 - static DBVT_INLINE NodeType* walkup(NodeType* n,int count) - { - while(n&&(count--)) n=n->parent; - return(n); - } - #endif - - // - // Api - // - - // - BvDynamicTree::BvDynamicTree() - { - m_root = 0; - m_free = 0; - m_lkhd = -1; - m_leaves = 0; - m_opath = 0; - } - - // - BvDynamicTree::~BvDynamicTree() - { - Clear(); - } - - // - void BvDynamicTree::Clear() - { - if(m_root) recursedeletenode(m_root); - delete m_free; - m_free=0; - } - - // - void BvDynamicTree::OptimizeBottomUp() - { - if(m_root) - { - NodeArrayType leaves; - leaves.reserve(m_leaves); - fetchleaves(m_root,leaves); - bottomup(leaves); - m_root=leaves[0]; - } - } - - // - void - BvDynamicTree::OptimizeTopDown(int bu_treshold) - { - if(m_root) - { - NodeArrayType leaves; - leaves.reserve(m_leaves); - fetchleaves(m_root,leaves); - m_root=topdown(leaves,bu_treshold); - } - } - - // - void - BvDynamicTree::OptimizeIncremental(int passes) - { - if(passes<0) passes=m_leaves; - if(m_root&&(passes>0)) - { - do { - NodeType* node=m_root; - unsigned bit=0; - while(node->IsInternal()) - { - node=sort(node,m_root)->m_childs[(m_opath>>bit)&1]; - bit=(bit+1)&(sizeof(unsigned)*8-1); - } - Update(node); - ++m_opath; - } while(--passes); - } - } - - // - BvDynamicTree::NodeType* - BvDynamicTree::Insert(const VolumeType& volume,void* data) - { - NodeType* leaf=createnode(0,volume,data); - insertleaf(m_root,leaf); - ++m_leaves; - return(leaf); - } - - // - void - BvDynamicTree::Update(NodeType* leaf,int lookahead) - { - NodeType* root=removeleaf(leaf); - if(root) - { - if(lookahead>=0) - { - for(int i=0;(im_parent;++i) - { - root=root->m_parent; - } - } else root=m_root; - } - insertleaf(root,leaf); - } - - // - void - BvDynamicTree::Update(NodeType* leaf,VolumeType& volume) - { - NodeType* root=removeleaf(leaf); - if(root) - { - if(m_lkhd>=0) - { - for(int i=0;(im_parent;++i) - { - root=root->m_parent; - } - } else root=m_root; - } - leaf->m_volume=volume; - insertleaf(root,leaf); - } - - // - bool - BvDynamicTree::Update(NodeType* leaf,VolumeType& volume,const AZ::Vector3& velocity,const float margin) - { - if(leaf->m_volume.Contains(volume)) return(false); - volume.Expand(AZ::Vector3(margin)); - volume.SignedExpand(velocity); - Update(leaf,volume); - return(true); - } - - // - bool - BvDynamicTree::Update(NodeType* leaf,VolumeType& volume,const AZ::Vector3& velocity) - { - if(leaf->m_volume.Contains(volume)) return(false); - volume.SignedExpand(velocity); - Update(leaf,volume); - return(true); - } - - // - bool - BvDynamicTree::Update(NodeType* leaf,VolumeType& volume,const float margin) - { - if(leaf->m_volume.Contains(volume)) return(false); - volume.Expand(AZ::Vector3(margin)); - Update(leaf,volume); - return(true); - } - - // - void - BvDynamicTree::Remove(NodeType* leaf) - { - removeleaf(leaf); - deletenode(leaf); - --m_leaves; - } - - template - int findLinearSearch(BvDynamicTree::ConstNodeArrayType& arr, const T& key) - { - size_t numElements = arr.size(); - int index = (int)numElements; - - for(size_t i=0;iPrepare(m_root,(unsigned int)nodes.nodes.size()); - for(unsigned int i=0;i<(unsigned int)nodes.nodes.size();++i) - { - const NodeType* n=nodes.nodes[i]; - int p=-1; - if(n->m_parent) p = findLinearSearch(nodes.nodes,n->m_parent); - if(n->IsInternal()) - { - const int c0=findLinearSearch(nodes.nodes,n->m_childs[0]); - const int c1=findLinearSearch(nodes.nodes,n->m_childs[1]); - iwriter->WriteNode(n,i,p,c0,c1); - } - else - { - iwriter->WriteLeaf(n,i,p); - } - } - } - - // - void - BvDynamicTree::Clone(BvDynamicTree& dest,IClone* iclone) const - { - dest.Clear(); - if(m_root!=0) - { - vector stack; - stack.reserve(m_leaves); - stack.push_back(sStkCLN(m_root,0)); - do { - const size_t i=stack.size()-1; - const sStkCLN e=stack[i]; - NodeType* n= dest.createnode(e.parent,e.node->m_volume,e.node->m_data); - stack.pop_back(); - if(e.parent!=0) - e.parent->m_childs[i&1]=n; - else - dest.m_root=n; - if(e.node->IsInternal()) - { - stack.push_back(sStkCLN(e.node->m_childs[0],n)); - stack.push_back(sStkCLN(e.node->m_childs[1],n)); - } - else - { - iclone->CloneLeaf(n); - } - } while(!stack.empty()); - } - } - - // - int - BvDynamicTree::GetMaxDepth(const NodeType* node) - { - int depth=0; - if(node) getmaxdepth(node,1,depth); - return depth ; - } - - // - int - BvDynamicTree::CountLeaves(const NodeType* node) - { - if(node->IsInternal()) - return(CountLeaves(node->m_childs[0])+CountLeaves(node->m_childs[1])); - else - return(1); - } - - // - void - BvDynamicTree::ExtractLeaves(const NodeType* node,vector& leaves) - { - if(node->IsInternal()) - { - ExtractLeaves(node->m_childs[0],leaves); - ExtractLeaves(node->m_childs[1],leaves); - } - else - { - leaves.push_back(node); - } - } - - // - #if DBVT_ENABLE_BENCHMARK - - #include - #include - #include - - /* - q6600,2.4ghz - - /Ox /Ob2 /Oi /Ot /I "." /I "..\.." /I "..\..\src" /D "NDEBUG" /D "_LIB" /D "_WINDOWS" /D "_CRT_SECURE_NO_DEPRECATE" /D "_CRT_NONSTDC_NO_DEPRECATE" /D "WIN32" - /GF /FD /MT /GS- /Gy /arch:SSE2 /Zc:wchar_t- /Fp"..\..\out\release8\build\libbulletcollision\libbulletcollision.pch" - /Fo"..\..\out\release8\build\libbulletcollision\\" - /Fd"..\..\out\release8\build\libbulletcollision\bulletcollision.pdb" - /W3 /nologo /c /Wp64 /Zi /errorReport:prompt - - Benchmarking dbvt... - World scale: 100.000000 - Extents base: 1.000000 - Extents range: 4.000000 - Leaves: 8192 - sizeof(VolumeType): 32 bytes - sizeof(NodeType): 44 bytes - [1] VolumeType intersections: 3499 ms (-1%) - [2] VolumeType merges: 1934 ms (0%) - [3] BvDynamicTree::collideTT: 5485 ms (-21%) - [4] BvDynamicTree::collideTT self: 2814 ms (-20%) - [5] BvDynamicTree::collideTT xform: 7379 ms (-1%) - [6] BvDynamicTree::collideTT xform,self: 7270 ms (-2%) - [7] BvDynamicTree::rayTest: 6314 ms (0%),(332143 r/s) - [8] insert/remove: 2093 ms (0%),(1001983 ir/s) - [9] updates (teleport): 1879 ms (-3%),(1116100 u/s) - [10] updates (jitter): 1244 ms (-4%),(1685813 u/s) - [11] optimize (incremental): 2514 ms (0%),(1668000 o/s) - [12] VolumeType notequal: 3659 ms (0%) - [13] culling(OCL+fullsort): 2218 ms (0%),(461 t/s) - [14] culling(OCL+qsort): 3688 ms (5%),(2221 t/s) - [15] culling(KDOP+qsort): 1139 ms (-1%),(7192 t/s) - [16] insert/remove batch(256): 5092 ms (0%),(823704 bir/s) - [17] VolumeType select: 3419 ms (0%) - */ - - struct btDbvtBenchmark - { - struct NilPolicy : BvDynamicTree::ICollide - { - NilPolicy() : m_pcount(0),m_depth(-SIMD_INFINITY),m_checksort(true) {} - void Process(const NodeType*,const NodeType*) { ++m_pcount; } - void Process(const NodeType*) { ++m_pcount; } - void Process(const NodeType*,btScalar depth) - { - ++m_pcount; - if(m_checksort) - { if(depth>=m_depth) m_depth=depth; else printf("wrong depth: %f (should be >= %f)\r\n",depth,m_depth); } - } - int m_pcount; - btScalar m_depth; - bool m_checksort; - }; - struct P14 : BvDynamicTree::ICollide - { - struct Node - { - const NodeType* leaf; - btScalar depth; - }; - void Process(const NodeType* leaf,btScalar depth) - { - Node n; - n.leaf = leaf; - n.depth = depth; - } - static int sortfnc(const Node& a,const Node& b) - { - if(a.depthb.depth) return(-1); - return(0); - } - btAlignedObjectArray m_nodes; - }; - struct P15 : BvDynamicTree::ICollide - { - struct Node - { - const NodeType* leaf; - btScalar depth; - }; - void Process(const NodeType* leaf) - { - Node n; - n.leaf = leaf; - n.depth = dot(leaf->volume.GetCenter(),m_axis); - } - static int sortfnc(const Node& a,const Node& b) - { - if(a.depthb.depth) return(-1); - return(0); - } - btAlignedObjectArray m_nodes; - btAZ::Vector3 m_axis; - }; - static btScalar RandUnit() - { - return(rand()/(btScalar)RAND_MAX); - } - static btAZ::Vector3 RandAZ::Vector3() - { - return(btAZ::Vector3(RandUnit(),RandUnit(),RandUnit())); - } - static btAZ::Vector3 RandAZ::Vector3(btScalar cs) - { - return(RandAZ::Vector3()*cs-btAZ::Vector3(cs,cs,cs)/2); - } - static VolumeType RandVolume(btScalar cs,btScalar eb,btScalar es) - { - return(VolumeType::FromCE(RandAZ::Vector3(cs),btAZ::Vector3(eb,eb,eb)+RandAZ::Vector3()*es)); - } - static btTransform RandTransform(btScalar cs) - { - btTransform t; - t.setOrigin(RandAZ::Vector3(cs)); - t.setRotation(btQuaternion(RandUnit()*SIMD_PI*2,RandUnit()*SIMD_PI*2,RandUnit()*SIMD_PI*2).normalized()); - return(t); - } - static void RandTree(btScalar cs,btScalar eb,btScalar es,int leaves,BvDynamicTree& dbvt) - { - dbvt.clear(); - for(int i=0;i volumes; - btAlignedObjectArray results; - volumes.resize(cfgLeaves); - results.resize(cfgLeaves); - for(int i=0;i volumes; - btAlignedObjectArray results; - volumes.resize(cfgLeaves); - results.resize(cfgLeaves); - for(int i=0;i transforms; - btDbvtBenchmark::NilPolicy policy; - transforms.resize(cfgBenchmark5_Iterations); - for(int i=0;i transforms; - btDbvtBenchmark::NilPolicy policy; - transforms.resize(cfgBenchmark6_Iterations); - for(int i=0;i rayorg; - btAlignedObjectArray raydir; - btDbvtBenchmark::NilPolicy policy; - rayorg.resize(cfgBenchmark7_Iterations); - raydir.resize(cfgBenchmark7_Iterations); - for(int i=0;i leaves; - btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt); - dbvt.optimizeTopDown(); - dbvt.extractLeaves(dbvt.m_root,leaves); - printf("[9] updates (teleport): "); - wallclock.reset(); - for(int i=0;i(leaves[rand()%cfgLeaves]), - btDbvtBenchmark::RandVolume(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale)); - } - } - const int time=(int)wallclock.getTimeMilliseconds(); - const int up=cfgBenchmark9_Passes*cfgBenchmark9_Iterations; - printf("%u ms (%i%%),(%u u/s)\r\n",time,(time-cfgBenchmark9_Reference)*100/time,up*1000/time); - } - if(cfgBenchmark10_Enable) - {// Benchmark 10 - srand(380843); - BvDynamicTree dbvt; - btAlignedObjectArray leaves; - btAlignedObjectArray vectors; - vectors.resize(cfgBenchmark10_Iterations); - for(int i=0;i(leaves[rand()%cfgLeaves]); - VolumeType v=VolumeType::FromMM(l->volume.GetMin()+d,l->volume.GetMax()+d); - dbvt.update(l,v); - } - } - const int time=(int)wallclock.getTimeMilliseconds(); - const int up=cfgBenchmark10_Passes*cfgBenchmark10_Iterations; - printf("%u ms (%i%%),(%u u/s)\r\n",time,(time-cfgBenchmark10_Reference)*100/time,up*1000/time); - } - if(cfgBenchmark11_Enable) - {// Benchmark 11 - srand(380843); - BvDynamicTree dbvt; - btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt); - dbvt.optimizeTopDown(); - printf("[11] optimize (incremental): "); - wallclock.reset(); - for(int i=0;i volumes; - btAlignedObjectArray results; - volumes.resize(cfgLeaves); - results.resize(cfgLeaves); - for(int i=0;i vectors; - btDbvtBenchmark::NilPolicy policy; - vectors.resize(cfgBenchmark13_Iterations); - for(int i=0;i vectors; - btDbvtBenchmark::P14 policy; - vectors.resize(cfgBenchmark14_Iterations); - for(int i=0;i vectors; - btDbvtBenchmark::P15 policy; - vectors.resize(cfgBenchmark15_Iterations); - for(int i=0;i batch; - btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt); - dbvt.optimizeTopDown(); - batch.reserve(cfgBenchmark16_BatchCount); - printf("[16] Insert/remove batch(%u): ",cfgBenchmark16_BatchCount); - wallclock.reset(); - for(int i=0;i volumes; - btAlignedObjectArray results; - btAlignedObjectArray indices; - volumes.resize(cfgLeaves); - results.resize(cfgLeaves); - indices.resize(cfgLeaves); - for(int i=0;i -#include -#include - -#include -#include - -namespace GridMate -{ - namespace Internal - { - /** - * - */ - class DynamicTreeAabb : public AZ::Aabb - { - public: - GM_CLASS_ALLOCATOR(DynamicTreeAabb); - - AZ_FORCE_INLINE explicit DynamicTreeAabb() {} - AZ_FORCE_INLINE DynamicTreeAabb(const AZ::Aabb& aabb) : AZ::Aabb(aabb) {} - AZ_FORCE_INLINE explicit DynamicTreeAabb(const AZ::Vector3& min,const AZ::Vector3& max) : AZ::Aabb(AZ::Aabb::CreateFromMinMax(min,max)) {} - - AZ_FORCE_INLINE static DynamicTreeAabb CreateFromFacePoints(const AZ::Vector3& a, const AZ::Vector3& b, const AZ::Vector3& c) - { - DynamicTreeAabb vol(a,a); - vol.AddPoint(b); - vol.AddPoint(c); - return vol; - } - - AZ_FORCE_INLINE void SignedExpand(const AZ::Vector3& e) - { - AZ::Vector3 zero = AZ::Vector3::CreateZero(); - AZ::Vector3 mxE = m_max + e; - AZ::Vector3 miE = m_min + e; - m_max = AZ::Vector3::CreateSelectCmpGreater(e,zero,mxE,m_max ); - m_min = AZ::Vector3::CreateSelectCmpGreater(e,zero,m_min,miE); - } - AZ_FORCE_INLINE int Classify(const AZ::Vector3& n,const float o,int s) const - { - AZ::Vector3 pi, px; - switch(s) - { - case (0+0+0): px=m_min; - pi=m_max; break; - case (1+0+0): px=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_min.GetZ()); - pi=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_max.GetZ());break; - case (0+2+0): px=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_min.GetZ()); - pi=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_max.GetZ());break; - case (1+2+0): px=AZ::Vector3(m_max.GetX(),m_max.GetY(),m_min.GetZ()); - pi=AZ::Vector3(m_min.GetX(),m_min.GetY(),m_max.GetZ());break; - case (0+0+4): px=AZ::Vector3(m_min.GetX(),m_min.GetY(),m_max.GetZ()); - pi=AZ::Vector3(m_max.GetX(),m_max.GetY(),m_min.GetZ());break; - case (1+0+4): px=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_max.GetZ()); - pi=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_min.GetZ());break; - case (0+2+4): px=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_max.GetZ()); - pi=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_min.GetZ());break; - case (1+2+4): px=m_max; - pi=m_min;break; - } - - if (n.Dot(px) + o < 0.0f) - { - return -1; - } - if (n.Dot(pi) + o > 0.0f) - { - return 1; - } - - return 0; - } - AZ_FORCE_INLINE float ProjectMinimum(const AZ::Vector3& v, unsigned signs) const - { - const AZ::Vector3* b[]={&m_max,&m_min}; - const AZ::Vector3 p( b[(signs>>0)&1]->GetX(),b[(signs>>1)&1]->GetY(),b[(signs>>2)&1]->GetZ()); - return p.Dot(v); - } - - // Move the code here - AZ_FORCE_INLINE friend bool IntersectAabbAabb(const DynamicTreeAabb& a,const DynamicTreeAabb& b); - AZ_FORCE_INLINE friend bool IntersectAabbPoint(const DynamicTreeAabb& a, const AZ::Vector3& b); - AZ_FORCE_INLINE friend bool IntersectAabbPlane(const DynamicTreeAabb& a, const AZ::Plane& b); - AZ_FORCE_INLINE friend float Proximity(const DynamicTreeAabb& a, const DynamicTreeAabb& b); - AZ_FORCE_INLINE friend int Select(const DynamicTreeAabb& o, const DynamicTreeAabb& a, const DynamicTreeAabb& b); - AZ_FORCE_INLINE friend void Merge(const DynamicTreeAabb& a, const DynamicTreeAabb& b, DynamicTreeAabb& r); - AZ_FORCE_INLINE friend bool NotEqual(const DynamicTreeAabb& a, const DynamicTreeAabb& b); - private: - AZ_FORCE_INLINE void AddSpan(const AZ::Vector3& d, float& smi, float& smx) const - { - AZ::Vector3 vecZero = AZ::Vector3::CreateZero(); - AZ::Vector3 mxD = m_max*d; - AZ::Vector3 miD = m_min*d; - AZ::Vector3 smiAdd = AZ::Vector3::CreateSelectCmpGreater(vecZero,d,mxD,miD); - AZ::Vector3 smxAdd = AZ::Vector3::CreateSelectCmpGreater(vecZero,d,miD,mxD); - AZ::Vector3 vecOne = AZ::Vector3::CreateOne(); - // sum components - smi += smiAdd.Dot(vecOne); - smx += smxAdd.Dot(vecOne); - } - }; - - // - AZ_FORCE_INLINE bool IntersectAabbAabb(const DynamicTreeAabb& a, const DynamicTreeAabb& b) - { - return a.Overlaps(b); - } - - AZ_FORCE_INLINE bool IntersectAabbPlane(const DynamicTreeAabb& a, const AZ::Plane& b) - { - //use plane normal to quickly select the nearest corner of the aabb - AZ::Vector3 testPoint = AZ::Vector3::CreateSelectCmpGreater(b.GetNormal(), AZ::Vector3::CreateZero(), a.GetMin(), a.GetMax()); - //test if nearest point is inside the plane - return b.GetPointDist(testPoint) <= 0.0f; - } - - // - AZ_FORCE_INLINE float Proximity(const DynamicTreeAabb& a, const DynamicTreeAabb& b) - { - const AZ::Vector3 d=(a.m_min+a.m_max)-(b.m_min+b.m_max); - // get abs and sum - return d.GetAbs().Dot(AZ::Vector3::CreateOne()); - } - - // - AZ_FORCE_INLINE int Select( const DynamicTreeAabb& o, const DynamicTreeAabb& a, const DynamicTreeAabb& b) - { - return Proximity(o,a) < Proximity(o,b); - } - - // - AZ_FORCE_INLINE void Merge(const DynamicTreeAabb& a, const DynamicTreeAabb& b, DynamicTreeAabb& r) - { - r.m_min = AZ::Vector3::CreateSelectCmpGreater(b.m_min,a.m_min,a.m_min,b.m_min); - r.m_max = AZ::Vector3::CreateSelectCmpGreater(a.m_max,b.m_max,a.m_max,b.m_max); - } - - // - AZ_FORCE_INLINE bool NotEqual( const DynamicTreeAabb& a, const DynamicTreeAabb& b) - { - return (a.m_min != b.m_min || a.m_max != b.m_max); - } - - - /* NodeType */ - struct DynamicTreeNode - { - GM_CLASS_ALLOCATOR(DynamicTreeNode); - - DynamicTreeAabb m_volume; - DynamicTreeNode* m_parent; - AZ_FORCE_INLINE bool IsLeaf() const { return(m_childs[1]==0); } - AZ_FORCE_INLINE bool IsInternal() const { return(!IsLeaf()); } - union - { - DynamicTreeNode* m_childs[2]; - void* m_data; - int m_dataAsInt; - }; - }; - } - - /** - * Implementation of dynamic aabb tree, based on the bullet dynamic tree (btDbvt). - * - * The BvDynamicTree class implements a fast dynamic bounding volume tree based on axis aligned bounding boxes (aabb tree). - * This BvDynamicTree is used for soft body collision detection and for the btDbvtBroadphase. It has a fast insert, remove and update of nodes. - * Unlike the BvTreeQuantized, nodes can be dynamically moved around, which allows for change in topology of the underlying data structure. - */ - class BvDynamicTree - { - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(BvDynamicTree); - - typedef Internal::DynamicTreeAabb VolumeType; - typedef Internal::DynamicTreeNode NodeType; - - typedef vector NodeArrayType; - typedef vector ConstNodeArrayType; - - private: - - /* Stack element */ - struct sStkNN - { - const NodeType* a; - const NodeType* b; - sStkNN() {} - sStkNN(const NodeType* na,const NodeType* nb) : a(na), b(nb) {} - }; - struct sStkNP - { - const NodeType* node; - int mask; - sStkNP(const NodeType* n, unsigned m) : node(n), mask(m) {} - }; - struct sStkNPS - { - const NodeType* node; - int mask; - float value; - sStkNPS() {} - sStkNPS(const NodeType* n, unsigned m, const float v) : node(n), mask(m), value(v) {} - }; - struct sStkCLN - { - const NodeType* node; - NodeType* parent; - sStkCLN(const NodeType* n, NodeType* p) : node(n), parent(p) {} - }; - - public: - /* ICollideCollector templated collectors should implement this functions or inherit from this class */ - struct ICollideCollector - { - void Process(const NodeType*, const NodeType*) {} - void Process(const NodeType*) {} - void Process(const NodeType* n, const float) { Process(n); } - bool Descent(const NodeType*) { return true; } - bool AllLeaves(const NodeType*) { return true; } - }; - - /* IWriter */ - struct IWriter - { - virtual ~IWriter() {} - virtual void Prepare(const NodeType* root,int numnodes) = 0; - virtual void WriteNode(const NodeType*, int index, int parent, int child0, int child1) = 0; - virtual void WriteLeaf(const NodeType*, int index, int parent) = 0; - }; - /* IClone */ - struct IClone - { - virtual ~IClone() {} - virtual void CloneLeaf(NodeType*) {} - }; - - // Constants - enum - { - SIMPLE_STACKSIZE = 64, - DOUBLE_STACKSIZE = SIMPLE_STACKSIZE * 2 - }; - - // Methods - BvDynamicTree(); - ~BvDynamicTree(); - - NodeType* GetRoot() const { return m_root; } - void Clear(); - bool Empty() const { return 0 == m_root; } - int GetNumLeaves() const { return m_leaves; } - void OptimizeBottomUp(); - void OptimizeTopDown(int bu_treshold = 128); - void OptimizeIncremental(int passes); - NodeType* Insert(const VolumeType& box,void* data); - void Update(NodeType* leaf, int lookahead=-1); - void Update(NodeType* leaf, VolumeType& volume); - bool Update(NodeType* leaf, VolumeType& volume, const AZ::Vector3& velocity, const float margin); - bool Update(NodeType* leaf, VolumeType& volume, const AZ::Vector3& velocity); - bool Update(NodeType* leaf, VolumeType& volume, const float margin); - void Remove(NodeType* leaf); - void Write(IWriter* iwriter) const; - void Clone(BvDynamicTree& dest, IClone* iclone=0) const; - static int GetMaxDepth(const NodeType* node); - static int CountLeaves(const NodeType* node); - static void ExtractLeaves(const NodeType* node, /*btAlignedObjectArray&*/vector& leaves); - #if DBVT_ENABLE_BENCHMARK - static void Benchmark(); - #else - static void Benchmark(){} - #endif - /** - * Collector should inherit from ICollide - */ - template - static inline void enumNodes( const NodeType* root, Collector& collector) - { - collector.Process(root); - if(root->IsInternal()) - { - enumNodes(root->m_childs[0],collector); - enumNodes(root->m_childs[1],collector); - } - } - template - static void enumLeaves( const NodeType* root,Collector& collector) - { - if(root->IsInternal()) - { - enumLeaves(root->m_childs[0],collector); - enumLeaves(root->m_childs[1],collector); - } - else - { - collector.Process(root); - } - } - template - void collideTT( const NodeType* root0,const NodeType* root1,Collector& collector) const - { - if(root0&&root1) - { - size_t depth=1; - size_t treshold=DOUBLE_STACKSIZE-4; - vector stkStack; - stkStack.resize(DOUBLE_STACKSIZE); - stkStack[0]=sStkNN(root0,root1); - do { - sStkNN p=stkStack[--depth]; - if(depth>treshold) - { - stkStack.resize(stkStack.size()*2); - treshold=stkStack.size()-4; - } - if(p.a==p.b) - { - if(p.a->IsInternal()) - { - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[0]); - stkStack[depth++]=sStkNN(p.a->m_childs[1],p.a->m_childs[1]); - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[1]); - } - } - else if(IntersectAabbAabb(p.a->m_volume,p.b->m_volume)) - { - if(p.a->IsInternal()) - { - if(p.b->IsInternal()) - { - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[0]); - stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[0]); - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[1]); - stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[1]); - } - else - { - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b); - stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b); - } - } - else - { - if(p.b->IsInternal()) - { - stkStack[depth++]=sStkNN(p.a,p.b->m_childs[0]); - stkStack[depth++]=sStkNN(p.a,p.b->m_childs[1]); - } - else - { - collector.Process(p.a,p.b); - } - } - } - } while(depth); - } - } - template - void collideTTpersistentStack( const NodeType* root0, const NodeType* root1,Collector& collector) - { - if(root0&&root1) - { - size_t depth=1; - size_t treshold=DOUBLE_STACKSIZE-4; - - m_stkStack.resize(DOUBLE_STACKSIZE); - m_stkStack[0]=sStkNN(root0,root1); - do - { - sStkNN p=m_stkStack[--depth]; - if(depth>treshold) - { - m_stkStack.resize(m_stkStack.size()*2); - treshold=m_stkStack.size()-4; - } - if(p.a==p.b) - { - if(p.a->IsInternal()) - { - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[0]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.a->m_childs[1]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[1]); - } - } - else if(IntersectAabbAabb(p.a->m_volume,p.b->m_volume)) - { - if(p.a->IsInternal()) - { - if(p.b->IsInternal()) - { - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[0]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[0]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[1]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[1]); - } - else - { - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b); - m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b); - } - } - else - { - if(p.b->IsInternal()) - { - m_stkStack[depth++]=sStkNN(p.a,p.b->m_childs[0]); - m_stkStack[depth++]=sStkNN(p.a,p.b->m_childs[1]); - } - else - { - collector.Process(p.a,p.b); - } - } - } - } while(depth); - } - } - template - void collideTV( const NodeType* root, const VolumeType& volume, Collector& collector) const - { - if(root) - { -// ATTRIBUTE_ALIGNED16(VolumeType) volume(vol); -// btAlignedObjectArray stack; - AZStd::fixed_vector stack; - //stack.reserve(SIMPLE_STACKSIZE); - stack.push_back(root); - do { - const NodeType* n=stack[stack.size()-1]; - stack.pop_back(); - if(IntersectAabbAabb(n->m_volume,volume)) - { - if(n->IsInternal()) - { - stack.push_back(n->m_childs[0]); - stack.push_back(n->m_childs[1]); - } - else - { - collector.Process(n); - } - } - } while(!stack.empty()); - } - } - - template - void collideTP(const NodeType* root, const AZ::Plane& plane, Collector& collector) const - { - if (root) - { - AZStd::fixed_vector stack; - stack.push_back(root); - do - { - const NodeType* n=stack[stack.size()-1]; - stack.pop_back(); - if (IntersectAabbPlane(n->m_volume, plane)) - { - if(n->IsInternal()) - { - stack.push_back(n->m_childs[0]); - stack.push_back(n->m_childs[1]); - } - else - { - collector.Process(n); - } - } - } while (!stack.empty()); - } - } - - ///rayTest is a re-entrant ray test, and can be called in parallel as long as the btAlignedAlloc is thread-safe (uses locking etc) - ///rayTest is slower than rayTestInternal, because it builds a local stack, using memory allocations, and it recomputes signs/rayDirectionInverses each time - template - static void rayTest( const NodeType* root, const AZ::Vector3& rayFrom, const AZ::Vector3& rayTo, Collector& collector) - { - if(root) - { - AZ::Vector3 ray = rayTo-rayFrom; - AZ::Vector3 rayDir = ray.GetNormalized(); - - ///what about division by zero? --> just set rayDirection[i] to INF/1e30 - AZ::Vector3 rayDirectionInverse = AZ::Vector3::CreateSelectCmpEqual(rayDir,AZ::Vector3::CreateZero(),AZ::Vector3(1e30),rayDir.GetReciprocal()); - - unsigned int signs[3];// = { rayDirectionInverse[0] < 0.0f, rayDirectionInverse[1] < 0.0f, rayDirectionInverse[2] < 0.0f }; - signs[0] = rayDirectionInverse.GetX() < 0.0f; - signs[1] = rayDirectionInverse.GetY() < 0.0f; - signs[2] = rayDirectionInverse.GetZ() < 0.0f; - - //float lambda_max = rayDir.Dot(ray); - - AZ::Vector3 resultNormal; - - //btAlignedObjectArray stack; - vector stack; - - int depth=1; - int treshold=DOUBLE_STACKSIZE-2; - - stack.resize(DOUBLE_STACKSIZE); - stack[0]=root; - AZ::Vector3 bounds[2]; - do { - const NodeType* node=stack[--depth]; - - bounds[0] = node->m_volume.GetMin(); - bounds[1] = node->m_volume.GetMax(); - - //float tmin = 1.0f; - //float lambda_min = 0.0f; - // todo.. - unsigned int result1 = /*btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max)*/0; -#ifdef COMPARE_BTRAY_AABB2 - float param = 1.0f; - bool result2 = /*btRayAabb(rayFrom,rayTo,node->volume.GetMin(),node->volume.GetMax(),param,resultNormal)*/0; - AZ_Assert(result1 == result2, ""); -#endif //TEST_BTRAY_AABB2 - if(result1) - { - if(node->IsInternal()) - { - if(depth>treshold) - { - stack.resize(stack.size()*2); - treshold=stack.size()-2; - } - stack[depth++]=node->m_childs[0]; - stack[depth++]=node->m_childs[1]; - } - else - { - collector.Process(node); - } - } - } while(depth); - - } - } - - ///rayTestInternal is faster than rayTest, because it uses a persistent stack (to reduce dynamic memory allocations to a minimum) and it uses precomputed signs/rayInverseDirections - ///rayTestInternal is used by btDbvtBroadphase to accelerate world ray casts - template - void rayTestInternal(const NodeType* root, const AZ::Vector3& rayFrom, const AZ::Vector3& rayTo, const AZ::Vector3& rayDirectionInverse, unsigned int signs[3], const float lambda_max, const AZ::Vector3& aabbMin, const AZ::Vector3& aabbMax, Collector& collector) const - { - (void)rayFrom;(void)rayTo;(void)rayDirectionInverse;(void)signs;(void)lambda_max; - if(root) - { - AZ::Vector3 resultNormal; - - int depth=1; - int treshold=DOUBLE_STACKSIZE-2; - vector stack; - stack.resize(DOUBLE_STACKSIZE); - stack[0]=root; - AZ::Vector3 bounds[2]; - do - { - const NodeType* node=stack[--depth]; - bounds[0] = node->m_volume.GetMin()+aabbMin; - bounds[1] = node->m_volume.GetMax()+aabbMax; - - //float tmin = 1.0f; - //float lambda_min = 0.0f; - unsigned int result1=false; - // todo... - result1 = /*btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max)*/false; - if(result1) - { - if(node->IsInternal()) - { - if(depth>treshold) - { - stack.resize(stack.size()*2); - treshold=stack.size()-2; - } - stack[depth++]=node->m_childs[0]; - stack[depth++]=node->m_childs[1]; - } - else - { - collector.Process(node); - } - } - } while(depth); - } - } - - template - static void collideKDOP(const NodeType* root, const AZ::Vector3* normals, const float* offsets, int count, Collector& collector) - { - (void)root;(void)normals;(void)offsets;(void)count;(void)collector; -/* if(root) - { - const int inside=(1< stack; - int signs[sizeof(unsigned)*8]; - btAssert(count=0)?1:0)+ - ((normals[i].y()>=0)?2:0)+ - ((normals[i].z()>=0)?4:0); - } - stack.reserve(SIMPLE_STACKSIZE); - stack.push_back(sStkNP(root,0)); - do { - sStkNP se=stack[stack.size()-1]; - bool out=false; - stack.pop_back(); - for(int i=0,j=1;(!out)&&(ivolume.Classify(normals[i],offsets[i],signs[i]); - switch(side) - { - case -1: out=true;break; - case +1: se.mask|=j;break; - } - } - } - if(!out) - { - if((se.mask!=inside)&&(se.node->isinternal())) - { - stack.push_back(sStkNP(se.node->childs[0],se.mask)); - stack.push_back(sStkNP(se.node->childs[1],se.mask)); - } - else - { - if(policy.AllLeaves(se.node)) enumLeaves(se.node,policy); - } - } - } while(!stack.empty()); - }*/ - } - template - static void collideOCL( const NodeType* root, const AZ::Vector3* normals, const float* offsets, const AZ::Vector3& sortaxis, int count, Collector& collector, bool fullsort=true) - { - (void)root;(void)normals;(void)offsets;(void)sortaxis;(void)count;(void)offsets;(void)collector;(void)fullsort; -/* if(root) - { - const unsigned srtsgns=(sortaxis[0]>=0?1:0)+ - (sortaxis[1]>=0?2:0)+ - (sortaxis[2]>=0?4:0); - const int inside=(1< stock; - btAlignedObjectArray ifree; - btAlignedObjectArray stack; - int signs[sizeof(unsigned)*8]; - btAssert(count=0)?1:0)+ - ((normals[i].y()>=0)?2:0)+ - ((normals[i].z()>=0)?4:0); - } - stock.reserve(SIMPLE_STACKSIZE); - stack.reserve(SIMPLE_STACKSIZE); - ifree.reserve(SIMPLE_STACKSIZE); - stack.push_back(allocate(ifree,stock,sStkNPS(root,0,root->volume.ProjectMinimum(sortaxis,srtsgns)))); - do { - const int id=stack[stack.size()-1]; - sStkNPS se=stock[id]; - stack.pop_back();ifree.push_back(id); - if(se.mask!=inside) - { - bool out=false; - for(int i=0,j=1;(!out)&&(ivolume.Classify(normals[i],offsets[i],signs[i]); - switch(side) - { - case -1: out=true;break; - case +1: se.mask|=j;break; - } - } - } - if(out) continue; - } - if(policy.Descent(se.node)) - { - if(se.node->isinternal()) - { - const NodeType* pns[]={ se.node->childs[0],se.node->childs[1]}; - sStkNPS nes[]={ sStkNPS(pns[0],se.mask,pns[0]->volume.ProjectMinimum(sortaxis,srtsgns)), - sStkNPS(pns[1],se.mask,pns[1]->volume.ProjectMinimum(sortaxis,srtsgns))}; - const int q=nes[0].value0)) - { - // Insert 0 - j=nearest(&stack[0],&stock[0],nes[q].value,0,stack.size()); - stack.push_back(0); -#if DBVT_USE_MEMMOVE - memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1)); -#else - for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1]; -#endif - stack[j]=allocate(ifree,stock,nes[q]); - // Insert 1 - j=nearest(&stack[0],&stock[0],nes[1-q].value,j,stack.size()); - stack.push_back(0); -#if DBVT_USE_MEMMOVE - memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1)); -#else - for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1]; -#endif - stack[j]=allocate(ifree,stock,nes[1-q]); - } - else - { - stack.push_back(allocate(ifree,stock,nes[q])); - stack.push_back(allocate(ifree,stock,nes[1-q])); - } - } - else - { - policy.Process(se.node,se.value); - } - } - } while(stack.size()); - }*/ - } - - template - static void collideTU(const NodeType* root, Collector& collector) - { - (void)root;(void)collector; -/* if(root) - { - btAlignedObjectArray stack; - stack.reserve(SIMPLE_STACKSIZE); - stack.push_back(root); - do { - const NodeType* n=stack[stack.size()-1]; - stack.pop_back(); - if(policy.Descent(n)) - { - if(n->isinternal()) - { stack.push_back(n->childs[0]);stack.push_back(n->childs[1]); } - else - { policy.Process(n); } - } - } while(stack.size()>0); - }*/ - } - - private: - BvDynamicTree(const BvDynamicTree&) {} - - // Helpers - //static AZ_FORCE_INLINE int nearest(const int* i,const BvDynamicTree::sStkNPS* a,const float& v,int l,int h) - //{ - // int m=0; - // while(l>1; - // if(a[i[m]].value>=v) l=m+1; else h=m; - // } - // return h; - //} - //static AZ_FORCE_INLINE int allocate( int_fixed_stack_type& ifree, stknps_fixed_stack_type& stock, const sStkNPS& value) - //{ - // int i; - // if( !ifree.empty() ) - // { - // i=ifree[ifree.size()-1]; - // ifree.pop_back(); - // stock[i]=value; - // } - // else - // { - // i=stock.size(); - // stock.push_back(value); - // } - // return i; - //} - // - - AZ_FORCE_INLINE void deletenode( NodeType* node) - { - //btAlignedFree(pdbvt->m_free); - delete m_free; - m_free=node; - } - - void recursedeletenode( NodeType* node) - { - if(!node->IsLeaf()) - { - recursedeletenode(node->m_childs[0]); - recursedeletenode(node->m_childs[1]); - } - - if( node == m_root ) m_root=0; - deletenode(node); - } - - - AZ_FORCE_INLINE NodeType* createnode( NodeType* parent, void* data) - { - NodeType* node; - if(m_free) - { node=m_free;m_free=0; } - else - { node = aznew NodeType(); } - node->m_parent = parent; - node->m_data = data; - node->m_childs[1] = 0; - return node; - } - AZ_FORCE_INLINE NodeType* createnode( BvDynamicTree::NodeType* parent, const VolumeType& volume, void* data) - { - NodeType* node = createnode(parent,data); - node->m_volume=volume; - return node; - } - // - AZ_FORCE_INLINE NodeType* createnode( BvDynamicTree::NodeType* parent, const VolumeType& volume0, const VolumeType& volume1, void* data) - { - NodeType* node = createnode(parent,data); - Merge(volume0,volume1,node->m_volume); - return node; - } - void insertleaf( NodeType* root, NodeType* leaf); - NodeType* removeleaf( NodeType* leaf); - void fetchleaves(NodeType* root,NodeArrayType& leaves,int depth=-1); - void split(const NodeArrayType& leaves,NodeArrayType& left,NodeArrayType& right,const AZ::Vector3& org,const AZ::Vector3& axis); - VolumeType bounds(const NodeArrayType& leaves); - void bottomup( NodeArrayType& leaves ); - NodeType* topdown(NodeArrayType& leaves,int bu_treshold); - AZ_FORCE_INLINE NodeType* sort(NodeType* n,NodeType*& r); - - NodeType* m_root; - NodeType* m_free; - int m_lkhd; - int m_leaves; - unsigned m_opath; - - //btAlignedObjectArray m_stkStack; - // Profile and choose static or dynamic vector. - typedef AZStd::fixed_vector stknn_fixed_stack_type; - typedef AZStd::fixed_vector int_fixed_stack_type; - typedef AZStd::fixed_vector stknps_fixed_stack_type; - - stknn_fixed_stack_type m_stkStack; - }; -} - -#endif // RR_DYNAMIC_BOUNDING_VOLUME_TREE_H -#pragma once diff --git a/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.cpp b/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.cpp deleted file mode 100644 index 13a0151bc7..0000000000 --- a/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.cpp +++ /dev/null @@ -1,597 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include - -#include -#include -#include - -#include -#include - -// for highly verbose internal debugging -//#define INTERNAL_DEBUG_PROXIMITY - -namespace GridMate -{ - - void ProximityInterestChunk::OnReplicaActivate(const ReplicaContext& rc) - { - m_interestHandler = static_cast(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4))); - AZ_Warning("GridMate", m_interestHandler, "No proximity interest handler in the user context"); - - if (m_interestHandler) - { - m_interestHandler->OnNewRulesChunk(this, rc.m_peer); - } - } - - void ProximityInterestChunk::OnReplicaDeactivate(const ReplicaContext& rc) - { - if (rc.m_peer && m_interestHandler) - { - m_interestHandler->OnDeleteRulesChunk(this, rc.m_peer); - } - } - - bool ProximityInterestChunk::AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx) - { - if (IsProxy()) - { - auto rulePtr = m_interestHandler->CreateRule(ctx.m_sourcePeer); - rulePtr->Set(bbox); - m_rules.insert(AZStd::make_pair(netId, rulePtr)); - } - - return true; - } - - bool ProximityInterestChunk::RemoveRuleFn(RuleNetworkId netId, const RpcContext&) - { - if (IsProxy()) - { - m_rules.erase(netId); - } - - return true; - } - - bool ProximityInterestChunk::UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&) - { - if (IsProxy()) - { - auto it = m_rules.find(netId); - if (it != m_rules.end()) - { - it->second->Set(bbox); - } - } - - return true; - } - - bool ProximityInterestChunk::AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&) - { - ProximityInterestChunk* peerChunk = m_interestHandler->FindRulesChunkByPeerId(peerId); - if (peerChunk) - { - auto it = peerChunk->m_rules.find(netId); - if (it == peerChunk->m_rules.end()) - { - auto rulePtr = m_interestHandler->CreateRule(peerId); - peerChunk->m_rules.insert(AZStd::make_pair(netId, rulePtr)); - rulePtr->Set(bbox); - } - } - return false; - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterest - */ - ProximityInterest::ProximityInterest(ProximityInterestHandler* handler) - : m_handler(handler) - , m_bbox(AZ::Aabb::CreateNull()) - { - AZ_Assert(m_handler, "Invalid interest handler"); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestRule - */ - void ProximityInterestRule::Set(const AZ::Aabb& bbox) - { - m_bbox = bbox; - m_handler->UpdateRule(this); - } - - void ProximityInterestRule::Destroy() - { - m_handler->DestroyRule(this); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestAttribute - */ - void ProximityInterestAttribute::Set(const AZ::Aabb& bbox) - { - m_bbox = bbox; - m_handler->UpdateAttribute(this); - } - - void ProximityInterestAttribute::Destroy() - { - m_handler->DestroyAttribute(this); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestHandler - */ - ProximityInterestHandler::ProximityInterestHandler() - : m_im(nullptr) - , m_rm(nullptr) - , m_lastRuleNetId(0) - , m_rulesReplica(nullptr) - { - m_attributeWorld = AZStd::make_unique(); - AZ_Assert(m_attributeWorld, "Out of memory"); - } - - ProximityInterestRule::Ptr ProximityInterestHandler::CreateRule(PeerId peerId) - { - ProximityInterestRule* rulePtr = aznew ProximityInterestRule(this, peerId, GetNewRuleNetId()); - if (m_rm && peerId == m_rm->GetLocalPeerId()) - { - m_rulesReplica->AddRuleRpc(rulePtr->GetNetworkId(), rulePtr->Get()); - } - - CreateAndInsertIntoSpatialStructure(rulePtr); - - return rulePtr; - } - - ProximityInterestAttribute::Ptr ProximityInterestHandler::CreateAttribute(ReplicaId replicaId) - { - auto newAttribute = aznew ProximityInterestAttribute(this, replicaId); - AZ_Assert(newAttribute, "Out of memory"); - - CreateAndInsertIntoSpatialStructure(newAttribute); - - return newAttribute; - } - - void ProximityInterestHandler::FreeRule(ProximityInterestRule* rule) - { - //TODO: should be pool-allocated - delete rule; - } - - void ProximityInterestHandler::DestroyRule(ProximityInterestRule* rule) - { - if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId()) - { - m_rulesReplica->RemoveRuleRpc(rule->GetNetworkId()); - } - - MarkAttributesDirtyInRule(rule); - - rule->m_bbox = AZ::Aabb::CreateNull(); - m_removedRules.insert(rule); - m_localRules.erase(rule); - } - - void ProximityInterestHandler::UpdateRule(ProximityInterestRule* rule) - { - if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId()) - { - m_rulesReplica->UpdateRuleRpc(rule->GetNetworkId(), rule->Get()); - } - - m_dirtyRules.insert(rule); - } - - void ProximityInterestHandler::FreeAttribute(ProximityInterestAttribute* attrib) - { - delete attrib; - } - - void ProximityInterestHandler::DestroyAttribute(ProximityInterestAttribute* attrib) - { - RemoveFromSpatialStructure(attrib); - - m_attributes.erase(attrib); - m_removedAttributes.insert(attrib); - } - - void ProximityInterestHandler::RemoveFromSpatialStructure(ProximityInterestAttribute* attribute) - { - attribute->m_bbox = AZ::Aabb::CreateNull(); - m_attributeWorld->Remove(attribute->GetNode()); - attribute->SetNode(nullptr); - } - - void ProximityInterestHandler::UpdateAttribute(ProximityInterestAttribute* attrib) - { - auto node = attrib->GetNode(); - AZ_Assert(node, "Attribute wasn't created correctly"); - node->m_volume = attrib->Get(); - m_attributeWorld->Update(node); - - m_dirtyAttributes.insert(attrib); - } - - void ProximityInterestHandler::OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer) - { - if (chunk != m_rulesReplica) // non-local - { - m_peerChunks.insert(AZStd::make_pair(peer->GetId(), chunk)); - - for (auto& rule : m_localRules) - { - chunk->AddRuleForPeerRpc(rule->GetNetworkId(), rule->GetPeerId(), rule->Get()); - } - } - } - - void ProximityInterestHandler::OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer) - { - (void)chunk; - m_peerChunks.erase(peer->GetId()); - } - - RuleNetworkId ProximityInterestHandler::GetNewRuleNetId() - { - ++m_lastRuleNetId; - - if (m_rulesReplica) - { - return m_rulesReplica->GetReplicaId() | (static_cast(m_lastRuleNetId) << 32); - } - - return (static_cast(m_lastRuleNetId) << 32); - } - - ProximityInterestChunk* ProximityInterestHandler::FindRulesChunkByPeerId(PeerId peerId) - { - auto it = m_peerChunks.find(peerId); - if (it == m_peerChunks.end()) - { - return nullptr; - } - - return it->second; - } - - const InterestMatchResult& ProximityInterestHandler::GetLastResult() - { - return m_resultCache; - } - - ProximityInterestHandler::RuleSet& ProximityInterestHandler::GetAffectedRules() - { - /* - * The expectation that lots of attributes will change frequently, - * so there is no point in trying to optimize cases - * where only a few attributes have changed. - */ - if (m_dirtyAttributes.empty() && !m_dirtyRules.empty()) - { - return m_dirtyRules; - } - - /* - * Assuming all rules might have been affected. - * - * There is an optimization chance here if the number of rules is large, as in 1,000+ rules. - * To handle such scale we would need another spatial structure for rules. - */ - return m_localRules; - } - - void ProximityInterestHandler::GetAttributesWithinRule(ProximityInterestRule* rule, SpatialIndex::NodeCollector& nodes) - { - m_attributeWorld->Query(rule->Get(), nodes); - } - - void ProximityInterestHandler::ClearDirtyState() - { - m_dirtyAttributes.clear(); - m_dirtyRules.clear(); - } - - void ProximityInterestHandler::CreateAndInsertIntoSpatialStructure(ProximityInterestAttribute* attribute) - { - m_attributes.insert(attribute); - SpatialIndex::Node* node = m_attributeWorld->Insert(attribute->Get(), attribute); - attribute->SetNode(node); - } - - void ProximityInterestHandler::CreateAndInsertIntoSpatialStructure(ProximityInterestRule* rule) - { - m_localRules.insert(rule); - } - - void ProximityInterestHandler::UpdateInternal(InterestMatchResult& result) - { - /* - * The goal is to return all dirty attributes that were either dirty because: - * 1) they changed which rules have apply to - * 2) rules have changed and no longer apply to those attributes - * and thus resulted in different peer(s) associated with a given replica. - */ - - const RuleSet& rules = GetAffectedRules(); - - for (auto& dirtyAttribute : m_dirtyAttributes) - { - result.insert(dirtyAttribute->GetReplicaId()); - } - - /* - * The exectation is to have a lot more attributes than rules. - * The amount of rules should grow linear with amount of peers, - * so it should be OK to iterate through all rules each update. - */ - for (auto& rule : rules) - { - CheckChangesForRule(rule, result); - } - - for (auto& removedRule : m_removedRules) - { - FreeRule(removedRule); - } - m_removedRules.clear(); - - // mark removed attribute as having no peers - for (auto& removedAttribute : m_removedAttributes) - { - result.insert(removedAttribute->GetReplicaId()); - FreeAttribute(removedAttribute); - } - m_removedAttributes.clear(); - } - - void ProximityInterestHandler::CheckChangesForRule(ProximityInterestRule* rule, InterestMatchResult& result) - { - SpatialIndex::NodeCollector collector; - GetAttributesWithinRule(rule, collector); - - auto peerId = rule->GetPeerId(); - for (ProximityInterestAttribute* attr : collector.GetNodes()) - { - AZ_Assert(attr, "bad node?"); - - auto findIt = result.find(attr->GetReplicaId()); - if (findIt != result.end()) - { - findIt->second.insert(peerId); - } - else - { - auto resultIt = result.insert(attr->GetReplicaId()); - AZ_Assert(resultIt.second, "Successfully inserted"); - resultIt.first->second.insert(peerId); - } - } - } - - void ProximityInterestHandler::MarkAttributesDirtyInRule(ProximityInterestRule* rule) - { - SpatialIndex::NodeCollector collector; - GetAttributesWithinRule(rule, collector); - - for (ProximityInterestAttribute* attr : collector.GetNodes()) - { - AZ_Assert(attr, "bad node?"); - - UpdateAttribute(attr); - } - } - - void ProximityInterestHandler::ProduceChanges(const InterestMatchResult& before, const InterestMatchResult& after) - { - m_resultCache.clear(); - -#if defined(INTERNAL_DEBUG_PROXIMITY) - before.PrintMatchResult("before"); - after.PrintMatchResult("after"); -#endif - - /* - * 'after' contains only the stuff that might have changed - */ - for (auto& possiblyDirty : after) - { - ReplicaId repId = possiblyDirty.first; - const InterestPeerSet& peerSet = possiblyDirty.second; - - auto foundInBefore = before.find(repId); - if (foundInBefore != before.end()) - { - if (!HasSamePeers(foundInBefore->second, peerSet)) - { - // was in the last calculation but has a different peer set now - m_resultCache.insert(AZStd::make_pair(repId, peerSet)); - } - } - else - { - // since it wasn't present during last calculation - m_resultCache.insert(AZStd::make_pair(repId, peerSet)); - } - } - - // Mark attributes (replicas) for removal that have not moved but a rule (clients) no longer sees it - for (auto& possiblyDirty : before) - { - ReplicaId repId = possiblyDirty.first; - - const auto foundInAfter = after.find(repId); - /* - * If the prior state was a replica A present on peer X: "A{X}", and now A should no longer be present on any peer: "A{}" - * then by the rules of InterestHandlers interacting with InterestManager, we should return in @m_resultCache the following: - * - * A{} - indicating that replica A must be removed all peers. - * - * On the next pass, the prior state would be: "A{}" and the current state would be "A{}" as well. At that point, we have - * already sent the update to remove A from X, so @m_resultCache should no longer mention A at all. - */ - if (foundInAfter == after.end() && !possiblyDirty.second.empty() /* "not A{}" see the above comment */) - { - m_resultCache.insert(AZStd::make_pair(repId, InterestPeerSet())); - } - } - -#if defined(INTERNAL_DEBUG_PROXIMITY) - m_resultCache.PrintMatchResult("changes"); -#endif - - } - - bool ProximityInterestHandler::HasSamePeers(const InterestPeerSet& one, const InterestPeerSet& another) - { - if (one.size() != another.size()) - { - return false; - } - - for (auto& peerFromOne : one) - { - if (another.find(peerFromOne) == another.end()) - { - return false; - } - } - - // Safe to assume it's the same sets since all entries are unique in a peer sets - return true; - } - - void ProximityInterestHandler::Update() - { - InterestMatchResult newResult; - - UpdateInternal(newResult); - ProduceChanges(m_lastResult, newResult); - - m_lastResult = std::move(newResult); - ClearDirtyState(); - } - - void ProximityInterestHandler::OnRulesHandlerRegistered(InterestManager* manager) - { - AZ_Assert(m_im == nullptr, "Handler is already registered with manager %p (%p)\n", m_im, manager); - AZ_Assert(m_rulesReplica == nullptr, "Rules replica is already created\n"); - AZ_TracePrintf("GridMate", "Proximity interest handler is registered\n"); - m_im = manager; - m_rm = m_im->GetReplicaManager(); - m_rm->RegisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4), this); - - auto replica = Replica::CreateReplica("ProximityInterestHandlerRules"); - m_rulesReplica = CreateAndAttachReplicaChunk(replica); - m_rm->AddPrimary(replica); - } - - void ProximityInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager) - { - (void)manager; - AZ_Assert(m_im == manager, "Handler was not registered with manager %p (%p)\n", manager, m_im); - AZ_TracePrintf("GridMate", "Proximity interest handler is unregistered\n"); - m_rulesReplica = nullptr; - m_im = nullptr; - m_rm->UnregisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)); - m_rm = nullptr; - - for (auto& chunk : m_peerChunks) - { - chunk.second->m_interestHandler = nullptr; - } - m_peerChunks.clear(); - - ClearDirtyState(); - DestroyAll(); - - m_resultCache.clear(); - } - - void ProximityInterestHandler::DestroyAll() - { - for (ProximityInterestRule* rule : m_localRules) - { - FreeRule(rule); - } - m_localRules.clear(); - - for (ProximityInterestAttribute* attr : m_attributes) - { - FreeAttribute(attr); - } - m_attributes.clear(); - - for (auto& removedRule : m_removedRules) - { - FreeRule(removedRule); - } - m_removedRules.clear(); - - for (auto& removedAttribute : m_removedAttributes) - { - FreeAttribute(removedAttribute); - } - m_removedAttributes.clear(); - } - - /////////////////////////////////////////////////////////////////////////// - ProximityInterestHandler::~ProximityInterestHandler() - { - /* - * If a handler was registered with a InterestManager, then InterestManager ought to have called OnRulesHandlerUnregistered - * but this is a safety pre-caution. - */ - DestroyAll(); - } - - SpatialIndex::SpatialIndex() - { - m_tree.reset(aznew GridMate::BvDynamicTree()); - } - - void SpatialIndex::Remove(Node* node) - { - m_tree->Remove(node); - } - - void SpatialIndex::Update(Node* node) - { - m_tree->Update(node); - } - - SpatialIndex::Node* SpatialIndex::Insert(const AZ::Aabb& get, ProximityInterestAttribute* attribute) - { - return m_tree->Insert(get, attribute); - } - - void SpatialIndex::Query(const AZ::Aabb& shape, NodeCollector& nodes) - { - m_tree->collideTV(m_tree->GetRoot(), shape, nodes); - } -} diff --git a/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.h b/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.h deleted file mode 100644 index 6659d44f44..0000000000 --- a/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.h +++ /dev/null @@ -1,314 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#ifndef GM_REPLICA_PROXIMITYINTERESTHANDLER_H -#define GM_REPLICA_PROXIMITYINTERESTHANDLER_H - -#include -#include -#include -#include -#include - -#include -#include - -namespace GridMate -{ - class ProximityInterestHandler; - class ProximityInterestAttribute; - - /* - * Base interest - */ - class ProximityInterest - { - friend class ProximityInterestHandler; - - public: - const AZ::Aabb& Get() const { return m_bbox; } - - protected: - explicit ProximityInterest(ProximityInterestHandler* handler); - - ProximityInterestHandler* m_handler; - AZ::Aabb m_bbox; - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * Proximity rule - */ - class ProximityInterestRule - : public InterestRule - , public ProximityInterest - { - friend class ProximityInterestHandler; - - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(ProximityInterestRule); - - void Set(const AZ::Aabb& bbox); - - private: - - // Intrusive ptr - template - friend struct AZStd::IntrusivePtrCountPolicy; - unsigned int m_refCount = 0; - AZ_FORCE_INLINE void add_ref() { ++m_refCount; } - AZ_FORCE_INLINE void release() { --m_refCount; if (!m_refCount) Destroy(); } - AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; } - /////////////////////////////////////////////////////////////////////////// - - ProximityInterestRule(ProximityInterestHandler* handler, PeerId peerId, RuleNetworkId netId) - : InterestRule(peerId, netId) - , ProximityInterest(handler) - {} - - void Destroy(); - }; - /////////////////////////////////////////////////////////////////////////// - - class SpatialIndex - { - public: - typedef Internal::DynamicTreeNode Node; - - class NodeCollector - { - typedef AZStd::vector Type; - - public: - void Process(const Internal::DynamicTreeNode* node) - { - m_nodes.push_back(reinterpret_cast(node->m_data)); - } - - const Type& GetNodes() const - { - return m_nodes; - } - - private: - Type m_nodes; - }; - - SpatialIndex(); - ~SpatialIndex() = default; - - AZ_FORCE_INLINE void Remove(Node* node); - AZ_FORCE_INLINE void Update(Node* node); - AZ_FORCE_INLINE Node* Insert(const AZ::Aabb& get, ProximityInterestAttribute* attribute); - AZ_FORCE_INLINE void Query(const AZ::Aabb& get, NodeCollector& nodes); - - private: - AZStd::unique_ptr m_tree; - }; - - /* - * Proximity attribute - */ - class ProximityInterestAttribute - : public InterestAttribute - , public ProximityInterest - { - friend class ProximityInterestHandler; - template friend class InterestPtr; - - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(ProximityInterestAttribute); - - void Set(const AZ::Aabb& bbox); - - private: - - // Intrusive ptr - template - friend struct AZStd::IntrusivePtrCountPolicy; - unsigned int m_refCount = 0; - AZ_FORCE_INLINE void add_ref() { ++m_refCount; } - AZ_FORCE_INLINE void release() { Destroy(); } - AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; } - /////////////////////////////////////////////////////////////////////////// - - ProximityInterestAttribute(ProximityInterestHandler* handler, ReplicaId repId) - : InterestAttribute(repId) - , ProximityInterest(handler) - , m_worldNode(nullptr) - {} - - void Destroy(); - - void SetNode(SpatialIndex::Node* node) { m_worldNode = node; } - SpatialIndex::Node* GetNode() const { return m_worldNode; } - SpatialIndex::Node* m_worldNode; ///< non-owning pointer - }; - /////////////////////////////////////////////////////////////////////////// - - class ProximityInterestChunk - : public ReplicaChunk - { - public: - GM_CLASS_ALLOCATOR(ProximityInterestChunk); - - // ReplicaChunk - typedef AZStd::intrusive_ptr Ptr; - bool IsReplicaMigratable() override { return false; } - bool IsBroadcast() override { return true; } - static const char* GetChunkName() { return "ProximityInterestChunk"; } - - ProximityInterestChunk() - : AddRuleRpc("AddRule") - , RemoveRuleRpc("RemoveRule") - , UpdateRuleRpc("UpdateRule") - , AddRuleForPeerRpc("AddRuleForPeerRpc") - , m_interestHandler(nullptr) - { - } - - void OnReplicaActivate(const ReplicaContext& rc) override; - void OnReplicaDeactivate(const ReplicaContext& rc) override; - - bool AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx); - bool RemoveRuleFn(RuleNetworkId netId, const RpcContext&); - bool UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&); - bool AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&); - - Rpc, RpcArg>::BindInterface AddRuleRpc; - Rpc>::BindInterface RemoveRuleRpc; - Rpc, RpcArg>::BindInterface UpdateRuleRpc; - - Rpc, RpcArg, RpcArg>::BindInterface AddRuleForPeerRpc; - - unordered_map m_rules; - ProximityInterestHandler* m_interestHandler; - }; - - /* - * Rules handler - */ - class ProximityInterestHandler - : public BaseRulesHandler - { - friend class ProximityInterestRule; - friend class ProximityInterestAttribute; - friend class ProximityInterestChunk; - - public: - - typedef unordered_set AttributeSet; - typedef unordered_set RuleSet; - - GM_CLASS_ALLOCATOR(ProximityInterestHandler); - - ProximityInterestHandler(); - ~ProximityInterestHandler(); - - /* - * Creates new proximity rule and binds it to the peer. - * Note: the lifetime of the created rule is tied to the lifetime of this handler. - */ - ProximityInterestRule::Ptr CreateRule(PeerId peerId); - - /* - * Creates new proximity attribute and binds it to the replica. - * Note: the lifetime of the created attribute is tied to the lifetime of this handler. - */ - ProximityInterestAttribute::Ptr CreateAttribute(ReplicaId replicaId); - - // Calculates rules and attributes matches - void Update() override; - - // Returns last recalculated results - const InterestMatchResult& GetLastResult() override; - - // Returns the manager it's bound to - InterestManager* GetManager() override { return m_im; } - - // Rules that this handler is aware of - const RuleSet& GetLocalRules() const { return m_localRules; } - - private: - - // BaseRulesHandler - void OnRulesHandlerRegistered(InterestManager* manager) override; - void OnRulesHandlerUnregistered(InterestManager* manager) override; - - void DestroyRule(ProximityInterestRule* rule); - void FreeRule(ProximityInterestRule* rule); - void UpdateRule(ProximityInterestRule* rule); - - void DestroyAttribute(ProximityInterestAttribute* attrib); - void FreeAttribute(ProximityInterestAttribute* attrib); - void UpdateAttribute(ProximityInterestAttribute* attrib); - - void OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer); - void OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer); - - RuleNetworkId GetNewRuleNetId(); - - ProximityInterestChunk* FindRulesChunkByPeerId(PeerId peerId); - - void DestroyAll(); - - InterestManager* m_im; - ReplicaManager* m_rm; - - AZ::u32 m_lastRuleNetId; - - unordered_map m_peerChunks; - - RuleSet m_localRules; - RuleSet m_removedRules; - RuleSet m_dirtyRules; - - AttributeSet m_attributes; - AttributeSet m_removedAttributes; - AttributeSet m_dirtyAttributes; - - ProximityInterestChunk* m_rulesReplica; - - // collection of all known attributes - AZStd::unique_ptr m_attributeWorld; - - InterestMatchResult m_resultCache; - - /////////////////////////////////////////////////////////////////////////////////////////////////// - // internal processing helpers - AZ_FORCE_INLINE RuleSet& GetAffectedRules(); - AZ_FORCE_INLINE void GetAttributesWithinRule(ProximityInterestRule* rule, SpatialIndex::NodeCollector& nodes); - AZ_FORCE_INLINE void ClearDirtyState(); - - AZ_FORCE_INLINE void CreateAndInsertIntoSpatialStructure(ProximityInterestAttribute* attribute); - AZ_FORCE_INLINE void RemoveFromSpatialStructure(ProximityInterestAttribute* attribute); - AZ_FORCE_INLINE void CreateAndInsertIntoSpatialStructure(ProximityInterestRule* rule); - - void UpdateInternal(InterestMatchResult& result); - void CheckChangesForRule(ProximityInterestRule* rule, InterestMatchResult& result); - void MarkAttributesDirtyInRule(ProximityInterestRule* rule); - - static bool HasSamePeers(const InterestPeerSet& one, const InterestPeerSet& another); - void ProduceChanges(const InterestMatchResult& before, const InterestMatchResult& after); - - InterestMatchResult m_lastResult; - /////////////////////////////////////////////////////////////////////////////////////////////////// - }; - /////////////////////////////////////////////////////////////////////////// -} - -#endif // GM_REPLICA_PROXIMITYINTERESTHANDLER_H diff --git a/Code/Framework/GridMate/GridMate/gridmate_files.cmake b/Code/Framework/GridMate/GridMate/gridmate_files.cmake index 7cc65ff69a..615ce53027 100644 --- a/Code/Framework/GridMate/GridMate/gridmate_files.cmake +++ b/Code/Framework/GridMate/GridMate/gridmate_files.cmake @@ -100,14 +100,10 @@ set(FILES Replica/Tasks/ReplicaPriorityPolicy.h Replica/Interest/BitmaskInterestHandler.cpp Replica/Interest/BitmaskInterestHandler.h - Replica/Interest/ProximityInterestHandler.cpp - Replica/Interest/ProximityInterestHandler.h Replica/Interest/InterestDefs.h Replica/Interest/InterestManager.cpp Replica/Interest/InterestManager.h Replica/Interest/InterestQueryResult.h - Replica/Interest/BvDynamicTree.cpp - Replica/Interest/BvDynamicTree.h Replica/Interest/RulesHandler.h Serialize/Buffer.cpp Serialize/Buffer.h diff --git a/Code/Framework/GridMate/Tests/Interest.cpp b/Code/Framework/GridMate/Tests/Interest.cpp deleted file mode 100644 index c449c92083..0000000000 --- a/Code/Framework/GridMate/Tests/Interest.cpp +++ /dev/null @@ -1,1823 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ -#include "Tests.h" -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace GridMate; - -// An easy switch to use an old brute force ProximityInterestHandler for performance comparison. -#if 0 -namespace GridMate -{ - /* - * Base interest - */ - class ProximityInterest - { - friend class ProximityInterestHandler; - - public: - const AZ::Aabb& Get() const { return m_bbox; } - - protected: - explicit ProximityInterest(ProximityInterestHandler* handler); - - ProximityInterestHandler* m_handler; - AZ::Aabb m_bbox; - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * Proximity rule - */ - class ProximityInterestRule - : public InterestRule - , public ProximityInterest - { - friend class ProximityInterestHandler; - - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(ProximityInterestRule); - - void Set(const AZ::Aabb& bbox); - - private: - - // Intrusive ptr - template - friend struct AZStd::IntrusivePtrCountPolicy; - unsigned int m_refCount = 0; - AZ_FORCE_INLINE void add_ref() { ++m_refCount; } - AZ_FORCE_INLINE void release() { --m_refCount; if (!m_refCount) Destroy(); } - AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; } - /////////////////////////////////////////////////////////////////////////// - - ProximityInterestRule(ProximityInterestHandler* handler, PeerId peerId, RuleNetworkId netId) - : InterestRule(peerId, netId) - , ProximityInterest(handler) - {} - - void Destroy(); - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * Proximity attribute - */ - class ProximityInterestAttribute - : public InterestAttribute - , public ProximityInterest - { - friend class ProximityInterestHandler; - template friend class InterestPtr; - - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(ProximityInterestAttribute); - - void Set(const AZ::Aabb& bbox); - - private: - - // Intrusive ptr - template - friend struct AZStd::IntrusivePtrCountPolicy; - unsigned int m_refCount = 0; - AZ_FORCE_INLINE void add_ref() { ++m_refCount; } - AZ_FORCE_INLINE void release() { Destroy(); } - AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; } - /////////////////////////////////////////////////////////////////////////// - - ProximityInterestAttribute(ProximityInterestHandler* handler, ReplicaId repId) - : InterestAttribute(repId) - , ProximityInterest(handler) - {} - - void Destroy(); - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * Rules handler - */ - class ProximityInterestHandler - : public BaseRulesHandler - { - friend class ProximityInterestRule; - friend class ProximityInterestAttribute; - friend class ProximityInterestChunk; - - public: - - typedef unordered_set AttributeSet; - typedef unordered_set RuleSet; - - GM_CLASS_ALLOCATOR(ProximityInterestHandler); - - ProximityInterestHandler(); - - // Creates new proximity rule and binds it to the peer - ProximityInterestRule::Ptr CreateRule(PeerId peerId); - - // Creates new proximity attribute and binds it to the replica - ProximityInterestAttribute::Ptr CreateAttribute(ReplicaId replicaId); - - // Calculates rules and attributes matches - void Update() override; - - // Returns last recalculated results - const InterestMatchResult& GetLastResult() override; - - // Returns manager its bound with - InterestManager* GetManager() override { return m_im; } - - const RuleSet& GetLocalRules() { return m_localRules; } - - private: - void UpdateInternal(InterestMatchResult& result); - - // BaseRulesHandler - void OnRulesHandlerRegistered(InterestManager* manager) override; - void OnRulesHandlerUnregistered(InterestManager* manager) override; - - void DestroyRule(ProximityInterestRule* rule); - void FreeRule(ProximityInterestRule* rule); - void UpdateRule(ProximityInterestRule* rule); - - void DestroyAttribute(ProximityInterestAttribute* attrib); - void FreeAttribute(ProximityInterestAttribute* attrib); - void UpdateAttribute(ProximityInterestAttribute* attrib); - - - void OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer); - void OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer); - - RuleNetworkId GetNewRuleNetId(); - - ProximityInterestChunk* FindRulesChunkByPeerId(PeerId peerId); - - InterestManager* m_im; - ReplicaManager* m_rm; - - AZ::u32 m_lastRuleNetId; - - unordered_map m_peerChunks; - RuleSet m_localRules; - - AttributeSet m_attributes; - RuleSet m_rules; - - ProximityInterestChunk* m_rulesReplica; - - InterestMatchResult m_resultCache; - }; - /////////////////////////////////////////////////////////////////////////// - - class ProximityInterestChunk - : public ReplicaChunk - { - public: - GM_CLASS_ALLOCATOR(ProximityInterestChunk); - - // ReplicaChunk - typedef AZStd::intrusive_ptr Ptr; - bool IsReplicaMigratable() override { return false; } - static const char* GetChunkName() { return "ProximityInterestChunk"; } - bool IsBroadcast() { return true; } - /////////////////////////////////////////////////////////////////////////// - - - ProximityInterestChunk() - : m_interestHandler(nullptr) - , AddRuleRpc("AddRule") - , RemoveRuleRpc("RemoveRule") - , UpdateRuleRpc("UpdateRule") - , AddRuleForPeerRpc("AddRuleForPeerRpc") - { - - } - - void OnReplicaActivate(const ReplicaContext& rc) override - { - m_interestHandler = static_cast(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4))); - AZ_Assert(m_interestHandler, "No proximity interest handler in the user context"); - - if (m_interestHandler) - { - m_interestHandler->OnNewRulesChunk(this, rc.m_peer); - } - } - - void OnReplicaDeactivate(const ReplicaContext& rc) override - { - if (rc.m_peer && m_interestHandler) - { - m_interestHandler->OnDeleteRulesChunk(this, rc.m_peer); - } - } - - bool AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx) - { - if (IsProxy()) - { - auto rulePtr = m_interestHandler->CreateRule(ctx.m_sourcePeer); - rulePtr->Set(bbox); - m_rules.insert(AZStd::make_pair(netId, rulePtr)); - } - - return true; - } - - bool RemoveRuleFn(RuleNetworkId netId, const RpcContext&) - { - if (IsProxy()) - { - m_rules.erase(netId); - } - - return true; - } - - bool UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&) - { - if (IsProxy()) - { - auto it = m_rules.find(netId); - if (it != m_rules.end()) - { - it->second->Set(bbox); - } - } - - return true; - } - - bool AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&) - { - ProximityInterestChunk* peerChunk = m_interestHandler->FindRulesChunkByPeerId(peerId); - if (peerChunk) - { - auto it = peerChunk->m_rules.find(netId); - if (it == peerChunk->m_rules.end()) - { - auto rulePtr = m_interestHandler->CreateRule(peerId); - peerChunk->m_rules.insert(AZStd::make_pair(netId, rulePtr)); - rulePtr->Set(bbox); - } - } - return false; - } - - Rpc, RpcArg>::BindInterface AddRuleRpc; - Rpc>::BindInterface RemoveRuleRpc; - Rpc, RpcArg>::BindInterface UpdateRuleRpc; - - Rpc, RpcArg, RpcArg>::BindInterface AddRuleForPeerRpc; - - ProximityInterestHandler* m_interestHandler; - unordered_map m_rules; - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterest - */ - ProximityInterest::ProximityInterest(ProximityInterestHandler* handler) - : m_bbox(AZ::Aabb::CreateNull()) - , m_handler(handler) - { - AZ_Assert(m_handler, "Invalid interest handler"); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestRule - */ - void ProximityInterestRule::Set(const AZ::Aabb& bbox) - { - m_bbox = bbox; - m_handler->UpdateRule(this); - } - - void ProximityInterestRule::Destroy() - { - m_handler->DestroyRule(this); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestAttribute - */ - void ProximityInterestAttribute::Set(const AZ::Aabb& bbox) - { - m_bbox = bbox; - m_handler->UpdateAttribute(this); - } - - void ProximityInterestAttribute::Destroy() - { - m_handler->DestroyAttribute(this); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestHandler - */ - ProximityInterestHandler::ProximityInterestHandler() - : m_im(nullptr) - , m_rm(nullptr) - , m_lastRuleNetId(0) - , m_rulesReplica(nullptr) - { - if (!ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(ReplicaChunkClassId(ProximityInterestChunk::GetChunkName()))) - { - ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - } - } - - ProximityInterestRule::Ptr ProximityInterestHandler::CreateRule(PeerId peerId) - { - ProximityInterestRule* rulePtr = aznew ProximityInterestRule(this, peerId, GetNewRuleNetId()); - if (peerId == m_rm->GetLocalPeerId()) - { - m_rulesReplica->AddRuleRpc(rulePtr->GetNetworkId(), rulePtr->Get()); - m_localRules.insert(rulePtr); - } - - return rulePtr; - } - - void ProximityInterestHandler::FreeRule(ProximityInterestRule* rule) - { - //TODO: should be pool-allocated - delete rule; - } - - void ProximityInterestHandler::DestroyRule(ProximityInterestRule* rule) - { - if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId()) - { - m_rulesReplica->RemoveRuleRpc(rule->GetNetworkId()); - } - - rule->m_bbox = AZ::Aabb::CreateNull(); - m_rules.insert(rule); - m_localRules.erase(rule); - } - - void ProximityInterestHandler::UpdateRule(ProximityInterestRule* rule) - { - if (rule->GetPeerId() == m_rm->GetLocalPeerId()) - { - m_rulesReplica->UpdateRuleRpc(rule->GetNetworkId(), rule->Get()); - } - - m_rules.insert(rule); - } - - ProximityInterestAttribute::Ptr ProximityInterestHandler::CreateAttribute(ReplicaId replicaId) - { - return aznew ProximityInterestAttribute(this, replicaId); - } - - void ProximityInterestHandler::FreeAttribute(ProximityInterestAttribute* attrib) - { - //TODO: should be pool-allocated - delete attrib; - } - - void ProximityInterestHandler::DestroyAttribute(ProximityInterestAttribute* attrib) - { - attrib->m_bbox = AZ::Aabb::CreateNull(); - m_attributes.insert(attrib); - } - - void ProximityInterestHandler::UpdateAttribute(ProximityInterestAttribute* attrib) - { - m_attributes.insert(attrib); - } - - void ProximityInterestHandler::OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer) - { - if (chunk != m_rulesReplica) // non-local - { - m_peerChunks.insert(AZStd::make_pair(peer->GetId(), chunk)); - - for (auto& rule : m_localRules) - { - chunk->AddRuleForPeerRpc(rule->GetNetworkId(), rule->GetPeerId(), rule->Get()); - } - } - } - - void ProximityInterestHandler::OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer) - { - (void)chunk; - m_peerChunks.erase(peer->GetId()); - } - - RuleNetworkId ProximityInterestHandler::GetNewRuleNetId() - { - ++m_lastRuleNetId; - return m_rulesReplica->GetReplicaId() | (static_cast(m_lastRuleNetId) << 32); - } - - ProximityInterestChunk* ProximityInterestHandler::FindRulesChunkByPeerId(PeerId peerId) - { - auto it = m_peerChunks.find(peerId); - if (it == m_peerChunks.end()) - { - return nullptr; - } - else - { - return it->second; - } - } - - const InterestMatchResult& ProximityInterestHandler::GetLastResult() - { - return m_resultCache; - } - - void ProximityInterestHandler::UpdateInternal(InterestMatchResult& result) - { - ////////////////////////////////////////////// - // just recalculating the whole state for now - for (auto attrIt = m_attributes.begin(); attrIt != m_attributes.end(); ) - { - ProximityInterestAttribute* attr = *attrIt; - - auto resultIt = result.insert(attr->GetReplicaId()); - for (auto ruleIt = m_rules.begin(); ruleIt != m_rules.end(); ++ruleIt) - { - ProximityInterestRule* rule = *ruleIt; - if (rule->m_bbox.Overlaps(attr->m_bbox)) - { - resultIt.first->second.insert(rule->GetPeerId()); - } - } - - if ((*attrIt)->IsDeleted()) - { - attrIt = m_attributes.erase(attrIt); - delete attr; - } - else - { - ++attrIt; - } - } - - for (auto ruleIt = m_rules.begin(); ruleIt != m_rules.end(); ) - { - ProximityInterestRule* rule = *ruleIt; - - if (rule->IsDeleted()) - { - ruleIt = m_rules.erase(ruleIt); - delete rule; - } - else - { - ++ruleIt; - } - } - ////////////////////////////////////////////// - } - - void ProximityInterestHandler::Update() - { - m_resultCache.clear(); - - UpdateInternal(m_resultCache); - } - - void ProximityInterestHandler::OnRulesHandlerRegistered(InterestManager* manager) - { - AZ_Assert(m_im == nullptr, "Handler is already registered with manager %p (%p)\n", m_im, manager); - AZ_Assert(m_rulesReplica == nullptr, "Rules replica is already created\n"); - AZ_TracePrintf("GridMate", "Proximity interest handler is registered\n"); - m_im = manager; - m_rm = m_im->GetReplicaManager(); - m_rm->RegisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4), this); - - auto replica = Replica::CreateReplica("ProximityInterestHandlerRules"); - m_rulesReplica = CreateAndAttachReplicaChunk(replica); - m_rm->AddPrimary(replica); - } - - void ProximityInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager) - { - (void)manager; - AZ_Assert(m_im == manager, "Handler was not registered with manager %p (%p)\n", manager, m_im); - AZ_TracePrintf("GridMate", "Proximity interest handler is unregistered\n"); - m_rulesReplica = nullptr; - m_im = nullptr; - m_rm->UnregisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)); - m_rm = nullptr; - - for (auto& chunk : m_peerChunks) - { - chunk.second->m_interestHandler = nullptr; - } - m_peerChunks.clear(); - m_localRules.clear(); - - for (ProximityInterestRule* rule : m_rules) - { - delete rule; - } - - for (ProximityInterestAttribute* attr : m_attributes) - { - delete attr; - } - - m_attributes.clear(); - m_rules.clear(); - - m_resultCache.clear(); - } - /////////////////////////////////////////////////////////////////////////// -} -#else -// Optimized spatial handler. -#include "GridMate/Replica/Interest/ProximityInterestHandler.h" -#endif - -namespace UnitTest { - -/* - * Helper class to capture performance of various Interest Managers - */ -class PerfForInterestManager -{ -public: - void Reset(); - - void PreUpdate(); - void PostUpdate(); - - AZ::u32 GetTotalFrames() const; - float GetAverageFrame() const; - float GetWorstFrame() const; - float GetBestFrame() const; - -private: - AZ::Debug::Timer m_timer; - AZ::u32 m_frameCount = 0; - float m_totalUpdateTime = 0.f; - float m_fastestFrame = 100.f; - float m_slowestFrame = 0.f; -}; - -PerfForInterestManager g_PerfIM = PerfForInterestManager(); -PerfForInterestManager g_PerfUpdatingAttributes = PerfForInterestManager(); - -/* -* Utility function to tick the replica manager -*/ -static void UpdateReplicas(ReplicaManager* replicaManager, InterestManager* interestManager) -{ - if (interestManager) - { - // Measuring time it takes to execute an update. - g_PerfIM.PreUpdate(); - interestManager->Update(); - g_PerfIM.PostUpdate(); - } - - if (replicaManager) - { - replicaManager->Unmarshal(); - replicaManager->UpdateFromReplicas(); - replicaManager->UpdateReplicas(); - replicaManager->Marshal(); - } -} - -class Integ_InterestTest - : public GridMateMPTestFixture -{ - /////////////////////////////////////////////////////////////////// - class InterestTestChunk - : public ReplicaChunk - { - public: - GM_CLASS_ALLOCATOR(InterestTestChunk); - - InterestTestChunk() - : m_data("Data", 0) - , m_bitmaskAttributeData("BitmaskAttributeData") - , m_attribute(nullptr) - { - } - - /////////////////////////////////////////////////////////////////// - typedef AZStd::intrusive_ptr Ptr; - static const char* GetChunkName() { return "InterestTestChunk"; } - bool IsReplicaMigratable() override { return false; } - bool IsBroadcast() override - { - return false; - } - /////////////////////////////////////////////////////////////////// - - void OnReplicaActivate(const ReplicaContext& rc) override - { - AZ_Printf("GridMate", "InterestTestChunk::OnReplicaActivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n", - GetReplicaId(), - IsPrimary() ? "primary" : "proxy", - rc.m_peer ? rc.m_peer->GetId() : 0, - rc.m_rm->GetLocalPeerId()); - - BitmaskInterestHandler* ih = static_cast(rc.m_rm->GetUserContext(AZ_CRC("BitmaskInterestHandler", 0x5bf5d75b))); - if (ih) - { - m_attribute = ih->CreateAttribute(GetReplicaId()); - m_attribute->Set(m_bitmaskAttributeData.Get()); - } - } - - void OnReplicaDeactivate(const ReplicaContext& rc) override - { - AZ_Printf("GridMate", "InterestTestChunk::OnReplicaDeactivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n", - GetReplicaId(), - IsPrimary() ? "primary" : "proxy", - rc.m_peer ? rc.m_peer->GetId() : 0, - rc.m_rm->GetLocalPeerId()); - - m_attribute = nullptr; - } - - void BitmaskHandler(const InterestBitmask& bitmask, const TimeContext&) - { - if (m_attribute) - { - m_attribute->Set(bitmask); - } - } - - DataSet m_data; - DataSet::BindInterface m_bitmaskAttributeData; - BitmaskInterestAttribute::Ptr m_attribute; - }; - /////////////////////////////////////////////////////////////////// - - class TestPeerInfo - : public SessionEventBus::Handler - { - public: - TestPeerInfo() - : m_gridMate(nullptr) - , m_lanSearch(nullptr) - , m_session(nullptr) - , m_im(nullptr) - , m_bitmaskHandler(nullptr) - , m_num(0) - { - GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - } - - void CreateTestReplica() - { - m_im = aznew InterestManager(); - InterestManagerDesc desc; - desc.m_rm = m_session->GetReplicaMgr(); - - m_im->Init(desc); - - m_bitmaskHandler = aznew BitmaskInterestHandler(); - m_im->RegisterHandler(m_bitmaskHandler); - - m_rule = m_bitmaskHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId()); - m_rule->Set(1 << m_num); - - auto r = Replica::CreateReplica("InterestTestReplica"); - m_replica = CreateAndAttachReplicaChunk(r); - - // Initializing attribute - // Shifing all by one - peer0 will recv from peer1, peer2 will recv from peer2, peer2 will recv from peer0 - unsigned i = (m_num + 2) % Integ_InterestTest::k_numMachines; - m_replica->m_data.Set(m_num); - m_replica->m_bitmaskAttributeData.Set(1 << i); - - m_session->GetReplicaMgr()->AddPrimary(r); - } - - void UpdateAttribute() - { - // Shifing all by one - peer0 will recv from peer2, peer1 will recv from peer0, peer2 will recv from peer1 - unsigned i = (m_num + 1) % Integ_InterestTest::k_numMachines; - m_replica->m_bitmaskAttributeData.Set(1 << i); - m_replica->m_attribute->Set(1 << i); - } - - void DeleteAttribute() - { - m_replica->m_attribute = nullptr; - } - - void UpdateRule() - { - m_rule->Set(0xffff); - } - - void DeleteRule() - { - m_rule = nullptr; - } - - void CreateRule() - { - m_rule = m_bitmaskHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId()); - m_rule->Set(0xffff); - } - - void OnSessionCreated(GridSession* session) override - { - m_session = session; - if (m_session->IsHost()) - { - CreateTestReplica(); - } - } - - void OnSessionJoined(GridSession* session) override - { - m_session = session; - CreateTestReplica(); - } - - void OnSessionDelete(GridSession* session) override - { - if (session == m_session) - { - m_rule = nullptr; - m_session = nullptr; - m_im->UnregisterHandler(m_bitmaskHandler); - delete m_bitmaskHandler; - delete m_im; - m_im = nullptr; - m_bitmaskHandler = nullptr; - } - } - - void OnSessionError(GridSession* session, const string& errorMsg) override - { - (void)session; - (void)errorMsg; - AZ_TracePrintf("GridMate", "Session error: %s\n", errorMsg.c_str()); - } - - IGridMate* m_gridMate; - GridSearch* m_lanSearch; - GridSession* m_session; - InterestManager* m_im; - BitmaskInterestHandler* m_bitmaskHandler; - - BitmaskInterestRule::Ptr m_rule; - unsigned m_num; - InterestTestChunk::Ptr m_replica; - }; - -public: - Integ_InterestTest() - { - ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - - ////////////////////////////////////////////////////////////////////////// - // Create all grid mates - m_peers[0].m_gridMate = m_gridMate; - m_peers[0].SessionEventBus::Handler::BusConnect(m_peers[0].m_gridMate); - m_peers[0].m_num = 0; - for (int i = 1; i < k_numMachines; ++i) - { - GridMateDesc desc; - m_peers[i].m_gridMate = GridMateCreate(desc); - AZ_TEST_ASSERT(m_peers[i].m_gridMate); - - m_peers[i].m_num = i; - m_peers[i].SessionEventBus::Handler::BusConnect(m_peers[i].m_gridMate); - } - ////////////////////////////////////////////////////////////////////////// - - for (int i = 0; i < k_numMachines; ++i) - { - // start the multiplayer service (session mgr, extra allocator, etc.) - StartGridMateService(m_peers[i].m_gridMate, SessionServiceDesc()); - AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_peers[i].m_gridMate) != nullptr); - } - } - virtual ~Integ_InterestTest() - { - StopGridMateService(m_peers[0].m_gridMate); - - for (int i = 1; i < k_numMachines; ++i) - { - if (m_peers[i].m_gridMate) - { - m_peers[i].SessionEventBus::Handler::BusDisconnect(); - GridMateDestroy(m_peers[i].m_gridMate); - } - } - - // this will stop the first IGridMate which owns the memory allocators. - m_peers[0].SessionEventBus::Handler::BusDisconnect(); - } - - void run() - { - TestCarrierDesc carrierDesc; - carrierDesc.m_enableDisconnectDetection = false;// true; - carrierDesc.m_threadUpdateTimeMS = 10; - carrierDesc.m_familyType = Driver::BSD_AF_INET; - - - LANSessionParams sp; - sp.m_topology = ST_PEER_TO_PEER; - sp.m_numPublicSlots = 64; - sp.m_port = k_hostPort; - EBUS_EVENT_ID_RESULT(m_peers[k_host].m_session, m_peers[k_host].m_gridMate, LANSessionServiceBus, HostSession, sp, carrierDesc); - m_peers[k_host].m_session->GetReplicaMgr()->SetAutoBroadcast(false); - - int listenPort = k_hostPort; - for (int i = 0; i < k_numMachines; ++i) - { - if (i == k_host) - { - continue; - } - - LANSearchParams searchParams; - searchParams.m_serverPort = k_hostPort; - searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port, the rest specify return ports - searchParams.m_familyType = Driver::BSD_AF_INET; - EBUS_EVENT_ID_RESULT(m_peers[i].m_lanSearch, m_peers[i].m_gridMate, LANSessionServiceBus, StartGridSearch, searchParams); - } - - - static const int maxNumUpdates = 300; - int numUpdates = 0; - TimeStamp time = AZStd::chrono::system_clock::now(); - - while (numUpdates <= maxNumUpdates) - { - if (numUpdates == 100) - { - // Checking everybody received only one replica: - // peer0 -> rep1, peer1 -> rep2, peer2 -> rep0 - for (int i = 0; i < k_numMachines; ++i) - { - ReplicaId repId = m_peers[(i + 1) % k_numMachines].m_replica->GetReplicaId(); - auto repRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(repRecv != nullptr); - - repId = m_peers[(i + 2) % k_numMachines].m_replica->GetReplicaId(); - auto repNotRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(repNotRecv == nullptr); - - // rotating mask left - m_peers[i].UpdateAttribute(); - } - } - - if (numUpdates == 150) - { - // Checking everybody received only one replica: - // peer0 -> rep2, peer1 -> rep0, peer2 -> rep1 - for (int i = 0; i < k_numMachines; ++i) - { - ReplicaId repId = m_peers[(i + 2) % k_numMachines].m_replica->GetReplicaId(); - auto repRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(repRecv != nullptr); - - repId = m_peers[(i + 1) % k_numMachines].m_replica->GetReplicaId(); - auto repNotRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(repNotRecv == nullptr); - - // setting rules to accept all replicas - m_peers[i].UpdateRule(); - } - } - - if (numUpdates == 200) - { - // Checking everybody received all replicas - for (int i = 0; i < k_numMachines; ++i) - { - for (int j = 0; j < k_numMachines; ++j) - { - ReplicaId repId = m_peers[j].m_replica->GetReplicaId(); - auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(rep); - } - - // Deleting all attributes - m_peers[i].DeleteAttribute(); - } - } - - if (numUpdates == 250) - { - // Checking everybody lost all replicas (except primary) - for (int i = 0; i < k_numMachines; ++i) - { - for (int j = 0; j < k_numMachines; ++j) - { - if (i == j) - { - continue; - } - - ReplicaId repId = m_peers[j].m_replica->GetReplicaId(); - auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(rep == nullptr); - } - - // deleting all rules - m_peers[i].DeleteRule(); - } - } - - ////////////////////////////////////////////////////////////////////////// - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_gridMate) - { - m_peers[i].m_gridMate->Update(); - if (m_peers[i].m_session) - { - UpdateReplicas(m_peers[i].m_session->GetReplicaMgr(), m_peers[i].m_im); - } - } - } - Update(); - ////////////////////////////////////////////////////////////////////////// - - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_lanSearch && m_peers[i].m_lanSearch->IsDone()) - { - AZ_TEST_ASSERT(m_peers[i].m_lanSearch->GetNumResults() == 1); - JoinParams jp; - EBUS_EVENT_ID_RESULT(m_peers[i].m_session, m_peers[i].m_gridMate, LANSessionServiceBus, JoinSessionBySearchInfo, static_cast(*m_peers[i].m_lanSearch->GetResult(0)), jp, carrierDesc); - m_peers[i].m_session->GetReplicaMgr()->SetAutoBroadcast(false); - - m_peers[i].m_lanSearch->Release(); - m_peers[i].m_lanSearch = nullptr; - } - } - - ////////////////////////////////////////////////////////////////////////// - // Debug Info - TimeStamp now = AZStd::chrono::system_clock::now(); - if (AZStd::chrono::milliseconds(now - time).count() > 1000) - { - time = now; - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_session == nullptr) - { - continue; - } - - if (m_peers[i].m_session->IsHost()) - { - AZ_Printf("GridMate", "------ Host %d ------\n", i); - } - else - { - AZ_Printf("GridMate", "------ Client %d ------\n", i); - } - - AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(), m_peers[i].m_session->IsHost() ? "yes" : "no", m_peers[i].m_session->GetTime()); - for (unsigned int iMember = 0; iMember < m_peers[i].m_session->GetNumberOfMembers(); ++iMember) - { - GridMember* member = m_peers[i].m_session->GetMemberByIndex(iMember); - AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no"); - } - AZ_Printf("GridMate", "\n"); - } - } - ////////////////////////////////////////////////////////////////////////// - - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30)); - numUpdates++; - } - } - - static const int k_numMachines = 3; - static const int k_host = 0; - static const int k_hostPort = 5450; - - TestPeerInfo m_peers[k_numMachines]; -}; - -/* - * Testing worse case performance of thousands of replicas and a few peers where all replicas/attributes change every frame - * and peers/rules change every frame as well. - */ -class LargeWorldTest - : public GridMateMPTestFixture -{ - /////////////////////////////////////////////////////////////////// - class LargeWorldTestChunk - : public ReplicaChunk - { - public: - GM_CLASS_ALLOCATOR(LargeWorldTestChunk); - - LargeWorldTestChunk() - : m_data("Data", 0) - , m_proximityAttributeData("LargeWorldAttributeData") - , m_attribute(nullptr) - { - } - - /////////////////////////////////////////////////////////////////// - typedef AZStd::intrusive_ptr Ptr; - static const char* GetChunkName() { return "LargeWorldTestChunk"; } - bool IsReplicaMigratable() override { return false; } - bool IsBroadcast() override - { - return false; - } - /////////////////////////////////////////////////////////////////// - - void OnReplicaActivate(const ReplicaContext& rc) override - { - /*if (!IsPrimary())*/ - /*{ - AZ_Printf("GridMate", "LargeWorldTestChunk::OnReplicaActivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n", - GetReplicaId(), - IsPrimary() ? "primary" : "proxy", - rc.m_peer ? rc.m_peer->GetId() : 0, - rc.m_rm->GetLocalPeerId()); - }*/ - - if (ProximityInterestHandler* ih = static_cast(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)))) - { - m_attribute = ih->CreateAttribute(GetReplicaId()); - m_attribute->Set(m_proximityAttributeData.Get()); - } - } - - void OnReplicaDeactivate(const ReplicaContext& /*rc*/) override - { - m_attribute = nullptr; - } - - void ProximityHandler(const AZ::Aabb& bounds, const TimeContext&) - { - if (m_attribute) - { - m_attribute->Set(bounds); - } - } - - DataSet m_data; - DataSet::BindInterface m_proximityAttributeData; - ProximityInterestAttribute::Ptr m_attribute; - }; - /////////////////////////////////////////////////////////////////// - - struct LargeWorldParams - { - AZ::u32 index = 0; - - const float commonSize = 50; - const AZ::Aabb box = AZ::Aabb::CreateFromMinMax( - AZ::Vector3::CreateZero(), - AZ::Vector3::CreateOne() * commonSize); - const float commonStep = commonSize + 1; - }; - - static LargeWorldParams& GetWorldParams() - { - static LargeWorldParams worldParams; - return worldParams; - } - - /* - * Create a chain of boxes in spaces along X axis - */ - static AZ::Aabb CreateNextRuleSpace() - { - auto offset = GetWorldParams().commonStep * aznumeric_cast(GetWorldParams().index); - - float min[] = { offset, 0, 0 }; - float max[] = { - GetWorldParams().commonSize + offset, - GetWorldParams().commonSize, - GetWorldParams().commonSize }; - - auto bounds = AZ::Aabb::CreateFromMinMax( - AZ::Vector3::CreateFromFloat3(min), - AZ::Vector3::CreateFromFloat3(max)); - - GetWorldParams().index++; - return bounds; - } - - class LargeWorldTestPeerInfo - : public SessionEventBus::Handler - { - public: - LargeWorldTestPeerInfo() - : m_gridMate(nullptr) - , m_lanSearch(nullptr) - , m_session(nullptr) - , m_im(nullptr) - , m_proximityHandler(nullptr) - , m_num(0) - { - GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - } - - ~LargeWorldTestPeerInfo() - { - SessionEventBus::Handler::BusDisconnect(); - } - - void CreateHostRuleHandler() - { - m_im = aznew InterestManager(); - InterestManagerDesc desc; - desc.m_rm = m_session->GetReplicaMgr(); - - m_im->Init(desc); - - m_proximityHandler = aznew ProximityInterestHandler(); - m_im->RegisterHandler(m_proximityHandler); - - m_rule = m_proximityHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId()); - m_rule->Set(AZ::Aabb::CreateNull()); // host rule doesn't matter in this test - } - - void CreateRuleHandler() - { - m_im = aznew InterestManager(); - InterestManagerDesc desc; - desc.m_rm = m_session->GetReplicaMgr(); - - m_im->Init(desc); - - m_proximityHandler = aznew ProximityInterestHandler(); - m_im->RegisterHandler(m_proximityHandler); - - m_rule = m_proximityHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId()); - m_rule->Set(CreateNextRuleSpace()); - } - - void CreateTestReplica(const AZ::Aabb& bounds) - { - auto r = Replica::CreateReplica("LargeWorldTestReplica"); - auto replica = CreateAndAttachReplicaChunk(r); - - // Initializing attribute - replica->m_data.Set(m_num); - replica->m_proximityAttributeData.Set(bounds); - - m_replicas.push_back(replica); - - m_session->GetReplicaMgr()->AddPrimary(r); - } - - void PopulateWorld() - { - AZ_Printf("GridMate", "LargeWorldTestChunk::PopulateWorld() starting...\n"); - - const float worldSizeInBoxes = 50; - const auto oneBox = AZ::Vector3::CreateOne(); - const auto thickness = 1; - for (float dx = 0; dx < worldSizeInBoxes; ++dx) - { - for (float dy = 0; dy < thickness; ++dy) - { - for (float dz = 0; dz < thickness; ++dz) - { - auto aabb = AZ::Aabb::CreateFromMinMax( - AZ::Vector3(50 * dx + 5, dy, dz), - AZ::Vector3(50 * dx + 5, dy, dz) + oneBox); - - CreateTestReplica(aabb); - } - } - } - - AZ_Printf("GridMate", "LargeWorldTestChunk::PopulateWorld() ... DONE\n"); - } - - void UpdateAttribute(LargeWorldTestChunk::Ptr replica) - { - if (replica && replica->m_attribute) - { - auto sameValue = replica->m_attribute->Get(); - - replica->m_proximityAttributeData.Set(sameValue); - replica->m_attribute->Set(sameValue); - } - } - - void UpdateRule() - { - // just make it dirty for now - if (m_rule) - { - auto sameValue = m_rule->Get(); - m_rule->Set(sameValue); - } - } - - void DeleteRule() - { - m_rule = nullptr; - } - - void OnSessionCreated(GridSession* session) override - { - m_session = session; - if (m_session->IsHost()) - { - CreateHostRuleHandler(); - PopulateWorld(); - } - } - - void OnSessionJoined(GridSession* session) override - { - m_session = session; - CreateRuleHandler(); - } - - void OnSessionDelete(GridSession* session) override - { - if (session == m_session) - { - m_rule = nullptr; - m_session = nullptr; - m_im->UnregisterHandler(m_proximityHandler); - delete m_proximityHandler; - delete m_im; - m_im = nullptr; - m_proximityHandler = nullptr; - } - } - - void OnSessionError(GridSession* session, const string& errorMsg) override - { - (void)session; - (void)errorMsg; - AZ_TracePrintf("GridMate", "Session error: %s\n", errorMsg.c_str()); - } - - IGridMate* m_gridMate; - GridSearch* m_lanSearch; - GridSession* m_session; - InterestManager* m_im; - ProximityInterestHandler* m_proximityHandler; - - ProximityInterestRule::Ptr m_rule; - unsigned m_num; - - AZStd::vector m_replicas; - }; - -public: - LargeWorldTest() : UnitTest::GridMateMPTestFixture(500u * 1024u * 1024u) // 500Mb - { - ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - - ////////////////////////////////////////////////////////////////////////// - // Create all grid mates - m_peers[0].m_gridMate = m_gridMate; - m_peers[0].SessionEventBus::Handler::BusConnect(m_peers[0].m_gridMate); - m_peers[0].m_num = 0; - for (int i = 1; i < k_numMachines; ++i) - { - GridMateDesc desc; - m_peers[i].m_gridMate = GridMateCreate(desc); - AZ_TEST_ASSERT(m_peers[i].m_gridMate); - - m_peers[i].m_num = i; - m_peers[i].SessionEventBus::Handler::BusConnect(m_peers[i].m_gridMate); - } - ////////////////////////////////////////////////////////////////////////// - - for (int i = 0; i < k_numMachines; ++i) - { - // start the multiplayer service (session mgr, extra allocator, etc.) - StartGridMateService(m_peers[i].m_gridMate, SessionServiceDesc()); - AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_peers[i].m_gridMate) != nullptr); - } - } - virtual ~LargeWorldTest() - { - StopGridMateService(m_peers[0].m_gridMate); - - for (int i = 1; i < k_numMachines; ++i) - { - if (m_peers[i].m_gridMate) - { - m_peers[i].SessionEventBus::Handler::BusDisconnect(); - GridMateDestroy(m_peers[i].m_gridMate); - } - } - - // this will stop the first IGridMate which owns the memory allocators. - m_peers[0].SessionEventBus::Handler::BusDisconnect(); - } - - void run() - { - g_PerfIM.Reset(); - g_PerfUpdatingAttributes.Reset(); - - TestCarrierDesc carrierDesc; - carrierDesc.m_enableDisconnectDetection = false; - carrierDesc.m_threadUpdateTimeMS = 10; - carrierDesc.m_familyType = Driver::BSD_AF_INET; - - LANSessionParams sp; - sp.m_topology = ST_PEER_TO_PEER; - sp.m_numPublicSlots = 64; - sp.m_port = k_hostPort; - EBUS_EVENT_ID_RESULT(m_peers[k_host].m_session, m_peers[k_host].m_gridMate, LANSessionServiceBus, HostSession, sp, carrierDesc); - m_peers[k_host].m_session->GetReplicaMgr()->SetAutoBroadcast(false); - - int listenPort = k_hostPort; - for (int i = 0; i < k_numMachines; ++i) - { - if (i == k_host) - { - continue; - } - - LANSearchParams searchParams; - searchParams.m_serverPort = k_hostPort; - searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port, the rest specify return ports - searchParams.m_familyType = Driver::BSD_AF_INET; - EBUS_EVENT_ID_RESULT(m_peers[i].m_lanSearch, m_peers[i].m_gridMate, LANSessionServiceBus, StartGridSearch, searchParams); - } - - - static const int maxNumUpdates = 300; - int numUpdates = 0; - TimeStamp time = AZStd::chrono::system_clock::now(); - - while (numUpdates <= maxNumUpdates) - { - g_PerfUpdatingAttributes.PreUpdate(); - for (LargeWorldTestChunk::Ptr replica : m_peers[0].m_replicas) - { - m_peers[0].UpdateAttribute(replica); - } - g_PerfUpdatingAttributes.PostUpdate(); - - for (auto peer : m_peers) - { - peer.UpdateRule(); - } - - //if (numUpdates == 100) - //{ - // // check how many replicas each client got - // for (AZ::u32 i = 1; i < k_numMachines; ++i) - // { - // AZ::u32 count = 0; - // for (auto& replica : m_peers[0].m_replicas) - // { - // ReplicaId repId = replica->GetReplicaId(); - // if (auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId)) - // { - // count++; - // } - // } - - // const AZ::Aabb& bounds = m_peers[i].m_rule->Get(); - - // AZ_Printf("GridMate", "Session %s Members: %d Bounds: %f.%f.%f-%f.%f.%f Replicas: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(), - // static_cast(bounds.GetMin().GetX()), - // static_cast(bounds.GetMin().GetY()), - // static_cast(bounds.GetMin().GetZ()), - // static_cast(bounds.GetMax().GetX()), - // static_cast(bounds.GetMax().GetY()), - // static_cast(bounds.GetMax().GetZ()), count); - - // AZ_Assert(count == 1, "Should have at least some replicas to start with"); - // } - //} - - if (numUpdates == 200) - { - // Deleting all attributes - for (auto& replica : m_peers[0].m_replicas) - { - replica->m_attribute = nullptr; - } - } - - if (numUpdates == 250) - { - // Checking everybody lost all replicas (except primary) - for (int i = 0; i < k_numMachines; ++i) - { - /*for (int j = 0; j < k_numMachines; ++j) - { - if (i == j) - { - continue; - } - - ReplicaId repId = m_peers[j].m_replica->GetReplicaId(); - auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(rep == nullptr); - }*/ - - // deleting all rules - m_peers[i].DeleteRule(); - } - } - - ////////////////////////////////////////////////////////////////////////// - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_gridMate) - { - m_peers[i].m_gridMate->Update(); - if (m_peers[i].m_session) - { - UpdateReplicas(m_peers[i].m_session->GetReplicaMgr(), m_peers[i].m_im); - } - } - } - Update(); - ////////////////////////////////////////////////////////////////////////// - - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_lanSearch && m_peers[i].m_lanSearch->IsDone()) - { - AZ_TEST_ASSERT(m_peers[i].m_lanSearch->GetNumResults() == 1); - JoinParams jp; - EBUS_EVENT_ID_RESULT(m_peers[i].m_session, m_peers[i].m_gridMate, LANSessionServiceBus, JoinSessionBySearchInfo, static_cast(*m_peers[i].m_lanSearch->GetResult(0)), jp, carrierDesc); - m_peers[i].m_session->GetReplicaMgr()->SetAutoBroadcast(false); - - m_peers[i].m_lanSearch->Release(); - m_peers[i].m_lanSearch = nullptr; - } - } - - ////////////////////////////////////////////////////////////////////////// - // Debug Info - TimeStamp now = AZStd::chrono::system_clock::now(); - if (AZStd::chrono::milliseconds(now - time).count() > 1000) - { - time = now; - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_session == nullptr) - { - continue; - } - - if (m_peers[i].m_session->IsHost()) - { - AZ_Printf("GridMate", "------ Host %d ------\n", i); - } - else - { - AZ_Printf("GridMate", "------ Client %d ------\n", i); - } - - AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(), m_peers[i].m_session->IsHost() ? "yes" : "no", m_peers[i].m_session->GetTime()); - for (unsigned int iMember = 0; iMember < m_peers[i].m_session->GetNumberOfMembers(); ++iMember) - { - GridMember* member = m_peers[i].m_session->GetMemberByIndex(iMember); - AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no"); - } - AZ_Printf("GridMate", "\n"); - } - } - ////////////////////////////////////////////////////////////////////////// - - //AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30)); - numUpdates++; - } - - auto averageFrame = g_PerfIM.GetAverageFrame(); - auto bestFrame = g_PerfIM.GetBestFrame(); - auto worstFrame = g_PerfIM.GetWorstFrame(); - auto frames = g_PerfIM.GetTotalFrames(); - AZ_Printf("GridMate", "Interest manager performance: average_frame = %f sec, frames = %d, best= %f sec, worst= %f sec\n", - averageFrame, frames, bestFrame, worstFrame); - - AZ_Printf("GridMate", "Updating attributes: average_frame = %f sec, frames = %d, best= %f sec, worst= %f sec\n", - g_PerfUpdatingAttributes.GetAverageFrame(), - g_PerfUpdatingAttributes.GetTotalFrames(), - g_PerfUpdatingAttributes.GetBestFrame(), - g_PerfUpdatingAttributes.GetWorstFrame()); - } - - static const int k_numMachines = 3; - static const int k_host = 0; - static const int k_hostPort = 5450; - - LargeWorldTestPeerInfo m_peers[k_numMachines]; -}; - -void PerfForInterestManager::Reset() -{ - m_frameCount = 0; - m_totalUpdateTime = 0; - m_slowestFrame = 0; - m_fastestFrame = 100.f; -} - -void PerfForInterestManager::PreUpdate() -{ - m_timer.Stamp(); -} - -void PerfForInterestManager::PostUpdate() -{ - auto frameTime = m_timer.StampAndGetDeltaTimeInSeconds(); - m_totalUpdateTime += frameTime; - m_frameCount++; - - m_slowestFrame = AZStd::max(m_slowestFrame, frameTime); - m_fastestFrame = AZStd::min(m_fastestFrame, frameTime); -} - -AZ::u32 PerfForInterestManager::GetTotalFrames() const -{ - return m_frameCount; -} - -float PerfForInterestManager::GetAverageFrame() const -{ - if (m_frameCount > 0) - { - return m_totalUpdateTime / m_frameCount; - } - - return 0; -} - -float PerfForInterestManager::GetWorstFrame() const -{ - return m_slowestFrame; -} - -float PerfForInterestManager::GetBestFrame() const -{ - return m_fastestFrame; -} - -class ProximityHandlerTests - : public GridMateMPTestFixture -{ -public: - struct xyz - { - float x, y, z; - }; - - static AZ::Aabb CreateBox(xyz min, float size) - { - return AZ::Aabb::CreateFromMinMax( - AZ::Vector3::CreateFromFloat3(&min.x), - AZ::Vector3::CreateFromFloat3(&min.x) + AZ::Vector3::CreateOne() * size); - } - - static void run() - { - SimpleFirstUpdate(); - SecondUpdateAfterNoChanges(); - SimpleOutsideOfRule(); - AttributeMovingOutsideOfRule(); - RuleMovingAndAttributeIsOut(); - RuleDestroyed(); - AttributeDestroyed(); - } - - static void RuleMovingAndAttributeIsOut() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - InterestMatchResult results = handler->GetLastResult(); - //ProximityInterestHandler::DebugPrint(results, "1st"); - - AZ_TEST_ASSERT(results[1].size() == 1); - AZ_TEST_ASSERT(results[1].find(100) != results[1].end()); - - // now move the attribute outside of the rule - rule1->Set(CreateBox({ 1000, 0, 0 }, 100)); - - handler->Update(); - results = handler->GetLastResult(); - //ProximityInterestHandler::DebugPrint(results, "2nd"); - - AZ_TEST_ASSERT(results[1].size() == 0); - } - - static void AttributeMovingOutsideOfRule() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - InterestMatchResult results = handler->GetLastResult(); - //ProximityInterestHandler::DebugPrint(results, "1st"); - - AZ_TEST_ASSERT(results[1].size() == 1); - AZ_TEST_ASSERT(results[1].find(100) != results[1].end()); - - // now move the attribute outside of the rule - attribute1->Set(CreateBox({ -1000, 0, 0 }, 10)); - - handler->Update(); - results = handler->GetLastResult(); - //ProximityInterestHandler::DebugPrint(results, "2nd"); - - AZ_TEST_ASSERT(results[1].size() == 0); - } - - static void SimpleFirstUpdate() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - - //ProximityInterestHandler::PrintMatchResult(results, "test"); - - AZ_TEST_ASSERT(results[1].size() == 1); - AZ_TEST_ASSERT(results[1].find(100) != results[1].end()); - } - - static void SecondUpdateAfterNoChanges() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - - //ProximityInterestHandler::PrintMatchResult(results, "test"); - - AZ_TEST_ASSERT(results.size() == 0); - } - - static void SimpleOutsideOfRule() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ -1000, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - - //ProximityInterestHandler::PrintMatchResult(results, "test"); - - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 0); - } - - static void RuleDestroyed() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - { - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 1); - } - - // rule1 should have been destroyed by now - - handler->Update(); - InterestMatchResult results = handler->GetLastResult(); - //ProximityInterestHandler::PrintMatchResult(results, "last"); - - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 0); - } - - static void AttributeDestroyed() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - { - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 1); - } - - // attribute1 should have been destroyed by now, but it will show up once to remove it from affected peers - handler->Update(); - InterestMatchResult results = handler->GetLastResult(); - results.PrintMatchResult("last"); - - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 0); - - // and now attribute1 should not show up in the changes - handler->Update(); - results = handler->GetLastResult(); - results.PrintMatchResult("last"); - - AZ_TEST_ASSERT(results.size() == 0); - } -}; - -}; // namespace UnitTest - -GM_TEST_SUITE(InterestSuite) - GM_TEST(Integ_InterestTest); -#if AZ_TRAIT_GRIDMATE_TEST_EXCLUDE_LARGEWORLDTEST != 0 - GM_TEST(LargeWorldTest); -#endif - GM_TEST(ProximityHandlerTests); -GM_TEST_SUITE_END() diff --git a/Code/Framework/GridMate/Tests/gridmate_test_files.cmake b/Code/Framework/GridMate/Tests/gridmate_test_files.cmake index a8b1dbd120..90ccbbf9a9 100644 --- a/Code/Framework/GridMate/Tests/gridmate_test_files.cmake +++ b/Code/Framework/GridMate/Tests/gridmate_test_files.cmake @@ -24,5 +24,4 @@ set(FILES StreamSocketDriverTests.cpp CarrierStreamSocketDriverTests.cpp Carrier.cpp - Interest.cpp ) From fffff75479b2777f50a7aceeaab3049842875804 Mon Sep 17 00:00:00 2001 From: jromnoa Date: Tue, 15 Jun 2021 13:55:10 -0700 Subject: [PATCH 209/233] add file check for _build_file_paths() function --- scripts/build/tools/upload_to_s3.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/build/tools/upload_to_s3.py b/scripts/build/tools/upload_to_s3.py index 3694014056..80ffcdb8e1 100755 --- a/scripts/build/tools/upload_to_s3.py +++ b/scripts/build/tools/upload_to_s3.py @@ -121,7 +121,8 @@ def _build_file_paths(path_to_files, files_in_path): for file_in_path in files_in_path: complete_file_path = os.path.join(path_to_files, file_in_path) - parsed_file_paths.append(complete_file_path) + if os.path.isfile(complete_file_path): + parsed_file_paths.append(complete_file_path) return parsed_file_paths From 2eca923694423a6714baf0464d1b527866cbe716 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 15 Jun 2021 14:34:24 -0700 Subject: [PATCH 210/233] Rework session validation to work through Connect packet --- .../AutoGen/Multiplayer.AutoPackets.xml | 5 +- .../Source/MultiplayerSystemComponent.cpp | 81 ++++++++----------- .../Code/Source/MultiplayerSystemComponent.h | 3 +- 3 files changed, 36 insertions(+), 53 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml index 3abd2a86d9..ce8931107f 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml @@ -9,16 +9,13 @@ + - - - - diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index aa5e0d7467..5f44a2cccb 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -199,21 +199,10 @@ namespace Multiplayer { AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); + m_pendingConnectionTickets.push(config.m_playerSessionId); AZStd::string hostname = config.m_dnsName.empty() ? config.m_ipAddress : config.m_dnsName; const IpAddress ipAddress(hostname.c_str(), config.m_port, m_networkInterface->GetType()); - ConnectionId connectionId = m_networkInterface->Connect(ipAddress); - - AzNetworking::IConnection* connection = m_networkInterface->GetConnectionSet().GetConnection(connectionId); - if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so - { - connection->SetUserData(new ClientToServerConnectionData(connection, *this, config.m_playerSessionId)); - } - else - { - reinterpret_cast(connection->GetUserData())->SetProviderTicket(config.m_playerSessionId); - } - - connection->SendReliablePacket(MultiplayerPackets::ValidateSession(config.m_playerSessionId.c_str())); + m_networkInterface->Connect(ipAddress); return true; } @@ -416,6 +405,22 @@ namespace Multiplayer [[maybe_unused]] MultiplayerPackets::Connect& packet ) { + // Validate our session with the provider if any + if (AZ::Interface::Get() != nullptr) + { + AzFramework::PlayerConnectionConfig config; + config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); + config.m_playerSessionId = packet.GetTicket(); + if(!AZ::Interface::Get()->ValidatePlayerJoinSession(config)) + { + auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); }; + m_networkInterface->GetConnectionSet().VisitConnections(visitor); + return true; + } + + reinterpret_cast(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str()); + } + if (connection->SendReliablePacket(MultiplayerPackets::Accept(InvalidHostId, sv_map))) { // Sync our console @@ -441,31 +446,6 @@ namespace Multiplayer return true; } - bool MultiplayerSystemComponent::HandleRequest - ( - [[maybe_unused]] AzNetworking::IConnection* connection, - [[maybe_unused]] const IPacketHeader& packetHeader, - [[maybe_unused]] MultiplayerPackets::ValidateSession& packet - ) - { - // Validate our session with the provider if any - if (AZ::Interface::Get() != nullptr) - { - AzFramework::PlayerConnectionConfig config; - config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); - config.m_playerSessionId = packet.GetTicket(); - if(!AZ::Interface::Get()->ValidatePlayerJoinSession(config)) - { - connection->Disconnect(DisconnectReason::TerminatedByServer, TerminationEndpoint::Local); - return false; - } - - reinterpret_cast(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str()); - } - - return true; - } - bool MultiplayerSystemComponent::HandleRequest ( AzNetworking::IConnection* connection, @@ -595,10 +575,16 @@ namespace Multiplayer datum.m_isInvited = false; datum.m_agentType = MultiplayerAgentType::Client; + AZStd::string providerTicket; if (connection->GetConnectionRole() == ConnectionRole::Connector) { AZLOG_INFO("New outgoing connection to remote address: %s", connection->GetRemoteAddress().GetString().c_str()); - connection->SendReliablePacket(MultiplayerPackets::Connect(0)); + if (!m_pendingConnectionTickets.empty()) + { + providerTicket = m_pendingConnectionTickets.front(); + m_pendingConnectionTickets.pop(); + } + connection->SendReliablePacket(MultiplayerPackets::Connect(0, providerTicket.c_str())); } else { @@ -628,7 +614,11 @@ namespace Multiplayer { if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so { - connection->SetUserData(new ClientToServerConnectionData(connection, *this)); + connection->SetUserData(new ClientToServerConnectionData(connection, *this, providerTicket)); + } + else + { + reinterpret_cast(connection->GetUserData())->SetProviderTicket(providerTicket); } AZStd::unique_ptr window = AZStd::make_unique(); @@ -652,12 +642,7 @@ namespace Multiplayer AZStd::string reasonString = ToString(reason); AZLOG_INFO("%s due to %s from remote address: %s", endpointString, reasonString.c_str(), connection->GetRemoteAddress().GetString().c_str()); - if (connection->GetConnectionRole() == ConnectionRole::Acceptor) - { - // The authority is shutting down its connection - m_shutdownEvent.Signal(m_networkInterface); - } - else if (GetAgentType() == MultiplayerAgentType::Client && connection->GetConnectionRole() == ConnectionRole::Connector) + if (GetAgentType() == MultiplayerAgentType::Client && connection->GetConnectionRole() == ConnectionRole::Connector) { // The client is disconnecting m_clientDisconnectedEvent.Signal(); @@ -675,7 +660,7 @@ namespace Multiplayer if (m_agentType == MultiplayerAgentType::DedicatedServer || m_agentType == MultiplayerAgentType::ClientServer) { if (AZ::Interface::Get() != nullptr && - connection->GetConnectionRole() == ConnectionRole::Connector) + connection->GetConnectionRole() == ConnectionRole::Acceptor) { AzFramework::PlayerConnectionConfig config; config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); @@ -686,7 +671,7 @@ namespace Multiplayer // Signal to session management when there are no remaining players in a dedicated server for potential cleanup // We avoid this for client server as the host itself is a user - if (m_agentType == MultiplayerAgentType::DedicatedServer && connection->GetConnectionRole() == ConnectionRole::Connector) + if (m_agentType == MultiplayerAgentType::DedicatedServer && connection->GetConnectionRole() == ConnectionRole::Acceptor) { if (AZ::Interface::Get() != nullptr && m_networkInterface->GetConnectionSet().GetConnectionCount() == 0) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 2a3fe73ff4..00313c6b65 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -82,7 +82,6 @@ namespace Multiplayer bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Connect& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Accept& packet); - bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ValidateSession& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::SyncConsole& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ConsoleCommand& packet); @@ -147,6 +146,8 @@ namespace Multiplayer ConnectionAcquiredEvent m_connAcquiredEvent; ClientDisconnectedEvent m_clientDisconnectedEvent; + AZStd::queue m_pendingConnectionTickets; + AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 }; HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId; From cd69b7c9d6c1214d8e4fa4b23dba77971b422eba Mon Sep 17 00:00:00 2001 From: jromnoa Date: Tue, 15 Jun 2021 14:34:44 -0700 Subject: [PATCH 211/233] do 1 print() call for a failed s3_upload_file() call and add a short 100 millisecond sleep between file upload retries --- scripts/build/tools/upload_to_s3.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/build/tools/upload_to_s3.py b/scripts/build/tools/upload_to_s3.py index 80ffcdb8e1..5666e6ec6b 100755 --- a/scripts/build/tools/upload_to_s3.py +++ b/scripts/build/tools/upload_to_s3.py @@ -26,6 +26,7 @@ python upload_to_s3.py --base_dir %WORKSPACE%/path/to/files --file_regex "(.*png import os import re import json +import time import boto3 from optparse import OptionParser @@ -100,13 +101,18 @@ def get_files_to_upload(base_dir, regex, search_subdirectories): def s3_upload_file(client, file, bucket, key_prefix=None, extra_args=None, max_retry=1): - key = file if key_prefix is None else '{}/{}'.format(key_prefix, file) + key = file if key_prefix is None else f'{key_prefix}/{file}' + error_message = None + for x in range(max_retry): try: client.upload_file(file, bucket, key, ExtraArgs=extra_args) return True except Exception as err: - print(('Upload failed: Exception while uploading: {}'.format(err))) + time.sleep(0.1) # Sleep for 100 milliseconds between retries. + error_message = err + + print(f'Upload failed - Exception while uploading: {error_message}') return False From cf5641a211ba15264d68604b420a223586fc00f2 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 15 Jun 2021 14:53:23 -0700 Subject: [PATCH 212/233] Cleanup shutdown, activate and deactivate for MPSystemComponent --- .../Code/Source/MultiplayerSystemComponent.cpp | 16 ++++++++++++---- .../Code/Tests/MultiplayerSystemTests.cpp | 5 ++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 5f44a2cccb..3564c1e91e 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -179,7 +179,10 @@ namespace Multiplayer AZ::TickBus::Handler::BusConnect(); AzFramework::SessionNotificationBus::Handler::BusConnect(); m_networkInterface = AZ::Interface::Get()->CreateNetworkInterface(AZ::Name(MPNetworkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this); - m_consoleCommandHandler.Connect(AZ::Interface::Get()->GetConsoleCommandInvokedEvent()); + if (AZ::Interface::Get()) + { + m_consoleCommandHandler.Connect(AZ::Interface::Get()->GetConsoleCommandInvokedEvent()); + } AZ::Interface::Register(this); AZ::Interface::Register(this); @@ -191,6 +194,8 @@ namespace Multiplayer { AZ::Interface::Unregister(this); AZ::Interface::Unregister(this); + m_consoleCommandHandler.Disconnect(); + AZ::Interface::Get()->DestroyNetworkInterface(AZ::Name(MPNetworkInterfaceName)); AzFramework::SessionNotificationBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); } @@ -673,10 +678,13 @@ namespace Multiplayer // We avoid this for client server as the host itself is a user if (m_agentType == MultiplayerAgentType::DedicatedServer && connection->GetConnectionRole() == ConnectionRole::Acceptor) { - if (AZ::Interface::Get() != nullptr - && m_networkInterface->GetConnectionSet().GetConnectionCount() == 0) + if (m_networkInterface->GetConnectionSet().GetConnectionCount() == 0) { - AZ::Interface::Get()->HandleDestroySession(); + m_shutdownEvent.Signal(m_networkInterface); + if (AZ::Interface::Get() != nullptr) + { + AZ::Interface::Get()->HandleDestroySession(); + } } } } diff --git a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp index 7b09b65de4..9579c84fc1 100644 --- a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp +++ b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp @@ -35,14 +35,16 @@ namespace UnitTest m_initHandler = Multiplayer::SessionInitEvent::Handler([this](AzNetworking::INetworkInterface* value) { TestInitEvent(value); }); m_mpComponent->AddSessionInitHandler(m_initHandler); - m_shutdownHandler = Multiplayer::SessionInitEvent::Handler([this](AzNetworking::INetworkInterface* value) { TestShutdownEvent(value); }); + m_shutdownHandler = Multiplayer::SessionShutdownEvent::Handler([this](AzNetworking::INetworkInterface* value) { TestShutdownEvent(value); }); m_mpComponent->AddSessionShutdownHandler(m_shutdownHandler); m_connAcquiredHandler = Multiplayer::ConnectionAcquiredEvent::Handler([this](Multiplayer::MultiplayerAgentDatum value) { TestConnectionAcquiredEvent(value); }); m_mpComponent->AddConnectionAcquiredHandler(m_connAcquiredHandler); + m_mpComponent->Activate(); } void TearDown() override { + m_mpComponent->Deactivate(); delete m_mpComponent; delete m_netComponent; AZ::NameDictionary::Destroy(); @@ -86,6 +88,7 @@ namespace UnitTest TEST_F(MultiplayerSystemTests, TestShutdownEvent) { + m_mpComponent->InitializeMultiplayer(Multiplayer::MultiplayerAgentType::DedicatedServer); IMultiplayerConnectionMock connMock1 = IMultiplayerConnectionMock(AzNetworking::ConnectionId(), AzNetworking::IpAddress(), AzNetworking::ConnectionRole::Acceptor); IMultiplayerConnectionMock connMock2 = IMultiplayerConnectionMock(AzNetworking::ConnectionId(), AzNetworking::IpAddress(), AzNetworking::ConnectionRole::Connector); m_mpComponent->OnDisconnect(&connMock1, AzNetworking::DisconnectReason::None, AzNetworking::TerminationEndpoint::Local); From 7d1a12a14f667cd619dd0bc5f1df285b6aef9ce3 Mon Sep 17 00:00:00 2001 From: Yuriy Toporovskyy Date: Tue, 15 Jun 2021 18:33:44 -0400 Subject: [PATCH 213/233] Bug fix: crash after full screen preview due to dangling pointer --- Code/Sandbox/Editor/EditorViewportWidget.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index b02aafbe32..9ff38a34c1 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -1381,6 +1381,10 @@ void EditorViewportWidget::SetViewportId(int id) { CViewport::SetViewportId(id); + // Clear the cached debugdisplay pointer. we're about to delete that render viewport, and deleting the render + // viewport invalidates the debugdisplay. + m_debugDisplay = nullptr; + // First delete any existing layout // This also deletes any existing render viewport widget (since it will be added to the layout) // Below is the typical method of clearing a QLayout, see e.g. https://doc.qt.io/qt-5/qlayout.html#takeAt From 2a6009c94b9796abbe0222abc67c36e5886e2745 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 15 Jun 2021 17:46:51 -0700 Subject: [PATCH 214/233] Cleanup OnDisconnect client case slightly --- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 3564c1e91e..c43d136cd8 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -647,9 +647,10 @@ namespace Multiplayer AZStd::string reasonString = ToString(reason); AZLOG_INFO("%s due to %s from remote address: %s", endpointString, reasonString.c_str(), connection->GetRemoteAddress().GetString().c_str()); - if (GetAgentType() == MultiplayerAgentType::Client && connection->GetConnectionRole() == ConnectionRole::Connector) + // The client is disconnecting + if (GetAgentType() == MultiplayerAgentType::Client) { - // The client is disconnecting + AZ_Assert(connection->GetConnectionRole() == ConnectionRole::Connector, "Client connection role should only ever be Connector"); m_clientDisconnectedEvent.Signal(); } From 17ba9f5c4feb423249c4579ae88b5061315bd31d Mon Sep 17 00:00:00 2001 From: John Date: Wed, 16 Jun 2021 10:09:02 +0100 Subject: [PATCH 215/233] Force non-gating at script level --- scripts/build/TestImpactAnalysis/tiaf_driver.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/scripts/build/TestImpactAnalysis/tiaf_driver.py b/scripts/build/TestImpactAnalysis/tiaf_driver.py index f452b9738f..a21f502a2a 100644 --- a/scripts/build/TestImpactAnalysis/tiaf_driver.py +++ b/scripts/build/TestImpactAnalysis/tiaf_driver.py @@ -54,8 +54,13 @@ def parse_args(): return args if __name__ == "__main__": - args = parse_args() - tiaf = TestImpact(args.config, args.pipeline, args.dst_commit) - return_code = tiaf.run(args.suite, args.test_failure_policy, args.safe_mode, args.test_timeout, args.global_timeout) - sys.exit(return_code) - \ No newline at end of file + try: + args = parse_args() + tiaf = TestImpact(args.config, args.pipeline, args.dst_commit) + return_code = tiaf.run(args.suite, args.test_failure_policy, args.safe_mode, args.test_timeout, args.global_timeout) + # Non-gating will be removed from this script and handled at the job level in SPEC-7413 + #sys.exit(return_code) + sys.exit(0) + except: + # Non-gating will be removed from this script and handled at the job level in SPEC-7413 + sys.exit(0) \ No newline at end of file From 0983d1ba45780e6d2aa55cdb734322795a7ea531 Mon Sep 17 00:00:00 2001 From: John Jones-Steele Date: Wed, 16 Jun 2021 11:22:59 +0100 Subject: [PATCH 216/233] Changes made to address comments. --- .../AzQtComponents/AzQtComponents/Components/Style.cpp | 3 +-- .../Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h | 2 -- Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp | 5 ++++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp index 137df2b4bd..47c0e66ff0 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp @@ -508,8 +508,7 @@ namespace AzQtComponents // First draw as standard to get the correct hover background for the complete control. QProxyStyle::drawControl(element, option, painter, widget); // Now draw the icon as non-hovered so control behaves as designed. - const QStyleOptionMenuItem* opt = qstyleoption_cast(option); - QStyleOptionMenuItem myOpt = *(const_cast(opt)); + QStyleOptionMenuItem myOpt = *qstyleoption_cast(option); myOpt.state &= ~QStyle::State_Selected; return QProxyStyle::drawControl(element, &myOpt, painter, widget); } diff --git a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h index 5fd01453e2..ab03223323 100644 --- a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h +++ b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h @@ -56,7 +56,5 @@ namespace AWSCore // To improve experience, use process watcher to keep track of ongoing tool process AZStd::unique_ptr m_resourceMappingToolWatcher; - - const int m_sizeOfIcon = 16; }; } // namespace AWSCore diff --git a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp index 2bf387de27..27e8710cbc 100644 --- a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp +++ b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp @@ -35,6 +35,9 @@ namespace AWSCore { + + static constexpr int IconSize = 16; + AWSCoreEditorMenu::AWSCoreEditorMenu(const QString& text) : QMenu(text) , m_resourceMappingToolWatcher(nullptr) @@ -228,7 +231,7 @@ namespace AWSCore void AWSCoreEditorMenu::AddSpaceForIcon(QMenu *menu) { QSize size = menu->sizeHint(); - size.setWidth(size.width() + m_sizeOfIcon); + size.setWidth(size.width() + IconSize); menu->setFixedSize(size); } } // namespace AWSCore From 0e6a266e89d22b65a4961971eb2554b6f3e6a460 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 16 Jun 2021 11:47:17 +0100 Subject: [PATCH 217/233] Remove frontend console tests --- .../TestImpactCommandLineOptionsTest.cpp | 1436 ----------------- .../Code/Tests/TestImpactConsoleMainTest.cpp | 0 ...actConsoleTestSequenceEventHandlerTest.cpp | 42 - ...estImpactFrontendConsoleStaticTestMain.cpp | 34 - ...tImpactRuntimeConfigurationFactoryTest.cpp | 27 - ..._frontend_console_static_tests_files.cmake | 5 - 6 files changed, 1544 deletions(-) delete mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactCommandLineOptionsTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleMainTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleTestSequenceEventHandlerTest.cpp delete mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactFrontendConsoleStaticTestMain.cpp delete mode 100644 Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactRuntimeConfigurationFactoryTest.cpp diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactCommandLineOptionsTest.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactCommandLineOptionsTest.cpp deleted file mode 100644 index cd02f5c5c7..0000000000 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactCommandLineOptionsTest.cpp +++ /dev/null @@ -1,1436 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include - -#include -#include -#include -#include - -namespace UnitTest -{ - class CommandLineOptionsTestFixture - : public AllocatorsTestFixture - { - public: - void SetUp() override - { - AllocatorsTestFixture::SetUp(); - m_args.push_back("program.exe"); - } - protected: - void InitOptions(); - - AZStd::unique_ptr m_options; - AZStd::vector m_args; - }; - - void CommandLineOptionsTestFixture::InitOptions() - { - m_options = AZStd::make_unique(m_args.size(), const_cast(m_args.data())); - } - - TEST_F(CommandLineOptionsTestFixture, CheckEmptyArgs_ExpectDefaultValues) - { - InitOptions(); - EXPECT_EQ(m_options->GetConfigurationFile(), LY_TEST_IMPACT_DEFAULT_CONFIG_FILE); - EXPECT_EQ(m_options->GetFailedTestCoveragePolicy(), TestImpact::Policy::FailedTestCoverage::Keep); - EXPECT_EQ(m_options->GetExecutionFailurePolicy(), TestImpact::Policy::ExecutionFailure::Continue); - EXPECT_FALSE(m_options->GetGlobalTimeout().has_value()); - EXPECT_FALSE(m_options->GetTestTargetTimeout().has_value()); - EXPECT_FALSE(m_options->GetMaxConcurrency().has_value()); - EXPECT_FALSE(m_options->HasOutputChangeList()); - EXPECT_EQ(m_options->GetTargetOutputCapture(), TestImpact::Policy::TargetOutputCapture::None); - EXPECT_EQ(m_options->GetTestFailurePolicy(), TestImpact::Policy::TestFailure::Abort); - EXPECT_EQ(m_options->GetIntegrityFailurePolicy(), TestImpact::Policy::IntegrityFailure::Abort); - EXPECT_EQ(m_options->GetTestPrioritizationPolicy(), TestImpact::Policy::TestPrioritization::None); - EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::None); - EXPECT_EQ(m_options->GetTestShardingPolicy(), TestImpact::Policy::TestSharding::Never); - EXPECT_FALSE(m_options->HasChangeListFile()); - EXPECT_FALSE(m_options->GetChangeListFile().has_value()); - EXPECT_FALSE(m_options->HasSafeMode()); - EXPECT_EQ(m_options->GetSuiteFilter(), TestImpact::SuiteType::Main); - } - - TEST_F(CommandLineOptionsTestFixture, ConfigurationFileHasEmptyPath_ExpectCommandLineOptionsException) - { - m_args.push_back("-config"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, ConfigurationFileHasSpecifiedPath_ExpectPath) - { - m_args.push_back("-config"); - m_args.push_back("Foo\\Bar"); - InitOptions(); - EXPECT_STREQ(m_options->GetConfigurationFile().c_str(), "Foo\\Bar"); - } - - TEST_F(CommandLineOptionsTestFixture, ConfigurationFileHasMultiplePaths_ExpectCommandLineOptionsException) - { - m_args.push_back("-config"); - m_args.push_back("value1,value2"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, UnifiedDiffFileHasEmptyPath_ExpectCommandLineOptionsException) - { - m_args.push_back("-changelist"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, UnifiedDiffFileHasSpecifiedPath_ExpectPath) - { - m_args.push_back("-changelist"); - m_args.push_back("Foo\\Bar"); - InitOptions(); - EXPECT_TRUE(m_options->HasChangeListFile()); - EXPECT_STREQ(m_options->GetChangeListFile()->c_str(), "Foo\\Bar"); - } - - TEST_F(CommandLineOptionsTestFixture, UnifiedDiffFileHasMultiplePaths_ExpectCommandLineOptionsException) - { - m_args.push_back("-changelist"); - m_args.push_back("value1,value2"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, OutputChangeListHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-ochangelist"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, OutputChangeListHasMultipleValues_ExpectCommandLineOptionsException) - { - m_args.push_back("-ochangelist"); - m_args.push_back("value1,value2,value3"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-sequence"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasNoneOption_ExpectNoneTestSequenceType) - { - m_args.push_back("-sequence"); - m_args.push_back("none"); - InitOptions(); - EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::None); - } - - TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasSeedOption_ExpectSeedTestSequenceType) - { - m_args.push_back("-sequence"); - m_args.push_back("seed"); - InitOptions(); - EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::Seed); - } - - TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasRegularOption_ExpectRegularTestSequenceType) - { - m_args.push_back("-sequence"); - m_args.push_back("regular"); - InitOptions(); - EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::Regular); - } - - TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasImpactAnalysisOption_ExpectImpactAnalysisTestSequenceType) - { - m_args.push_back("-sequence"); - m_args.push_back("tia"); - InitOptions(); - EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::ImpactAnalysis); - } - - TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasImpactAnalysisNoWriteOption_ExpectImpactAnalysisNoWriteTestSequenceType) - { - m_args.push_back("-sequence"); - m_args.push_back("tianowrite"); - InitOptions(); - EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::ImpactAnalysisNoWrite); - } - - TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasSafeImpactAnalysisOption_ExpectSafeImpactAnalysisTestSequenceType) - { - m_args.push_back("-sequence"); - m_args.push_back("tiaorseed"); - InitOptions(); - EXPECT_EQ(m_options->GetTestSequenceType(), TestImpact::TestSequenceType::ImpactAnalysisOrSeed); - } - - TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasInvalidOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-sequence"); - m_args.push_back("foo"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, TestSequenceTypeHasMultipleValues_ExpectCommandLineOptionsException) - { - m_args.push_back("-sequence"); - m_args.push_back("seed,tia"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, TestPrioritizationPolicyHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-ppolicy"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, TestPrioritizationPolicyHasNoneOption_ExpectNoneTestPrioritizationPolicy) - { - m_args.push_back("-ppolicy"); - m_args.push_back("none"); - InitOptions(); - EXPECT_EQ(m_options->GetTestPrioritizationPolicy(), TestImpact::Policy::TestPrioritization::None); - } - - TEST_F(CommandLineOptionsTestFixture, TestPrioritizationPolicyHasDependencyLocalityOption_ExpectDependencyLocalityTestPrioritizationPolicy) - { - m_args.push_back("-ppolicy"); - m_args.push_back("locality"); - InitOptions(); - EXPECT_EQ(m_options->GetTestPrioritizationPolicy(), TestImpact::Policy::TestPrioritization::DependencyLocality); - } - - TEST_F(CommandLineOptionsTestFixture, TestPrioritizationPolicyInvalidOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-ppolicy"); - m_args.push_back("none,locality"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, ExecutionFailurePolicyHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-ppolicy"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, ExecutionFailurePolicyHasAbortOption_ExpectAbortExecutionFailurePolicy) - { - m_args.push_back("-epolicy"); - m_args.push_back("abort"); - InitOptions(); - EXPECT_EQ(m_options->GetExecutionFailurePolicy(), TestImpact::Policy::ExecutionFailure::Abort); - } - - TEST_F(CommandLineOptionsTestFixture, ExecutionFailurePolicyHasContinueOption_ExpectContinueExecutionFailurePolicy) - { - m_args.push_back("-epolicy"); - m_args.push_back("continue"); - InitOptions(); - EXPECT_EQ(m_options->GetExecutionFailurePolicy(), TestImpact::Policy::ExecutionFailure::Continue); - } - - TEST_F(CommandLineOptionsTestFixture, ExecutionFailurePolicyHasIgnoreOption_ExpectIgnoreExecutionFailurePolicy) - { - m_args.push_back("-epolicy"); - m_args.push_back("ignore"); - InitOptions(); - EXPECT_EQ(m_options->GetExecutionFailurePolicy(), TestImpact::Policy::ExecutionFailure::Ignore); - } - - TEST_F(CommandLineOptionsTestFixture, ExecutionFailurePolicyHasInvalidOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-epolicy"); - m_args.push_back("foo"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, ExecutionFailurePolicyHasMultipleValues_ExpectCommandLineOptionsException) - { - m_args.push_back("-epolicy"); - m_args.push_back("abort,ingore"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, ExecutionFailureDraftingPolicyHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-rexecfailures"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, FailedTestCoveragePolicyHasKeepOption_ExpectKeepFailedTestCoveragePolicy) - { - m_args.push_back("-cpolicy"); - m_args.push_back("keep"); - InitOptions(); - EXPECT_EQ(m_options->GetFailedTestCoveragePolicy(), TestImpact::Policy::FailedTestCoverage::Keep); - } - - TEST_F(CommandLineOptionsTestFixture, FailedTestCoveragePolicyHasDiscardOption_ExpectDiscardFailedTestCoveragePolicy) - { - m_args.push_back("-cpolicy"); - m_args.push_back("discard"); - InitOptions(); - EXPECT_EQ(m_options->GetFailedTestCoveragePolicy(), TestImpact::Policy::FailedTestCoverage::Discard); - } - - TEST_F(CommandLineOptionsTestFixture, FailedTestCoveragePolicyInvalidOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-cpolicy"); - m_args.push_back("foo"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, FailedTestCoveragePolicyPolicyHasMultipleValues_ExpectCommandLineOptionsException) - { - m_args.push_back("--cpolicy"); - m_args.push_back("keep,discard"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, TestFailurePolicyHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-fpolicy"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, TestFailurePolicyHasAbortOption_ExpectAbortTestFailurePolicy) - { - m_args.push_back("-fpolicy"); - m_args.push_back("abort"); - InitOptions(); - EXPECT_EQ(m_options->GetTestFailurePolicy(), TestImpact::Policy::TestFailure::Abort); - } - - TEST_F(CommandLineOptionsTestFixture, TestFailurePolicyHasContinueOption_ExpectContinueTestFailurePolicy) - { - m_args.push_back("-fpolicy"); - m_args.push_back("continue"); - InitOptions(); - EXPECT_EQ(m_options->GetTestFailurePolicy(), TestImpact::Policy::TestFailure::Continue); - } - - TEST_F(CommandLineOptionsTestFixture, TestFailurePolicyInvalidOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-fpolicy"); - m_args.push_back("foo"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, TestFailurePolicyHasMultipeValues_ExpectCommandLineOptionsException) - { - m_args.push_back("-fpolicy"); - m_args.push_back("abort,continue"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, IntegrityFailurePolicyHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-ipolicy"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, IntegrityFailurePolicyHasAbortOption_ExpectAbortIntegrityFailurePolicy) - { - m_args.push_back("-ipolicy"); - m_args.push_back("abort"); - InitOptions(); - EXPECT_EQ(m_options->GetIntegrityFailurePolicy(), TestImpact::Policy::IntegrityFailure::Abort); - } - - TEST_F(CommandLineOptionsTestFixture, IntegrityFailurePolicyHasContinueOption_ExpectContinueIntegrityFailurePolicy) - { - m_args.push_back("-ipolicy"); - m_args.push_back("continue"); - InitOptions(); - EXPECT_EQ(m_options->GetIntegrityFailurePolicy(), TestImpact::Policy::IntegrityFailure::Continue); - } - - TEST_F(CommandLineOptionsTestFixture, IntegrityFailurePolicyInvalidOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-ipolicy"); - m_args.push_back("foo"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, IntegrityFailurePolicyHasMultipeValues_ExpectCommandLineOptionsException) - { - m_args.push_back("-ipolicy"); - m_args.push_back("abort,continue"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, TestShardingHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-shard"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, TestShardingHasOnOption_ExpectOnTestSharding) - { - m_args.push_back("-shard"); - m_args.push_back("on"); - InitOptions(); - EXPECT_EQ(m_options->GetTestShardingPolicy(), TestImpact::Policy::TestSharding::Always); - } - - TEST_F(CommandLineOptionsTestFixture, TestShardingHasOffOption_ExpectOffTestSharding) - { - m_args.push_back("-shard"); - m_args.push_back("off"); - InitOptions(); - EXPECT_EQ(m_options->GetTestShardingPolicy(), TestImpact::Policy::TestSharding::Never); - } - - TEST_F(CommandLineOptionsTestFixture, TestShardingInvalidOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-shard"); - m_args.push_back("foo"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, TestShardingHasMultipeValues_ExpectCommandLineOptionsException) - { - m_args.push_back("-shard"); - m_args.push_back("on,off"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, TargetOutputCaptureHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-targetout"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, TargetOutputCaptureHasStdOutOption_ExpectStdOutTargetOutputCapture) - { - m_args.push_back("-targetout"); - m_args.push_back("stdout"); - InitOptions(); - EXPECT_EQ(m_options->GetTargetOutputCapture(), TestImpact::Policy::TargetOutputCapture::StdOut); - } - - TEST_F(CommandLineOptionsTestFixture, TargetOutputCaptureHasFileOption_ExpectFileTargetOutputCapture) - { - m_args.push_back("-targetout"); - m_args.push_back("file"); - InitOptions(); - EXPECT_EQ(m_options->GetTargetOutputCapture(), TestImpact::Policy::TargetOutputCapture::File); - } - - TEST_F(CommandLineOptionsTestFixture, TargetOutputCaptureHasStdOutAndFileOption_ExpectStdOutAndFileTargetOutputCapture) - { - m_args.push_back("-targetout"); - m_args.push_back("stdout,file"); - InitOptions(); - EXPECT_EQ(m_options->GetTargetOutputCapture(), TestImpact::Policy::TargetOutputCapture::StdOutAndFile); - } - - TEST_F(CommandLineOptionsTestFixture, TargetOutputCaptureInvalidOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-targetout"); - m_args.push_back("foo"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, TargetOutputCaptureHasExcessValues_ExpectCommandLineOptionsException) - { - m_args.push_back("-targetout"); - m_args.push_back("stdout,file,stdout"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, MaxConcurrencyHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-maxconcurrency"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, MaxConcurrencyHasInRangeOptions_ExpectInRangeMaxConcurrency) - { - m_args.push_back("-maxconcurrency"); - m_args.push_back("10"); - InitOptions(); - EXPECT_EQ(m_options->GetMaxConcurrency(), 10); - } - - TEST_F(CommandLineOptionsTestFixture, MaxConcurrencyHasOutOfRangeOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-maxconcurrency"); - m_args.push_back("-1"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, MaxConcurrencyInvalidOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-maxconcurrency"); - m_args.push_back("foo"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, MaxConcurrencyHasMultipeValues_ExpectCommandLineOptionsException) - { - m_args.push_back("-maxconcurrency"); - m_args.push_back("10,20"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, TestTargetTimeoutHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-ttimeout"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, TestTargetTimeoutHasInRangeOptions_ExpectInRangeTestTargetTimeout) - { - m_args.push_back("-ttimeout"); - m_args.push_back("10"); - InitOptions(); - EXPECT_EQ(m_options->GetTestTargetTimeout(), AZStd::chrono::seconds(10)); - } - - TEST_F(CommandLineOptionsTestFixture, TestTargetTimeoutHasOutOfRangeOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-ttimeout"); - m_args.push_back("-1"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, TestTargetTimeoutInvalidOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-ttimeout"); - m_args.push_back("foo"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, TestTargetTimeoutHasMultipeValues_ExpectCommandLineOptionsException) - { - m_args.push_back("-ttimeout"); - m_args.push_back("10,20"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, GlobalTimeoutHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-gtimeout"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, GlobalTimeoutHasInRangeOptions_ExpectInRangeGlobalTimeout) - { - m_args.push_back("-gtimeout"); - m_args.push_back("10"); - InitOptions(); - EXPECT_EQ(m_options->GetGlobalTimeout(), AZStd::chrono::seconds(10)); - } - - TEST_F(CommandLineOptionsTestFixture, GlobalTimeoutHasOutOfRangeOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-gtimeout"); - m_args.push_back("-1"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, GlobalTimeoutInvalidOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-gtimeout"); - m_args.push_back("foo"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, GlobalTimeoutHasMultipeValues_ExpectCommandLineOptionsException) - { - m_args.push_back("-gtimeout"); - m_args.push_back("10,20"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - // - - TEST_F(CommandLineOptionsTestFixture, SafeModeHasEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-safemode"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, SafeModeHasOnOption_ExpectOnSafeMode) - { - m_args.push_back("-safemode"); - m_args.push_back("on"); - InitOptions(); - EXPECT_TRUE(m_options->HasSafeMode()); - } - - TEST_F(CommandLineOptionsTestFixture, SafeModeHasOffOption_ExpectOffSafeMode) - { - m_args.push_back("-safemode"); - m_args.push_back("off"); - InitOptions(); - EXPECT_FALSE(m_options->HasSafeMode()); - } - - TEST_F(CommandLineOptionsTestFixture, SafeModeInvalidOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-safemode"); - m_args.push_back("foo"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, SafeModeHasMultipeValues_ExpectCommandLineOptionsException) - { - m_args.push_back("-safemode"); - m_args.push_back("on,off"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, SuiteFilterEmptyOption_ExpectCommandLineOptionsException) - { - m_args.push_back("-suite"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } - catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } - catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, SuiteFilterMultipleOptions_ExpectCommandLineOptionsException) - { - m_args.push_back("-suite"); - m_args.push_back("periodic,smoke"); - - try - { - InitOptions(); - - // Do not expect the command line options construction to succeed - FAIL(); - } catch ([[maybe_unused]] const TestImpact::CommandLineOptionsException& e) - { - // Expect a command line options to be thrown - SUCCEED(); - } catch (...) - { - // Do not expect any other exceptions - FAIL(); - } - } - - TEST_F(CommandLineOptionsTestFixture, SuitesFilterMainOption_ExpectMainSuiteFilter) - { - m_args.push_back("-suites"); - m_args.push_back("main"); - InitOptions(); - EXPECT_EQ(m_options->GetSuiteFilter(), TestImpact::SuiteType::Main); - } - - TEST_F(CommandLineOptionsTestFixture, SuitesFilterPeriodicOption_ExpectPeriodicSuiteFilter) - { - m_args.push_back("-suites"); - m_args.push_back("periodic"); - InitOptions(); - EXPECT_EQ(m_options->GetSuiteFilter(), TestImpact::SuiteType::Periodic); - } - - TEST_F(CommandLineOptionsTestFixture, SuitesFilterSandboxOption_ExpectSandboxSuiteFilter) - { - m_args.push_back("-suites"); - m_args.push_back("sandbox"); - InitOptions(); - EXPECT_EQ(m_options->GetSuiteFilter(), TestImpact::SuiteType::Sandbox); - } -} diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleMainTest.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleMainTest.cpp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleTestSequenceEventHandlerTest.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleTestSequenceEventHandlerTest.cpp deleted file mode 100644 index 52422daa31..0000000000 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactConsoleTestSequenceEventHandlerTest.cpp +++ /dev/null @@ -1,42 +0,0 @@ -///* -// * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -// * its licensors. -// * -// * For complete copyright and license terms please see the LICENSE at the root of this -// * distribution (the "License"). All use of this software is governed by the License, -// * or, if provided, by the license below or the license accompanying this file. Do not -// * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * -// */ -// -//#include -//#include -// -//#include -// -//#include -//#include -//#include -//#include -// -//namespace UnitTest -//{ -// class ConsoleTestSequenceTestFixture -// : public AllocatorsTestFixture -// { -// }; -// -// TEST_F(ConsoleTestSequenceTestFixture, CheckEmptyArgs_ExpectDefaultValues) -// { -// AZStd::unordered_set suites = { "PERIODIC","MAIN" }; -// TestImpact::Console::TestSequenceEventHandler seq(&suites); -// AZStd::vector selectedTests = { "Test1", "Test2", "Test3", "Test4", "Test5" }; -// seq.operator()(TestImpact::Client::TestRunSelection(selectedTests, {"Test6"})); -// -// for (auto i = 0; i < selectedTests.size(); i++) -// { -// seq.operator()(TestImpact::Client::TestRun(selectedTests[i], (TestImpact::Client::TestRunResult)i, AZStd::chrono::milliseconds(i))); -// } -// } -//} diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactFrontendConsoleStaticTestMain.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactFrontendConsoleStaticTestMain.cpp deleted file mode 100644 index 9e128f90af..0000000000 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactFrontendConsoleStaticTestMain.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include -#include - -class TestImpactTestEnvironment - : public AZ::Test::ITestEnvironment -{ -protected: - void SetupEnvironment() override - { - AZ::AllocatorInstance::Create(); - AZ::AllocatorInstance::Create(); - } - - void TeardownEnvironment() override - { - AZ::AllocatorInstance::Destroy(); - AZ::AllocatorInstance::Destroy(); - } -}; - -AZ_UNIT_TEST_HOOK(new TestImpactTestEnvironment); diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactRuntimeConfigurationFactoryTest.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactRuntimeConfigurationFactoryTest.cpp deleted file mode 100644 index 4b1b3f162e..0000000000 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Tests/TestImpactRuntimeConfigurationFactoryTest.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include - -#include - -namespace UnitTest -{ - //TEST(FOO, BAR) - //{ - // std::ifstream configFile("C:\\o3de_TIF_Feature\\windows_vs2019\\bin\\debug\\tiaf.debug.json"); - // AZStd::string configData((std::istreambuf_iterator(configFile)), std::istreambuf_iterator()); - // const auto configuration = TestImpact::ConfigurationFactory(configData); - //} -} diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_static_tests_files.cmake b/Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_static_tests_files.cmake index 4d8e65b2cc..5714be5dfb 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_static_tests_files.cmake +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Code/testimpactframework_frontend_console_static_tests_files.cmake @@ -10,9 +10,4 @@ # set(FILES - Tests/TestImpactCommandLineOptionsTest.cpp - Tests/TestImpactRuntimeConfigurationFactoryTest.cpp - Tests/TestImpactFrontendConsoleStaticTestMain.cpp - Tests/TestImpactConsoleMainTest.cpp - Tests/TestImpactConsoleTestSequenceEventHandlerTest.cpp ) From d9b1ccd3235f5c59233e35d5c16363763a9019b2 Mon Sep 17 00:00:00 2001 From: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> Date: Wed, 16 Jun 2021 12:58:01 +0100 Subject: [PATCH 218/233] Add align grid button to Viewport UI (#1311) * first pass of adding grid snapping button * update to request current grid size * show/hide snapping option based on selection * small tidy-up changes * small updates following review feedback * added some unit tests for snapping functionality and some small tidy-up/refactoring * small refactor to ensure snap to grid ui only appears with snapping enabled * add missing include to resolve build error * fixes for build * add & to make compiler happy --- Code/Framework/AzCore/AzCore/std/math.h | 1 + .../AzManipulatorTestFrameworkTestHelpers.h | 1 + .../Manipulators/ManipulatorSnapping.cpp | 25 +- .../Manipulators/ManipulatorSnapping.h | 17 +- .../EditorTransformComponentSelection.cpp | 116 +++-- .../EditorTransformComponentSelection.h | 36 +- ...torTransformComponentSelectionRequestBus.h | 3 + .../ViewportUi/ViewportUiDisplay.cpp | 2 +- ...EditorTransformComponentSelectionTests.cpp | 413 +++++++++--------- 9 files changed, 361 insertions(+), 253 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/std/math.h b/Code/Framework/AzCore/AzCore/std/math.h index f5e2ac7ea7..fe2469eb41 100644 --- a/Code/Framework/AzCore/AzCore/std/math.h +++ b/Code/Framework/AzCore/AzCore/std/math.h @@ -26,6 +26,7 @@ namespace AZStd using std::exp2; using std::floor; using std::fmod; + using std::pow; using std::round; using std::sin; using std::sqrt; diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h index b61956bdf9..d0107bb317 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h @@ -13,6 +13,7 @@ #pragma once #include +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp index 427bb9e9d1..27d8f7e5a3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -95,16 +96,34 @@ namespace AzToolsFramework return axis * snapAdjustment.m_nextSnapDistance; } + AZ::Vector3 CalculateSnappedOffset( + const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, const size_t snapAxesCount, const float size) + { + return AZStd::accumulate( + snapAxes, snapAxes + snapAxesCount, AZ::Vector3::CreateZero(), + [&unsnappedPosition, size](AZ::Vector3 acc, const AZ::Vector3& snapAxis) + { + acc += CalculateSnappedOffset(unsnappedPosition, snapAxis, size); + return acc; + }); + } + + AZ::Vector3 CalculateSnappedPosition( + const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, const size_t snapAxesCount, const float size) + { + return unsnappedPosition + CalculateSnappedOffset(unsnappedPosition, snapAxes, snapAxesCount, size); + } + AZ::Vector3 CalculateSnappedTerrainPosition( - const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, const int viewportId, const float gridSize) + const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, const int viewportId, const float size) { const AZ::Transform localFromWorld = worldFromLocal.GetInverse(); const AZ::Vector3 localSurfacePosition = localFromWorld.TransformPoint(worldSurfacePosition); // snap in xy plane AZ::Vector3 localSnappedSurfacePosition = localSurfacePosition + - CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisX(), gridSize) + - CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisY(), gridSize); + CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisX(), size) + + CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisY(), size); // find terrain height at xy snapped location float terrainHeight = 0.0f; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h index f2fa104d4c..9024625fae 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h @@ -12,6 +12,7 @@ #pragma once +#include #include #include @@ -58,10 +59,18 @@ namespace AzToolsFramework //! @note A movement of more than half size (in either direction) will cause a snap by size. AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size); + //! Overload of CalculateSnappedOffset taking multiple axes. + AZ::Vector3 CalculateSnappedOffset( + const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, size_t snapAxesCount, float size); + + //! Return the final snapped position according to size (unsnappedPosition + CalculateSnappedOffset). + AZ::Vector3 CalculateSnappedPosition( + const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, size_t snapAxesCount, float size); + //! For a given point on the terrain, calculate the closest xy position snapped to the grid //! (z position is aligned to terrain height, not snapped to z grid) AZ::Vector3 CalculateSnappedTerrainPosition( - const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, int viewportId, float gridSize); + const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, int viewportId, float size); //! Wrapper for grid snapping and grid size bus calls. GridSnapParameters GridSnapSettings(int viewportId); @@ -84,8 +93,8 @@ namespace AzToolsFramework //! @param exponent Precision to use when rounding. inline float Round(const float value, const float exponent) { - const float precision = std::pow(10.0f, exponent); - return roundf(value * precision) / precision; + const float precision = AZStd::pow(10.0f, exponent); + return AZStd::round(value * precision) / precision; } //! Round to 3 significant digits (3 digits common usage). @@ -116,7 +125,7 @@ namespace AzToolsFramework //! when dealing with values far from the origin. inline AZ::Vector3 NonUniformScaleReciprocal(const AZ::Vector3& nonUniformScale) { - AZ::Vector3 scaleReciprocal = nonUniformScale.GetReciprocal(); + const AZ::Vector3 scaleReciprocal = nonUniformScale.GetReciprocal(); return AZ::Vector3(Round3(scaleReciprocal.GetX()), Round3(scaleReciprocal.GetY()), Round3(scaleReciprocal.GetZ())); } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index eb16bf158e..c4f1ae33da 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -127,6 +127,7 @@ namespace AzToolsFramework static const char* const s_dittoTranslationIndividualUndoRedoDesc = "Ditto translation individual"; static const char* const s_dittoScaleIndividualWorldUndoRedoDesc = "Ditto scale individual world"; static const char* const s_dittoScaleIndividualLocalUndoRedoDesc = "Ditto scale individual local"; + static const char* const s_snapToWorldGridUndoRedoDesc = "Snap to world grid"; static const char* const s_showAllEntitiesUndoRedoDesc = s_showAllTitle; static const char* const s_lockSelectionUndoRedoDesc = s_lockSelectionTitle; static const char* const s_hideSelectionUndoRedoDesc = s_hideSelectionTitle; @@ -142,6 +143,7 @@ namespace AzToolsFramework static const char* const SpaceClusterWorldTooltip = "Toggle world space lock"; static const char* const SpaceClusterParentTooltip = "Toggle parent space lock"; static const char* const SpaceClusterLocalTooltip = "Toggle local space lock"; + static const char* const SnappingClusterSnapToWorldTooltip = "Snap selected entities to the world space grid"; static const AZ::Color s_fadedXAxisColor = AZ::Color(AZ::u8(200), AZ::u8(127), AZ::u8(127), AZ::u8(255)); static const AZ::Color s_fadedYAxisColor = AZ::Color(AZ::u8(127), AZ::u8(190), AZ::u8(127), AZ::u8(255)); @@ -150,8 +152,6 @@ namespace AzToolsFramework static const AZ::Color s_pickedOrientationColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f); static const AZ::Color s_selectedEntityAabbColor = AZ::Color(0.6f, 0.6f, 0.6f, 0.4f); - static const int s_defaultViewportId = 0; - static const float s_pivotSize = 0.075f; // the size of the pivot (box) to render when selected // data passed to manipulators when processing mouse interactions @@ -503,7 +503,8 @@ namespace AzToolsFramework void EditorTransformComponentSelection::SetAllViewportUiVisible(const bool visible) { SetViewportUiClusterVisible(m_transformModeClusterId, visible); - SetViewportUiClusterVisible(m_spaceCluster.m_spaceClusterId, visible); + SetViewportUiClusterVisible(m_spaceCluster.m_clusterId, visible); + SetViewportUiClusterVisible(m_snappingCluster.m_clusterId, visible); m_viewportUiVisible = visible; } @@ -524,8 +525,8 @@ namespace AzToolsFramework }; ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, - m_spaceCluster.m_spaceClusterId, buttonIdFromFrameFn(referenceFrame)); + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, m_spaceCluster.m_clusterId, + buttonIdFromFrameFn(referenceFrame)); } namespace ETCS @@ -1037,6 +1038,8 @@ namespace AzToolsFramework CreateTransformModeSelectionCluster(); CreateSpaceSelectionCluster(); + CreateSnappingCluster(); + RegisterActions(); SetupBoxSelect(); RefreshSelectedEntityIdsAndRegenerateManipulators(); @@ -1048,7 +1051,8 @@ namespace AzToolsFramework DestroyManipulators(m_entityIdManipulators); DestroyCluster(m_transformModeClusterId); - DestroyCluster(m_spaceCluster.m_spaceClusterId); + DestroyCluster(m_spaceCluster.m_clusterId); + DestroyCluster(m_snappingCluster.m_clusterId); UnregisterActions(); @@ -2513,28 +2517,64 @@ namespace AzToolsFramework m_transformModeSelectionHandler); } + void EditorTransformComponentSelection::CreateSnappingCluster() + { + // create the cluster for switching spaces/reference frames + ViewportUi::ViewportUiRequestBus::EventResult( + m_snappingCluster.m_clusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster, + ViewportUi::Alignment::TopRight); + + m_snappingCluster.m_snapToWorldButtonId = RegisterClusterButton(m_snappingCluster.m_clusterId, "Grid"); + + // set button tooltips + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, + m_snappingCluster.m_clusterId, m_snappingCluster.m_snapToWorldButtonId, SnappingClusterSnapToWorldTooltip); + + const auto onButtonClicked = [this](const ViewportUi::ButtonId buttonId) + { + if (buttonId == m_snappingCluster.m_snapToWorldButtonId) + { + float gridSize = 1.0f; + ViewportInteraction::ViewportInteractionRequestBus::EventResult( + gridSize, ViewportUi::DefaultViewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::GridSize); + + SnapSelectedEntitiesToWorldGrid(gridSize); + } + }; + + m_snappingCluster.m_snappingHandler = AZ::Event::Handler(onButtonClicked); + + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler, + m_snappingCluster.m_clusterId, m_snappingCluster.m_snappingHandler); + + // hide initially + SetViewportUiClusterVisible(m_snappingCluster.m_clusterId, false); + } + void EditorTransformComponentSelection::CreateSpaceSelectionCluster() { // create the cluster for switching spaces/reference frames ViewportUi::ViewportUiRequestBus::EventResult( - m_spaceCluster.m_spaceClusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster, + m_spaceCluster.m_clusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster, ViewportUi::Alignment::TopRight); // create and register the buttons (strings correspond to icons even if the values appear different) - m_spaceCluster.m_worldButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "World"); - m_spaceCluster.m_parentButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Parent"); - m_spaceCluster.m_localButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Local"); + m_spaceCluster.m_worldButtonId = RegisterClusterButton(m_spaceCluster.m_clusterId, "World"); + m_spaceCluster.m_parentButtonId = RegisterClusterButton(m_spaceCluster.m_clusterId, "Parent"); + m_spaceCluster.m_localButtonId = RegisterClusterButton(m_spaceCluster.m_clusterId, "Local"); // set button tooltips ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, - m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_worldButtonId, SpaceClusterWorldTooltip); + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_spaceCluster.m_clusterId, + m_spaceCluster.m_worldButtonId, SpaceClusterWorldTooltip); ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, - m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_parentButtonId, SpaceClusterParentTooltip); + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_spaceCluster.m_clusterId, + m_spaceCluster.m_parentButtonId, SpaceClusterParentTooltip); ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, - m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_localButtonId, SpaceClusterLocalTooltip); + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_spaceCluster.m_clusterId, + m_spaceCluster.m_localButtonId, SpaceClusterLocalTooltip); auto onButtonClicked = [this](ViewportUi::ButtonId buttonId) { @@ -2576,14 +2616,31 @@ namespace AzToolsFramework } ViewportUi::ViewportUiRequestBus::Event( ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonLocked, - m_spaceCluster.m_spaceClusterId, buttonId, m_spaceCluster.m_spaceLock.has_value()); + m_spaceCluster.m_clusterId, buttonId, m_spaceCluster.m_spaceLock.has_value()); }; - m_spaceCluster.m_spaceSelectionHandler = AZ::Event::Handler(onButtonClicked); + m_spaceCluster.m_spaceHandler = AZ::Event::Handler(onButtonClicked); ViewportUi::ViewportUiRequestBus::Event( ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler, - m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_spaceSelectionHandler); + m_spaceCluster.m_clusterId, m_spaceCluster.m_spaceHandler); + } + + void EditorTransformComponentSelection::SnapSelectedEntitiesToWorldGrid(const float gridSize) + { + AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); + + const AZStd::array snapAxes = { AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ() }; + + ScopedUndoBatch undoBatch(s_snapToWorldGridUndoRedoDesc); + for (const AZ::EntityId& entityId : m_selectedEntityIds) + { + ScopedUndoBatch::MarkEntityDirty(entityId); + SetEntityWorldTranslation( + entityId, CalculateSnappedPosition(GetWorldTranslation(entityId), snapAxes.data(), snapAxes.size(), gridSize)); + } + + RefreshManipulators(RefreshType::Translation); } EditorTransformComponentSelectionRequests::Mode EditorTransformComponentSelection::GetTransformMode() @@ -3145,15 +3202,16 @@ namespace AzToolsFramework return "Transform Component"; } - void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu(QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags) + void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu( + QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags) { - QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick)); - QObject::connect( - action, &QAction::triggered, action, - [this]() - { - ToggleCenterPivotSelection(); - }); + QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick)); + QObject::connect( + action, &QAction::triggered, action, + [this]() + { + ToggleCenterPivotSelection(); + }); } void EditorTransformComponentSelection::BeforeEntitySelectionChanged() @@ -3175,7 +3233,7 @@ namespace AzToolsFramework } void EditorTransformComponentSelection::AfterEntitySelectionChanged( - const EntityIdList& /*newlySelectedEntities*/, const EntityIdList& /*newlyDeselectedEntities*/) + [[maybe_unused]] const EntityIdList& newlySelectedEntities, [[maybe_unused]] const EntityIdList& newlyDeselectedEntities) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -3195,6 +3253,8 @@ namespace AzToolsFramework m_didSetSelectedEntities = false; } + SetViewportUiClusterVisible(m_snappingCluster.m_clusterId, m_viewportUiVisible && !m_selectedEntityIds.empty()); + RegenerateManipulators(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h index 773921ebea..db96d91911 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h @@ -115,12 +115,25 @@ namespace AzToolsFramework SpaceCluster(const SpaceCluster&) = delete; SpaceCluster& operator=(const SpaceCluster&) = delete; - ViewportUi::ClusterId m_spaceClusterId; //!< The id identifying the reference space cluster. + ViewportUi::ClusterId m_clusterId; //!< The id identifying the reference space cluster. ViewportUi::ButtonId m_localButtonId; //!< Local reference space button id. ViewportUi::ButtonId m_parentButtonId; //!< Parent reference space button id. ViewportUi::ButtonId m_worldButtonId; //!< World reference space button id. - AZ::Event::Handler m_spaceSelectionHandler; //!< Callback for when a space cluster button is pressed. AZStd::optional m_spaceLock; //!< Locked reference frame to use if set. + AZ::Event::Handler m_spaceHandler; //!< Callback for when a space cluster button is pressed. + }; + + //! Grouping of viewport ui related state for aligning transforms to a grid. + struct SnappingCluster + { + SnappingCluster() = default; + // disable copying and moving (implicit) + SnappingCluster(const SnappingCluster&) = delete; + SnappingCluster& operator=(const SnappingCluster&) = delete; + + ViewportUi::ClusterId m_clusterId; //!< The cluster id for all snapping buttons. + ViewportUi::ButtonId m_snapToWorldButtonId; //!< The button id for snapping all axes to the world. + AZ::Event::Handler m_snappingHandler; //!< Callback for when a snapping cluster button is pressed. }; //! Entity selection/interaction handling. @@ -180,6 +193,7 @@ namespace AzToolsFramework void CreateTransformModeSelectionCluster(); void CreateSpaceSelectionCluster(); + void CreateSnappingCluster(); void ClearManipulatorTranslationOverride(); void ClearManipulatorOrientationOverride(); @@ -228,14 +242,15 @@ namespace AzToolsFramework AZStd::optional GetManipulatorTransform() override; void OverrideManipulatorOrientation(const AZ::Quaternion& orientation) override; void OverrideManipulatorTranslation(const AZ::Vector3& translation) override; - void CopyTranslationToSelectedEntitiesIndividual(const AZ::Vector3& translation); - void CopyTranslationToSelectedEntitiesGroup(const AZ::Vector3& translation); - void ResetTranslationForSelectedEntitiesLocal(); - void CopyOrientationToSelectedEntitiesIndividual(const AZ::Quaternion& orientation); - void CopyOrientationToSelectedEntitiesGroup(const AZ::Quaternion& orientation); - void ResetOrientationForSelectedEntitiesLocal(); - void CopyScaleToSelectedEntitiesIndividualLocal(float scale); - void CopyScaleToSelectedEntitiesIndividualWorld(float scale); + void CopyTranslationToSelectedEntitiesIndividual(const AZ::Vector3& translation) override; + void CopyTranslationToSelectedEntitiesGroup(const AZ::Vector3& translation) override; + void ResetTranslationForSelectedEntitiesLocal() override; + void CopyOrientationToSelectedEntitiesIndividual(const AZ::Quaternion& orientation) override; + void CopyOrientationToSelectedEntitiesGroup(const AZ::Quaternion& orientation) override; + void ResetOrientationForSelectedEntitiesLocal() override; + void CopyScaleToSelectedEntitiesIndividualLocal(float scale) override; + void CopyScaleToSelectedEntitiesIndividualWorld(float scale) override; + void SnapSelectedEntitiesToWorldGrid(float gridSize) override; // EditorManipulatorCommandUndoRedoRequestBus ... void UndoRedoEntityManipulatorCommand( @@ -320,6 +335,7 @@ namespace AzToolsFramework AzFramework::ClickDetector m_clickDetector; //!< Detect different types of mouse click. AzFramework::CursorState m_cursorState; //!< Track the mouse position and delta movement each frame. SpaceCluster m_spaceCluster; //!< Related viewport ui state for controlling the current reference space. + SnappingCluster m_snappingCluster; //!< Related viewport ui state for aligning positions to a grid or reference frame. bool m_viewportUiVisible = true; //!< Used to hide/show the viewport ui elements. }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h index 966f9333fc..c7b75da206 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h @@ -104,6 +104,9 @@ namespace AzToolsFramework //! Copy scale to to each individual entity in world (absolute) space. virtual void CopyScaleToSelectedEntitiesIndividualWorld(float scale) = 0; + //! Snap selected entities to be aligned with the world space grid. + virtual void SnapSelectedEntitiesToWorldGrid(float gridSize) = 0; + protected: ~EditorTransformComponentSelectionRequests() = default; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index 7b2d45e652..bb2aeed4d6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -281,7 +281,7 @@ namespace AzToolsFramework::ViewportUi::Internal void ViewportUiDisplay::HideViewportUiElement(ViewportUiElementId elementId) { if (ViewportUiElementInfo element = GetViewportUiElementInfo(elementId); - element.m_widget && UiDisplayEnabled()) + element.m_widget) { element.m_widget->setVisible(false); } diff --git a/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp b/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp index cbc81ba9b8..9a34efa6f5 100644 --- a/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp @@ -1,42 +1,42 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include #include #include -#include #include +#include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include #include -#include #include #include +#include #include #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include using namespace AzToolsFramework; @@ -46,12 +46,11 @@ namespace AZ { return os << entityId.ToString().c_str(); } -} +} // namespace AZ namespace UnitTest { - class EditorEntityVisibilityCacheFixture - : public ToolsApplicationFixture + class EditorEntityVisibilityCacheFixture : public ToolsApplicationFixture { public: void CreateLayerAndEntityHierarchy() @@ -116,8 +115,7 @@ namespace UnitTest } // Fixture to support testing EditorTransformComponentSelection functionality on an Entity selection. - class EditorTransformComponentSelectionFixture - : public ToolsApplicationFixture + class EditorTransformComponentSelectionFixture : public ToolsApplicationFixture { public: void SetUpEditorFixtureImpl() override @@ -138,13 +136,11 @@ namespace UnitTest EntityIdList m_entityIds; }; - void EditorTransformComponentSelectionFixture::ArrangeIndividualRotatedEntitySelection( - const AZ::Quaternion& orientation) + void EditorTransformComponentSelectionFixture::ArrangeIndividualRotatedEntitySelection(const AZ::Quaternion& orientation) { for (auto entityId : m_entityIds) { - AZ::TransformBus::Event( - entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, orientation); + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, orientation); } } @@ -152,40 +148,32 @@ namespace UnitTest { AZStd::optional manipulatorTransform; EditorTransformComponentSelectionRequestBus::EventResult( - manipulatorTransform, GetEntityContextId(), - &EditorTransformComponentSelectionRequests::GetManipulatorTransform); + manipulatorTransform, GetEntityContextId(), &EditorTransformComponentSelectionRequests::GetManipulatorTransform); return manipulatorTransform; } - void EditorTransformComponentSelectionFixture::RefreshManipulators( - EditorTransformComponentSelectionRequests::RefreshType refreshType) + void EditorTransformComponentSelectionFixture::RefreshManipulators(EditorTransformComponentSelectionRequests::RefreshType refreshType) { EditorTransformComponentSelectionRequestBus::Event( GetEntityContextId(), &EditorTransformComponentSelectionRequests::RefreshManipulators, refreshType); } - void EditorTransformComponentSelectionFixture::SetTransformMode( - EditorTransformComponentSelectionRequests::Mode transformMode) + void EditorTransformComponentSelectionFixture::SetTransformMode(EditorTransformComponentSelectionRequests::Mode transformMode) { EditorTransformComponentSelectionRequestBus::Event( - GetEntityContextId(), &EditorTransformComponentSelectionRequests::SetTransformMode, - transformMode); + GetEntityContextId(), &EditorTransformComponentSelectionRequests::SetTransformMode, transformMode); } - void EditorTransformComponentSelectionFixture::OverrideManipulatorOrientation( - const AZ::Quaternion& orientation) + void EditorTransformComponentSelectionFixture::OverrideManipulatorOrientation(const AZ::Quaternion& orientation) { EditorTransformComponentSelectionRequestBus::Event( - GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorOrientation, - orientation); + GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorOrientation, orientation); } - void EditorTransformComponentSelectionFixture::OverrideManipulatorTranslation( - const AZ::Vector3& translation) + void EditorTransformComponentSelectionFixture::OverrideManipulatorTranslation(const AZ::Vector3& translation) { EditorTransformComponentSelectionRequestBus::Event( - GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorTranslation, - translation); + GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorTranslation, translation); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -202,8 +190,7 @@ namespace UnitTest SetTransformMode(EditorTransformComponentSelectionRequests::Mode::Rotation); - const AZ::Transform manipulatorTransformBefore = - GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); + const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); // check preconditions - manipulator transform matches parent/world transform (identity) EXPECT_THAT(manipulatorTransformBefore.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY())); @@ -218,8 +205,7 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Then - const AZ::Transform manipulatorTransformAfter = - GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); + const AZ::Transform manipulatorTransformAfter = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); // check postconditions - manipulator transform matches parent/world transform (identity) EXPECT_THAT(manipulatorTransformAfter.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY())); @@ -229,8 +215,7 @@ namespace UnitTest { // create invalid starting orientation to guarantee correct data is coming from GetLocalRotationQuaternion AZ::Quaternion entityOrientation = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), 90.0f); - AZ::TransformBus::EventResult( - entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion); + AZ::TransformBus::EventResult(entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion); // manipulator orientation matches entity orientation EXPECT_THAT(entityOrientation, IsClose(manipulatorTransformAfter.GetRotation())); @@ -252,8 +237,7 @@ namespace UnitTest SetTransformMode(EditorTransformComponentSelectionRequests::Mode::Rotation); - const AZ::Transform manipulatorTransformBefore = - GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); + const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); // check preconditions - manipulator transform matches manipulator orientation override (not entity transform) EXPECT_THAT(manipulatorTransformBefore.GetBasisX(), IsClose(AZ::Vector3::CreateAxisY())); @@ -268,8 +252,7 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Then - const AZ::Transform manipulatorTransformAfter = - GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); + const AZ::Transform manipulatorTransformAfter = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); // check postconditions - manipulator transform matches parent/world space (manipulator override was cleared) EXPECT_THAT(manipulatorTransformAfter.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY())); @@ -278,8 +261,7 @@ namespace UnitTest for (auto entityId : m_entityIds) { AZ::Quaternion entityOrientation; - AZ::TransformBus::EventResult( - entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion); + AZ::TransformBus::EventResult(entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion); // entity transform matches initial (entity transform was not reset, only manipulator was) EXPECT_THAT(entityOrientation, IsClose(initialEntityOrientation)); @@ -301,16 +283,13 @@ namespace UnitTest AZ::EntityId parentId = CreateDefaultEditorEntity("Parent", &parent); AZ::EntityId childId = CreateDefaultEditorEntity("Child", &child); - AZ::TransformBus::Event( - childId, &AZ::TransformInterface::SetParent, parentId); - AZ::TransformBus::Event( - parentId, &AZ::TransformInterface::SetParent, grandParentId); + AZ::TransformBus::Event(childId, &AZ::TransformInterface::SetParent, parentId); + AZ::TransformBus::Event(parentId, &AZ::TransformInterface::SetParent, grandParentId); UnitTest::SliceAssets sliceAssets; const auto sliceAssetId = UnitTest::SaveAsSlice({ grandParent }, GetApplication(), sliceAssets); - EntityList instantiatedEntities = - UnitTest::InstantiateSlice(sliceAssetId, sliceAssets); + EntityList instantiatedEntities = UnitTest::InstantiateSlice(sliceAssetId, sliceAssets); const AZ::EntityId entityIdToMove = instantiatedEntities.back()->GetId(); EditorEntityComponentChangeDetector editorEntityChangeDetector(entityIdToMove); @@ -321,8 +300,7 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // When EditorTransformComponentSelectionRequestBus::Event( - GetEntityContextId(), - &EditorTransformComponentSelectionRequests::CopyOrientationToSelectedEntitiesIndividual, + GetEntityContextId(), &EditorTransformComponentSelectionRequests::CopyOrientationToSelectedEntitiesIndividual, AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::DegToRad(90.0f))); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -362,10 +340,9 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Then AzToolsFramework::EntityIdList selectedEntities; - ToolsApplicationRequestBus::BroadcastResult( - selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities); + ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities); - AzToolsFramework::EntityIdList expectedSelectedEntities = {entity4, entity5, entity6}; + AzToolsFramework::EntityIdList expectedSelectedEntities = { entity4, entity5, entity6 }; EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities)); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -396,10 +373,9 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Then AzToolsFramework::EntityIdList selectedEntities; - ToolsApplicationRequestBus::BroadcastResult( - selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities); + ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities); - AzToolsFramework::EntityIdList expectedSelectedEntities = {m_entity1, entity2, entity3, entity4}; + AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entity1, entity2, entity3, entity4 }; EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities)); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -416,11 +392,9 @@ namespace UnitTest const auto finalTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 0.0f)); // calculate the position in screen space of the initial position of the entity - const auto initialPositionScreen = - AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState); + const auto initialPositionScreen = AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState); // calculate the position in screen space of the final position of the entity - const auto finalPositionScreen = - AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState); + const auto finalPositionScreen = AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState); // select the entity (this will cause the manipulators to appear in EditorTransformComponentSelection) AzToolsFramework::SelectEntity(m_entity1); @@ -452,10 +426,10 @@ namespace UnitTest } // simple widget to listen for a mouse wheel event and then forward it on to the ViewportSelectionRequestBus - class WheelEventWidget - : public QWidget + class WheelEventWidget : public QWidget { using MouseInteractionResult = AzToolsFramework::ViewportInteraction::MouseInteractionResult; + public: WheelEventWidget(QWidget* parent = nullptr) : QWidget(parent) @@ -490,8 +464,7 @@ namespace UnitTest { EditorTransformComponentSelectionRequests::Mode transformMode; EditorTransformComponentSelectionRequestBus::EventResult( - transformMode, GetEntityContextId(), - &EditorTransformComponentSelectionRequestBus::Events::GetTransformMode); + transformMode, GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::GetTransformMode); return transformMode; }; @@ -519,6 +492,56 @@ namespace UnitTest EXPECT_THAT(wheelEventWidget.m_mouseInteractionResult, Eq(vi::MouseInteractionResult::Viewport)); } + TEST_F(EditorTransformComponentSelectionFixture, EntityPositionsCanBeSnappedToGrid) + { + using ::testing::Pointwise; + + m_entityIds.push_back(CreateDefaultEditorEntity("Entity2")); + m_entityIds.push_back(CreateDefaultEditorEntity("Entity3")); + + const AZStd::vector initialUnsnappedPositions = { AZ::Vector3(1.2f, 3.5f, 6.7f), AZ::Vector3(13.2f, 15.6f, 11.4f), + AZ::Vector3(4.2f, 103.2f, 16.6f) }; + AZ::TransformBus::Event(m_entityIds[0], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[0]); + AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[1]); + AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[2]); + + AzToolsFramework::SelectEntities(m_entityIds); + + EditorTransformComponentSelectionRequestBus::Event( + GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::SnapSelectedEntitiesToWorldGrid, 2.0f); + + AZStd::vector entityPositionsAfterSnap; + AZStd::transform( + m_entityIds.cbegin(), m_entityIds.cend(), AZStd::back_inserter(entityPositionsAfterSnap), + [](const AZ::EntityId& entityId) + { + return GetWorldTranslation(entityId); + }); + + const AZStd::vector expectedSnappedPositions = { AZ::Vector3(2.0f, 4.0f, 6.0f), AZ::Vector3(14.0f, 16.0f, 12.0f), + AZ::Vector3(4.0f, 104.0f, 16.0f) }; + EXPECT_THAT(entityPositionsAfterSnap, Pointwise(ContainerIsClose(), expectedSnappedPositions)); + } + + TEST_F(EditorTransformComponentSelectionFixture, ManipulatorStaysAlignedToEntityTranslationAfterSnap) + { + const auto initialUnsnappedPosition = AZ::Vector3(1.2f, 3.5f, 6.7f); + AZ::TransformBus::Event(m_entityIds[0], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPosition); + + AzToolsFramework::SelectEntities(m_entityIds); + + EditorTransformComponentSelectionRequestBus::Event( + GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::SnapSelectedEntitiesToWorldGrid, 1.0f); + + const auto entityPositionAfterSnap = GetWorldTranslation(m_entity1); + const AZ::Vector3 manipulatorPositionAfterSnap = + GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()).GetTranslation(); + + const auto expectedSnappedPosition = AZ::Vector3(1.0f, 4.0f, 7.0f); + EXPECT_THAT(entityPositionAfterSnap, IsClose(expectedSnappedPosition)); + EXPECT_THAT(expectedSnappedPosition, IsClose(manipulatorPositionAfterSnap)); + } + // struct to contain input reference frame and expected orientation outcome based on // the reference frame, selection and entity hierarchy struct ReferenceFrameWithOrientation @@ -541,19 +564,20 @@ namespace UnitTest class EditorTransformComponentSelectionSingleEntityPivotFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P(EditorTransformComponentSelectionSingleEntityPivotFixture, PivotOrientationMatchesReferenceFrameSingleEntity) { - using ETCS::PivotOrientationResult; using ETCS::CalculatePivotOrientation; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given AZ::TransformBus::Event( m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateFromQuaternionAndTranslation( - ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero())); + AZ::Transform::CreateFromQuaternionAndTranslation(ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero())); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -574,20 +598,20 @@ namespace UnitTest All, EditorTransformComponentSelectionSingleEntityPivotFixture, testing::Values( - ReferenceFrameWithOrientation{ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace}, - ReferenceFrameWithOrientation{ReferenceFrame::Parent, AZ::Quaternion::CreateIdentity()}, - ReferenceFrameWithOrientation{ReferenceFrame::World, AZ::Quaternion::CreateIdentity()})); + ReferenceFrameWithOrientation{ ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace }, + ReferenceFrameWithOrientation{ ReferenceFrame::Parent, AZ::Quaternion::CreateIdentity() }, + ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() })); class EditorTransformComponentSelectionSingleEntityWithParentPivotFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; - TEST_P( - EditorTransformComponentSelectionSingleEntityWithParentPivotFixture, - PivotOrientationMatchesReferenceFrameEntityWithParent) + TEST_P(EditorTransformComponentSelectionSingleEntityWithParentPivotFixture, PivotOrientationMatchesReferenceFrameEntityWithParent) { - using ETCS::PivotOrientationResult; using ETCS::CalculatePivotOrientation; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -596,8 +620,7 @@ namespace UnitTest AZ::TransformBus::Event( parentEntityId, &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateFromQuaternionAndTranslation( - ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero())); + AZ::Transform::CreateFromQuaternionAndTranslation(ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero())); AZ::TransformBus::Event( m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, @@ -624,20 +647,20 @@ namespace UnitTest All, EditorTransformComponentSelectionSingleEntityWithParentPivotFixture, testing::Values( - ReferenceFrameWithOrientation{ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace}, - ReferenceFrameWithOrientation{ReferenceFrame::Parent, ParentExpectedPivotLocalOrientationInWorldSpace}, - ReferenceFrameWithOrientation{ReferenceFrame::World, AZ::Quaternion::CreateIdentity()})); + ReferenceFrameWithOrientation{ ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace }, + ReferenceFrameWithOrientation{ ReferenceFrame::Parent, ParentExpectedPivotLocalOrientationInWorldSpace }, + ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() })); class EditorTransformComponentSelectionMultipleEntitiesPivotFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; - TEST_P( - EditorTransformComponentSelectionMultipleEntitiesPivotFixture, - PivotOrientationMatchesReferenceFrameMultipleEntities) + TEST_P(EditorTransformComponentSelectionMultipleEntitiesPivotFixture, PivotOrientationMatchesReferenceFrameMultipleEntities) { - using ETCS::PivotOrientationResult; using ETCS::CalculatePivotOrientationForEntityIds; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -646,23 +669,18 @@ namespace UnitTest // setup entities in arbitrary triangle arrangement AZ::TransformBus::Event( - m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f))); + m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f))); AZ::TransformBus::Event( - m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); + m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); AZ::TransformBus::Event( - m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); + m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); // note: EntityIdManipulatorLookup{} is unused during this test - EntityIdManipulatorLookups lookups { - {m_entityIds[0], EntityIdManipulatorLookup{}}, - {m_entityIds[1], EntityIdManipulatorLookup{}}, - {m_entityIds[2], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} }, + { m_entityIds[1], EntityIdManipulatorLookup{} }, + { m_entityIds[2], EntityIdManipulatorLookup{} } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -691,14 +709,16 @@ namespace UnitTest class EditorTransformComponentSelectionMultipleEntitiesWithSameParentPivotFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P( EditorTransformComponentSelectionMultipleEntitiesWithSameParentPivotFixture, PivotOrientationMatchesReferenceFrameMultipleEntitiesSameParent) { - using ETCS::PivotOrientationResult; using ETCS::CalculatePivotOrientationForEntityIds; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -711,22 +731,18 @@ namespace UnitTest ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f))); AZ::TransformBus::Event( - m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); + m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); AZ::TransformBus::Event( - m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); + m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]); AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[0]); // note: EntityIdManipulatorLookup{} is unused during this test // only select second two entities that are children of m_entityIds[0] - EntityIdManipulatorLookups lookups{ - {m_entityIds[1], EntityIdManipulatorLookup{}}, - {m_entityIds[2], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} }, + { m_entityIds[2], EntityIdManipulatorLookup{} } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -755,14 +771,16 @@ namespace UnitTest class EditorTransformComponentSelectionMultipleEntitiesWithDifferentParentPivotFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P( EditorTransformComponentSelectionMultipleEntitiesWithDifferentParentPivotFixture, PivotOrientationMatchesReferenceFrameMultipleEntitiesDifferentParent) { - using ETCS::PivotOrientationResult; using ETCS::CalculatePivotOrientationForEntityIds; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -776,22 +794,18 @@ namespace UnitTest ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f))); AZ::TransformBus::Event( - m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); + m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); AZ::TransformBus::Event( - m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); + m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]); AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[3]); // note: EntityIdManipulatorLookup{} is unused during this test // only select second two entities that are children of different m_entities - EntityIdManipulatorLookups lookups{ - {m_entityIds[1], EntityIdManipulatorLookup{}}, - {m_entityIds[2], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} }, + { m_entityIds[2], EntityIdManipulatorLookup{} } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -819,30 +833,29 @@ namespace UnitTest class EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P( EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture, PivotOrientationMatchesReferenceFrameSingleEntityOptionalOverride) { - using ETCS::PivotOrientationResult; using ETCS::CalculateSelectionPivotOrientation; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given AZ::TransformBus::Event( m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateFromQuaternionAndTranslation( - ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero())); + AZ::Transform::CreateFromQuaternionAndTranslation(ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero())); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // When const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam(); - EntityIdManipulatorLookups lookups{ - {m_entityIds[0], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} } }; // set override frame (orientation only) OptionalFrame optionalFrame; @@ -870,14 +883,16 @@ namespace UnitTest class EditorTransformComponentSelectionMultipleEntitiesPivotAndOverrideFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P( EditorTransformComponentSelectionMultipleEntitiesPivotAndOverrideFixture, PivotOrientationMatchesReferenceFrameMultipleEntitiesOptionalOverride) { - using ETCS::PivotOrientationResult; using ETCS::CalculateSelectionPivotOrientation; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -885,23 +900,18 @@ namespace UnitTest m_entityIds.push_back(CreateDefaultEditorEntity("Entity3")); AZ::TransformBus::Event( - m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f))); + m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f))); AZ::TransformBus::Event( - m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); + m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); AZ::TransformBus::Event( - m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); + m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); // note: EntityIdManipulatorLookup{} is unused during this test - EntityIdManipulatorLookups lookups{ - {m_entityIds[0], EntityIdManipulatorLookup{}}, - {m_entityIds[1], EntityIdManipulatorLookup{}}, - {m_entityIds[2], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} }, + { m_entityIds[1], EntityIdManipulatorLookup{} }, + { m_entityIds[2], EntityIdManipulatorLookup{} } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -932,14 +942,16 @@ namespace UnitTest class EditorTransformComponentSelectionMultipleEntitiesPivotAndNoOverrideFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P( EditorTransformComponentSelectionMultipleEntitiesPivotAndNoOverrideFixture, PivotOrientationMatchesReferenceFrameMultipleEntitiesNoOptionalOverride) { - using ETCS::PivotOrientationResult; using ETCS::CalculateSelectionPivotOrientation; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -947,23 +959,18 @@ namespace UnitTest m_entityIds.push_back(CreateDefaultEditorEntity("Entity3")); AZ::TransformBus::Event( - m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f))); + m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f))); AZ::TransformBus::Event( - m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); + m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); AZ::TransformBus::Event( - m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); + m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); // note: EntityIdManipulatorLookup{} is unused during this test - EntityIdManipulatorLookups lookups{ - {m_entityIds[0], EntityIdManipulatorLookup{}}, - {m_entityIds[1], EntityIdManipulatorLookup{}}, - {m_entityIds[2], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} }, + { m_entityIds[1], EntityIdManipulatorLookup{} }, + { m_entityIds[2], EntityIdManipulatorLookup{} } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -992,14 +999,16 @@ namespace UnitTest class EditorTransformComponentSelectionMultipleEntitiesSameParentPivotAndNoOverrideFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P( EditorTransformComponentSelectionMultipleEntitiesSameParentPivotAndNoOverrideFixture, PivotOrientationMatchesReferenceFrameMultipleEntitiesSameParentNoOptionalOverride) { - using ETCS::PivotOrientationResult; using ETCS::CalculateSelectionPivotOrientation; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -1012,21 +1021,17 @@ namespace UnitTest ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f))); AZ::TransformBus::Event( - m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); + m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); AZ::TransformBus::Event( - m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); + m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]); AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[0]); // note: EntityIdManipulatorLookup{} is unused during this test - EntityIdManipulatorLookups lookups{ - {m_entityIds[1], EntityIdManipulatorLookup{}}, - {m_entityIds[2], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} }, + { m_entityIds[2], EntityIdManipulatorLookup{} } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1174,13 +1179,13 @@ namespace UnitTest AZ::TransformBus::Event(f, &AZ::TransformBus::Events::SetParent, secondLayerId); // Layer1 - // A - // B - // C - // Layer2 - // D - // E - // F + // A + // B + // C + // Layer2 + // D + // E + // F /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1270,13 +1275,13 @@ namespace UnitTest AZ::TransformBus::Event(f, &AZ::TransformBus::Events::SetParent, secondLayerId); // Layer1 - // A - // B - // C - // Layer2 - // D - // E - // F + // A + // B + // C + // Layer2 + // D + // E + // F /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1368,8 +1373,7 @@ namespace UnitTest EXPECT_TRUE(!IsEntityVisible(m_layerId)); bool flagSetVisible = false; - EditorVisibilityRequestBus::EventResult( - flagSetVisible, m_layerId, &EditorVisibilityRequestBus::Events::GetVisibilityFlag); + EditorVisibilityRequestBus::EventResult(flagSetVisible, m_layerId, &EditorVisibilityRequestBus::Events::GetVisibilityFlag); // even though a layer is set to not be visible, this is recorded by SetLayerChildrenVisibility // and AreLayerChildrenVisible - the visibility flag will not be modified and remains true @@ -1377,12 +1381,12 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// } - class EditorEntityInfoRequestActivateTestComponent - : public AzToolsFramework::Components::EditorComponentBase + class EditorEntityInfoRequestActivateTestComponent : public AzToolsFramework::Components::EditorComponentBase { public: AZ_EDITOR_COMPONENT( - EditorEntityInfoRequestActivateTestComponent, "{849DA1FC-6A0C-4CB8-A0BB-D90DEE7FF7F7}", + EditorEntityInfoRequestActivateTestComponent, + "{849DA1FC-6A0C-4CB8-A0BB-D90DEE7FF7F7}", AzToolsFramework::Components::EditorComponentBase); static void Reflect(AZ::ReflectContext* context); @@ -1391,13 +1395,13 @@ namespace UnitTest void Activate() override { // ensure we can successfully read IsVisible and IsLocked (bus will be connected to in entity Init) - EditorEntityInfoRequestBus::EventResult( - m_visible, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsVisible); - EditorEntityInfoRequestBus::EventResult( - m_locked, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsLocked); + EditorEntityInfoRequestBus::EventResult(m_visible, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsVisible); + EditorEntityInfoRequestBus::EventResult(m_locked, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsLocked); } - void Deactivate() override {} + void Deactivate() override + { + } bool m_visible = false; bool m_locked = true; @@ -1407,14 +1411,11 @@ namespace UnitTest { if (auto serializeContext = azrtti_cast(context)) { - serializeContext->Class() - ->Version(0) - ; + serializeContext->Class()->Version(0); } } - class EditorEntityModelEntityInfoRequestFixture - : public ToolsApplicationFixture + class EditorEntityModelEntityInfoRequestFixture : public ToolsApplicationFixture { public: void SetUpEditorFixtureImpl() override @@ -1435,8 +1436,7 @@ namespace UnitTest // This is necessary to prevent a warning in the undo system. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, - entity->GetId()); + &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entity->GetId()); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1469,8 +1469,7 @@ namespace UnitTest // This is necessary to prevent a warning in the undo system. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, - entity->GetId()); + &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entity->GetId()); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// From b1508b522fd2d1e31301d7bb251b26dc6eae0ccd Mon Sep 17 00:00:00 2001 From: John Date: Wed, 16 Jun 2021 13:25:26 +0100 Subject: [PATCH 219/233] Add build output folder to tiaf driver --- scripts/build/Platform/Windows/build_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 81100eead7..9201e13a80 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -88,7 +88,7 @@ "COMMAND": "python_windows.cmd", "PARAMETERS": { "SCRIPT_PATH": "scripts/build/TestImpactAnalysis/tiaf_driver.py", - "SCRIPT_PARAMETERS": "--testFailurePolicy abort --safeMode --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"!OUTPUT_DIRECTORY!\\TestImpactFramework\\persistent\\tiaf.profile.json\"" + "SCRIPT_PARAMETERS": "--testFailurePolicy abort --safeMode --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.profile.json\"" } }, "debug_vs2019": { From e7e68d3ba3a90fab8ad019ba4ef2b85d9904d6f5 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 16 Jun 2021 16:21:49 +0100 Subject: [PATCH 220/233] Set test failure policy to continue --- scripts/build/Platform/Windows/build_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 9201e13a80..db4e6b8fde 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -88,7 +88,7 @@ "COMMAND": "python_windows.cmd", "PARAMETERS": { "SCRIPT_PATH": "scripts/build/TestImpactAnalysis/tiaf_driver.py", - "SCRIPT_PARAMETERS": "--testFailurePolicy abort --safeMode --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.profile.json\"" + "SCRIPT_PARAMETERS": "--testFailurePolicy abort --testFailurePolicy=continue --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.profile.json\"" } }, "debug_vs2019": { From a1c6cb80a06364dca3b3121843c879dac762406a Mon Sep 17 00:00:00 2001 From: John Date: Wed, 16 Jun 2021 17:14:45 +0100 Subject: [PATCH 221/233] Fix double test failure policy --- scripts/build/Platform/Windows/build_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index db4e6b8fde..e52a3c2d53 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -88,7 +88,7 @@ "COMMAND": "python_windows.cmd", "PARAMETERS": { "SCRIPT_PATH": "scripts/build/TestImpactAnalysis/tiaf_driver.py", - "SCRIPT_PARAMETERS": "--testFailurePolicy abort --testFailurePolicy=continue --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.profile.json\"" + "SCRIPT_PARAMETERS": "--testFailurePolicy=continue --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.profile.json\"" } }, "debug_vs2019": { From 2f9a055a8fec46991f334526f9cb7140d5fe4ecc Mon Sep 17 00:00:00 2001 From: mnaumov Date: Wed, 16 Jun 2021 11:42:18 -0700 Subject: [PATCH 222/233] Fixing "type not registered with BehaviorContext" when running ASV --- .../DisplayMapperConfigurationDescriptor.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp index a064b61a1a..ae48c7defb 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp @@ -50,6 +50,8 @@ namespace AZ if (auto behaviorContext = azrtti_cast(context)) { + behaviorContext->Class("OutputDeviceTransformType"); + behaviorContext->Class("AcesParameterOverrides") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Category, "render") @@ -58,6 +60,16 @@ namespace AZ ->Method("LoadPreset", &AcesParameterOverrides::LoadPreset) ->Property("overrideDefaults", BehaviorValueProperty(&AcesParameterOverrides::m_overrideDefaults)) ->Property("preset", BehaviorValueProperty(&AcesParameterOverrides::m_preset)) + ->Enum(OutputDeviceTransformType::NumOutputDeviceTransformTypes)>( + "OutputDeviceTransformType_NumOutputDeviceTransformTypes") + ->Enum(OutputDeviceTransformType::OutputDeviceTransformType_48Nits)>( + "OutputDeviceTransformType_48Nits") + ->Enum(OutputDeviceTransformType::OutputDeviceTransformType_1000Nits)>( + "OutputDeviceTransformType_1000Nits") + ->Enum(OutputDeviceTransformType::OutputDeviceTransformType_2000Nits)>( + "OutputDeviceTransformType_2000Nits") + ->Enum(OutputDeviceTransformType::OutputDeviceTransformType_4000Nits)>( + "OutputDeviceTransformType_4000Nits") ->Property("alterSurround", BehaviorValueProperty(&AcesParameterOverrides::m_alterSurround)) ->Property("applyDesaturation", BehaviorValueProperty(&AcesParameterOverrides::m_applyDesaturation)) ->Property("applyCATD60toD65", BehaviorValueProperty(&AcesParameterOverrides::m_applyCATD60toD65)) From df912533abca0ebda5b12959362e8c521fc1ee40 Mon Sep 17 00:00:00 2001 From: Cynthia Lin Date: Fri, 11 Jun 2021 15:11:42 -0700 Subject: [PATCH 223/233] [ATOM-15781] Add Python script for calculating avg/max frame render time of a sample. --- .../timestamp_aggregator.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Gems/Atom/Feature/Common/Assets/Scripts/performance_metrics/timestamp_aggregator.py diff --git a/Gems/Atom/Feature/Common/Assets/Scripts/performance_metrics/timestamp_aggregator.py b/Gems/Atom/Feature/Common/Assets/Scripts/performance_metrics/timestamp_aggregator.py new file mode 100644 index 0000000000..0508395275 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Scripts/performance_metrics/timestamp_aggregator.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + +from genericpath import isdir +from argparse import ArgumentParser +import json +from pathlib import Path +import os + +# this allows us to add additional data if necessary, e.g. frame_test_timestamps.json +is_timestamp_file = lambda file: file.name.startswith('frame') and file.name.endswith('_timestamps.json') +ns_to_ms = lambda time: time / 1e6 + +def main(logs_dir): + count = 0 + total = 0 + maximum = 0 + + print(f'Analyzing frame timestamp logs in {logs_dir}') + + # go through files in alphabetical order (remove sorted() if not necessary) + for file in sorted(logs_dir.iterdir(), key=lambda file: len(file.name)): + if file.is_dir() or not is_timestamp_file(file): + continue + + data = json.loads(file.read_text()) + entries = data['ClassData']['timestampEntries'] + timestamps = [entry['timestampResultInNanoseconds'] for entry in entries] + + frame_time = sum(timestamps) + frame_name = file.name.split('_')[0] + print(f'- Total time for frame {frame_name}: {ns_to_ms(frame_time)}ms') + + maximum = max(maximum, frame_time) + total += frame_time + count += 1 + + if count < 1: + print(f'No logs were found in {base_dir}') + exit(1) + + print(f'Avg. time across {count} frames: {ns_to_ms(total / count)}ms') + print(f'Max frame time: {ns_to_ms(maximum)}ms') + +if __name__ == '__main__': + parser = ArgumentParser(description='Gathers statistics from a group of pass timestamp logs') + parser.add_argument('path', help='Path to the directory containing the pass timestamp logs') + args = parser.parse_args() + + base_dir = Path(args.path) + if not base_dir.exists(): + raise FileNotFoundError('Invalid path provided') + main(base_dir) From b315c13488116b5f9958792593861e33a53090c5 Mon Sep 17 00:00:00 2001 From: mnaumov Date: Wed, 16 Jun 2021 20:26:04 -0700 Subject: [PATCH 224/233] [LYN-4544] Fixing thumbnail crashing on bad data --- .../Rendering/ThumbnailRendererSteps/CaptureStep.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp index 16baf16886..f2f33c4236 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp @@ -96,6 +96,13 @@ namespace AZ RPI::AttachmentReadback::CallbackFunction readbackCallback = [&](const RPI::AttachmentReadback::ReadbackResult& result) { + if (!result.m_dataBuffer) + { + AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( + m_context->GetData()->m_thumbnailKeyRendered, + &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender); + return; + } uchar* data = result.m_dataBuffer.get()->data(); QImage image( data, result.m_imageDescriptor.m_size.m_width, result.m_imageDescriptor.m_size.m_height, QImage::Format_RGBA8888); From 8e9ffc458cf84e8a8c5a7389b44e456d7163ebab Mon Sep 17 00:00:00 2001 From: John Date: Thu, 17 Jun 2021 13:51:59 +0100 Subject: [PATCH 225/233] Fix PythonBindingsExampleTest.Application_Run_Fails --- Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp b/Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp index e9ea13fb82..a463a5216d 100644 --- a/Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp +++ b/Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp @@ -65,7 +65,7 @@ namespace PythonBindingsExample TEST_F(PythonBindingsExampleTest, Application_Run_Fails) { - EXPECT_FALSE(s_application->Run()); + EXPECT_TRUE(s_application->Run()); } TEST_F(PythonBindingsExampleTest, Application_RunWithParameters_Works) From 9f62d631fb71c6f7fea7020879a4730e7e14ec2c Mon Sep 17 00:00:00 2001 From: amzn-sean <75276488+amzn-sean@users.noreply.github.com> Date: Thu, 17 Jun 2021 15:52:27 +0100 Subject: [PATCH 226/233] Physics joints updated to the new API (#1361) Co-authored-by: Ulugbek Adilbekov --- ...43584_Joints_HingeSoftLimitsConstrained.py | 50 +- ...43584_Joints_HingeSoftLimitsConstrained.ly | 4 +- ...8243586_Joints_HingeLeadFollowerCollide.ly | 4 +- ...243589_Joints_BallSoftLimitsConstrained.ly | 4 +- ...18243591_Joints_BallLeadFollowerCollide.ly | 4 +- .../Physics/Common/PhysicsJoint.cpp | 36 + .../AzFramework/Physics/Common/PhysicsJoint.h | 143 ++++ .../AzFramework/Physics/Common/PhysicsTypes.h | 19 +- .../Configuration/JointConfiguration.cpp | 37 + .../Configuration/JointConfiguration.h | 51 ++ .../AzFramework/AzFramework/Physics/Joint.cpp | 56 -- .../AzFramework/AzFramework/Physics/Joint.h | 75 -- .../AzFramework/Physics/PhysicsScene.h | 39 ++ .../AzFramework/Physics/Ragdoll.cpp | 4 +- .../AzFramework/AzFramework/Physics/Ragdoll.h | 7 +- .../AzFramework/Physics/SystemBus.h | 53 -- .../AzFramework/AzFramework/Physics/Utils.cpp | 4 +- .../AzFramework/azframework_files.cmake | 6 +- Gems/Blast/Code/Tests/Mocks/BlastMocks.h | 18 - .../CommandSystem/Source/RagdollCommands.cpp | 33 +- .../CommandSystem/Source/RagdollCommands.h | 4 +- .../Ragdoll/RagdollJointLimitWidget.cpp | 20 +- .../Plugins/Ragdoll/RagdollJointLimitWidget.h | 1 - .../Ragdoll/RagdollNodeInspectorPlugin.cpp | 17 +- .../Ragdoll/RagdollNodeInspectorPlugin.h | 6 +- .../Code/Tests/D6JointLimitConfiguration.cpp | 2 +- .../Code/Tests/D6JointLimitConfiguration.h | 7 +- .../Code/Tests/Mocks/PhysicsSystem.h | 41 +- .../Ragdoll/CanCopyPasteColliders.cpp | 21 +- .../Ragdoll/CanCopyPasteJointLimits.cpp | 36 +- .../Code/Tests/RagdollCommandTests.cpp | 40 +- .../Code/Tests/UI/CanAddToSimulatedObject.cpp | 27 + .../Code/Tests/UI/RagdollEditTests.cpp | 28 + .../Code/emotionfx_editor_tests_files.cmake | 2 - .../Code/emotionfx_shared_tests_files.cmake | 2 + Gems/PhysX/Code/CMakeLists.txt | 11 + .../Code/Editor/EditorJointConfiguration.cpp | 39 +- .../Code/Editor/EditorJointConfiguration.h | 9 +- .../Configuration/PhysXJointConfiguration.h | 107 +++ Gems/PhysX/Code/Source/BallJointComponent.cpp | 66 +- Gems/PhysX/Code/Source/BallJointComponent.h | 9 +- .../Code/Source/EditorBallJointComponent.cpp | 1 + .../Code/Source/EditorBallJointComponent.h | 1 - .../Code/Source/EditorFixedJointComponent.cpp | 2 +- .../Code/Source/EditorFixedJointComponent.h | 1 - .../Code/Source/EditorHingeJointComponent.cpp | 1 + .../Code/Source/EditorHingeJointComponent.h | 1 - Gems/PhysX/Code/Source/EditorJointComponent.h | 1 - .../PhysX/Code/Source/FixedJointComponent.cpp | 46 +- Gems/PhysX/Code/Source/FixedJointComponent.h | 10 +- .../PhysX/Code/Source/HingeJointComponent.cpp | 66 +- Gems/PhysX/Code/Source/HingeJointComponent.h | 9 +- Gems/PhysX/Code/Source/Joint.cpp | 646 ------------------ Gems/PhysX/Code/Source/Joint.h | 311 --------- .../Configuration/PhysXJointConfiguration.cpp | 155 +++++ Gems/PhysX/Code/Source/Joint/PhysXJoint.cpp | 200 ++++++ Gems/PhysX/Code/Source/Joint/PhysXJoint.h | 126 ++++ .../Code/Source/Joint/PhysXJointUtils.cpp | 470 +++++++++++++ .../PhysX/Code/Source/Joint/PhysXJointUtils.h | 101 +++ Gems/PhysX/Code/Source/JointComponent.cpp | 98 +-- Gems/PhysX/Code/Source/JointComponent.h | 51 +- .../PhysXCharacters/API/CharacterUtils.cpp | 29 +- .../Source/PhysXCharacters/API/Ragdoll.cpp | 4 +- .../PhysXCharacters/API/RagdollNode.cpp | 22 +- .../Source/PhysXCharacters/API/RagdollNode.h | 9 +- .../Components/RagdollComponent.cpp | 2 +- .../Source/Platform/Android/PAL_android.cmake | 2 + .../Source/Platform/Linux/PAL_linux.cmake | 1 + .../Code/Source/Platform/Mac/PAL_mac.cmake | 1 + .../Source/Platform/Windows/PAL_windows.cmake | 1 + .../Code/Source/Platform/iOS/PAL_ios.cmake | 1 + Gems/PhysX/Code/Source/Scene/PhysXScene.cpp | 105 +++ Gems/PhysX/Code/Source/Scene/PhysXScene.h | 9 + .../Code/Source/Scene/PhysXSceneInterface.cpp | 31 + .../Code/Source/Scene/PhysXSceneInterface.h | 4 + .../Source/System/PhysXJointInterface.cpp | 299 ++++++++ .../Code/Source/System/PhysXJointInterface.h | 52 ++ Gems/PhysX/Code/Source/System/PhysXSystem.h | 2 + Gems/PhysX/Code/Source/SystemComponent.cpp | 45 -- Gems/PhysX/Code/Source/SystemComponent.h | 22 - Gems/PhysX/Code/Source/Utils.cpp | 10 +- .../Benchmarks/PhysXBenchmarksUtilities.h | 1 + .../Tests/Benchmarks/PhysXJointBenchmarks.cpp | 57 +- .../Code/Tests/PhysXGenericTestFixture.h | 2 +- Gems/PhysX/Code/Tests/PhysXJointsTest.cpp | 108 ++- .../PhysX/Code/Tests/RagdollConfiguration.xml | 176 ++--- Gems/PhysX/Code/Tests/RagdollTests.cpp | 4 +- Gems/PhysX/Code/physx_files.cmake | 10 +- .../Code/Source/SystemComponent.cpp | 2 +- .../Code/Tests/ScriptCanvasPhysicsTest.cpp | 7 + 90 files changed, 2792 insertions(+), 1665 deletions(-) create mode 100644 Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsJoint.cpp create mode 100644 Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsJoint.h create mode 100644 Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp create mode 100644 Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.h delete mode 100644 Code/Framework/AzFramework/AzFramework/Physics/Joint.cpp delete mode 100644 Code/Framework/AzFramework/AzFramework/Physics/Joint.h create mode 100644 Gems/PhysX/Code/Include/PhysX/Joint/Configuration/PhysXJointConfiguration.h delete mode 100644 Gems/PhysX/Code/Source/Joint.cpp delete mode 100644 Gems/PhysX/Code/Source/Joint.h create mode 100644 Gems/PhysX/Code/Source/Joint/Configuration/PhysXJointConfiguration.cpp create mode 100644 Gems/PhysX/Code/Source/Joint/PhysXJoint.cpp create mode 100644 Gems/PhysX/Code/Source/Joint/PhysXJoint.h create mode 100644 Gems/PhysX/Code/Source/Joint/PhysXJointUtils.cpp create mode 100644 Gems/PhysX/Code/Source/Joint/PhysXJointUtils.h create mode 100644 Gems/PhysX/Code/Source/System/PhysXJointInterface.cpp create mode 100644 Gems/PhysX/Code/Source/System/PhysXJointInterface.h diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243584_Joints_HingeSoftLimitsConstrained.py b/AutomatedTesting/Gem/PythonTests/physics/C18243584_Joints_HingeSoftLimitsConstrained.py index 90027be2d7..73800a54be 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243584_Joints_HingeSoftLimitsConstrained.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243584_Joints_HingeSoftLimitsConstrained.py @@ -56,6 +56,7 @@ def C18243584_Joints_HingeSoftLimitsConstrained(): """ import os import sys + import math import ImportPathHelper as imports @@ -93,22 +94,51 @@ def C18243584_Joints_HingeSoftLimitsConstrained(): Report.info_vector3(lead.position, "lead initial position:") Report.info_vector3(follower.position, "follower initial position:") leadInitialPosition = lead.position - followerInitialPosition = follower.position - - # 4) Wait for several seconds - general.idle_wait(4.0) # wait for lead and follower to move - + + # 4) Wait for the follower to move above the lead or Timeout + normalizedStartPos = JointsHelper.getRelativeVector(lead.position, follower.position) + normalizedStartPos = normalizedStartPos.GetNormalizedSafe() + + class WaitCondition: + TARGET_ANGLE = math.radians(45) + TARGET_MAX_ANGLE = math.radians(180) + + angleAchieved = 0.0 + followerMovedAbove45Deg = False #this is expected to be true to pass the test + followerMovedAbove180Deg = True #this is expected to be false to pass the test + + def checkConditionMet(self): + #calculate the current follower-lead vector + normalVec = JointsHelper.getRelativeVector(lead.position, follower.position) + normalVec = normalVec.GetNormalizedSafe() + #dot product + acos to get the angle + currentAngle = math.acos(normalizedStartPos.Dot(normalVec)) + #if the angle is now less then last time, it is no longer rising, so end the test. + if currentAngle < self.angleAchieved: + return True + + self.angleAchieved = currentAngle + self.followerMovedAbove45Deg = currentAngle > self.TARGET_ANGLE + self.followerMovedAbove180Deg = currentAngle > self.TARGET_MAX_ANGLE + return False + + def isFollowerPositionCorrect(self): + return self.followerMovedAbove45Deg and not self.followerMovedAbove180Deg + + waitCondition = WaitCondition() + + MAX_WAIT_TIME = 5.0 #seconds + conditionMet = helper.wait_for_condition(lambda: waitCondition.checkConditionMet(), MAX_WAIT_TIME) + # 5) Check to see if lead and follower behaved as expected - Report.info_vector3(lead.position, "lead position after 1 second:") - Report.info_vector3(follower.position, "follower position after 1 second:") + Report.info_vector3(lead.position, "lead position after test:") + Report.info_vector3(follower.position, "follower position after test:") leadPositionDelta = lead.position.Subtract(leadInitialPosition) leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON) Report.critical_result(Tests.check_lead_position, leadRemainedStill) - followerMovedInXOnly = ((follower.position.x > leadInitialPosition.x) > FLOAT_EPSILON and - (follower.position.z - leadInitialPosition.z) > FLOAT_EPSILON) - Report.critical_result(Tests.check_follower_position, followerMovedInXOnly) + Report.critical_result(Tests.check_follower_position, conditionMet and waitCondition.isFollowerPositionCorrect()) # 6) Exit Game Mode helper.exit_game_mode(Tests.exit_game_mode) diff --git a/AutomatedTesting/Levels/Physics/C18243584_Joints_HingeSoftLimitsConstrained/C18243584_Joints_HingeSoftLimitsConstrained.ly b/AutomatedTesting/Levels/Physics/C18243584_Joints_HingeSoftLimitsConstrained/C18243584_Joints_HingeSoftLimitsConstrained.ly index 585ca394f1..2307843887 100644 --- a/AutomatedTesting/Levels/Physics/C18243584_Joints_HingeSoftLimitsConstrained/C18243584_Joints_HingeSoftLimitsConstrained.ly +++ b/AutomatedTesting/Levels/Physics/C18243584_Joints_HingeSoftLimitsConstrained/C18243584_Joints_HingeSoftLimitsConstrained.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16f592487e8973abcf6b696aee6e430924c22daac0d6bb781c9e76153cd932f7 -size 8885 +oid sha256:352a64f523b3246000393309fa7f14955fe554e0b792a4177349f0f2db8a2b62 +size 5901 diff --git a/AutomatedTesting/Levels/Physics/C18243586_Joints_HingeLeadFollowerCollide/C18243586_Joints_HingeLeadFollowerCollide.ly b/AutomatedTesting/Levels/Physics/C18243586_Joints_HingeLeadFollowerCollide/C18243586_Joints_HingeLeadFollowerCollide.ly index d5675c2542..83ba9e3831 100644 --- a/AutomatedTesting/Levels/Physics/C18243586_Joints_HingeLeadFollowerCollide/C18243586_Joints_HingeLeadFollowerCollide.ly +++ b/AutomatedTesting/Levels/Physics/C18243586_Joints_HingeLeadFollowerCollide/C18243586_Joints_HingeLeadFollowerCollide.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d5c38cf9b97ae28c391916e6637aebf767d5ac009df61e730396fe8b116f3e5 -size 6913 +oid sha256:31bd1feb92c3bb8a5c5df4638927a9a80e879329965732c4c32f673c236a8b0a +size 6021 diff --git a/AutomatedTesting/Levels/Physics/C18243589_Joints_BallSoftLimitsConstrained/C18243589_Joints_BallSoftLimitsConstrained.ly b/AutomatedTesting/Levels/Physics/C18243589_Joints_BallSoftLimitsConstrained/C18243589_Joints_BallSoftLimitsConstrained.ly index 6091c9e137..ecd4b61cb0 100644 --- a/AutomatedTesting/Levels/Physics/C18243589_Joints_BallSoftLimitsConstrained/C18243589_Joints_BallSoftLimitsConstrained.ly +++ b/AutomatedTesting/Levels/Physics/C18243589_Joints_BallSoftLimitsConstrained/C18243589_Joints_BallSoftLimitsConstrained.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6f26f1c1c037fa0b848ac9b3d9a76e476b4853d770f1a77c01714286733b567 -size 6921 +oid sha256:063779c1e80ce22319cb82ff0d7635d3dcbb043b789c3830cc405ffa36d3c0ef +size 5876 diff --git a/AutomatedTesting/Levels/Physics/C18243591_Joints_BallLeadFollowerCollide/C18243591_Joints_BallLeadFollowerCollide.ly b/AutomatedTesting/Levels/Physics/C18243591_Joints_BallLeadFollowerCollide/C18243591_Joints_BallLeadFollowerCollide.ly index e50ffc9dff..3991492730 100644 --- a/AutomatedTesting/Levels/Physics/C18243591_Joints_BallLeadFollowerCollide/C18243591_Joints_BallLeadFollowerCollide.ly +++ b/AutomatedTesting/Levels/Physics/C18243591_Joints_BallLeadFollowerCollide/C18243591_Joints_BallLeadFollowerCollide.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ffd3c4ee04fa8a414995c39c7ca79246e3d9b0ceee1ad1b85d61a5298f71495 -size 6940 +oid sha256:5bd3a952841aa924a4869c74fad7ed397667a87948270f0ce35f314e6cf2e14a +size 5927 diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsJoint.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsJoint.cpp new file mode 100644 index 0000000000..c654038ead --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsJoint.cpp @@ -0,0 +1,36 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +#include +#include + +#include +#include + +namespace AzPhysics +{ + AZ_CLASS_ALLOCATOR_IMPL(Joint, AZ::SystemAllocator, 0); + + void Joint::Reflect(AZ::ReflectContext* context) + { + if (auto* serializeContext = azdynamic_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("SceneOwner", &Joint::m_sceneOwner) + ->Field("JointHandle", &Joint::m_jointHandle) + ; + } + } +} diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsJoint.h b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsJoint.h new file mode 100644 index 0000000000..a73a9e5cd4 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsJoint.h @@ -0,0 +1,143 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + class ReflectContext; +} + +namespace AzPhysics +{ + struct JointConfiguration; + + //! Base class for all Joints in Physics. + struct Joint + { + AZ_CLASS_ALLOCATOR_DECL; + AZ_RTTI(AzPhysics::Joint, "{1EEC9382-3434-4866-9B18-E93F151A6F59}"); + static void Reflect(AZ::ReflectContext* context); + + virtual ~Joint() = default; + + //! The current Scene the joint is contained. + SceneHandle m_sceneOwner = AzPhysics::InvalidSceneHandle; + + //! The handle to this joint. + JointHandle m_jointHandle = AzPhysics::InvalidJointHandle; + + //! Helper functions for setting user data. + //! @param userData Can be a pointer to any type as internally will be cast to a void*. Object lifetime not managed by the Joint. + template + void SetUserData(T* userData) + { + m_customUserData = static_cast(userData); + } + //! Helper functions for getting the set user data. + //! @return Will return a void* to the user data set. + void* GetUserData() + { + return m_customUserData; + } + + virtual AZ::Crc32 GetNativeType() const = 0; + virtual void* GetNativePointer() const = 0; + + virtual AzPhysics::SimulatedBodyHandle GetParentBodyHandle() const = 0; + virtual AzPhysics::SimulatedBodyHandle GetChildBodyHandle() const = 0; + + virtual void SetParentBody(AzPhysics::SimulatedBodyHandle parentBody) = 0; + virtual void SetChildBody(AzPhysics::SimulatedBodyHandle childBody) = 0; + + virtual void GenerateJointLimitVisualizationData( + [[ maybe_unused ]] float scale, + [[ maybe_unused ]] AZ::u32 angularSubdivisions, + [[ maybe_unused ]] AZ::u32 radialSubdivisions, + [[ maybe_unused ]] AZStd::vector& vertexBufferOut, + [[ maybe_unused ]] AZStd::vector& indexBufferOut, + [[ maybe_unused ]] AZStd::vector& lineBufferOut, + [[ maybe_unused ]] AZStd::vector& lineValidityBufferOut) { } + + private: + void* m_customUserData = nullptr; + }; + + //! Alias for a list of non owning weak pointers to Joint objects. + using JointList = AZStd::vector; + + //! Interface to access Joint utilities and helper functions + class JointHelpersInterface + { + public: + AZ_RTTI(AzPhysics::JointHelpersInterface, "{A511C64D-C8A5-4E8F-9C69-8DC5EFAD0C4C}"); + + JointHelpersInterface() = default; + virtual ~JointHelpersInterface() = default; + AZ_DISABLE_COPY_MOVE(JointHelpersInterface); + + //! Returns a list of supported Joint types + virtual const AZStd::vector GetSupportedJointTypeIds() const = 0; + + //! Returns a TypeID if the request joint type is supported. + //! If the Physics backend supports this joint type JointHelpersInterface::GetSupportedJointTypeId will return a AZ::TypeId. + virtual AZStd::optional GetSupportedJointTypeId(JointType typeEnum) const = 0; + + //! Computes parameters such as joint limit local rotations to give the desired initial joint limit orientation. + //! @param jointLimitTypeId The type ID used to identify the particular kind of joint limit configuration to be created. + //! @param parentWorldRotation The rotation in world space of the parent world body associated with the joint. + //! @param childWorldRotation The rotation in world space of the child world body associated with the joint. + //! @param axis Axis used to define the centre for limiting angular degrees of freedom. + //! @param exampleLocalRotations A vector (which may be empty) containing example valid rotations in the local space + //! of the child world body relative to the parent world body, which may optionally be used to help estimate the extents + //! of the joint limit. + virtual AZStd::unique_ptr ComputeInitialJointLimitConfiguration( + const AZ::TypeId& jointLimitTypeId, + const AZ::Quaternion& parentWorldRotation, + const AZ::Quaternion& childWorldRotation, + const AZ::Vector3& axis, + const AZStd::vector& exampleLocalRotations) = 0; + + /// Generates joint limit visualization data in appropriate format to pass to DebugDisplayRequests draw functions. + /// @param configuration The joint configuration to generate visualization data for. + /// @param parentRotation The rotation of the joint's parent body (in the same frame as childRotation). + /// @param childRotation The rotation of the joint's child body (in the same frame as parentRotation). + /// @param scale Scale factor for the output display data. + /// @param angularSubdivisions Level of detail in the angular direction (may be clamped in the implementation). + /// @param radialSubdivisions Level of detail in the radial direction (may be clamped in the implementation). + /// @param[out] vertexBufferOut Used with indexBufferOut to define triangles to be displayed. + /// @param[out] indexBufferOut Used with vertexBufferOut to define triangles to be displayed. + /// @param[out] lineBufferOut Used to define lines to be displayed. + /// @param[out] lineValidityBufferOut Whether each line in the line buffer is part of a valid or violated limit. + virtual void GenerateJointLimitVisualizationData( + const JointConfiguration& configuration, + const AZ::Quaternion& parentRotation, + const AZ::Quaternion& childRotation, + float scale, + AZ::u32 angularSubdivisions, + AZ::u32 radialSubdivisions, + AZStd::vector& vertexBufferOut, + AZStd::vector& indexBufferOut, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut) = 0; + }; +} diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsTypes.h b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsTypes.h index b2b5fd1511..42aee35c2c 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsTypes.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsTypes.h @@ -51,8 +51,10 @@ namespace AzPhysics using SceneIndex = AZ::s8; using SimulatedBodyIndex = AZ::s32; + using JointIndex = AZ::s32; static_assert(std::is_signed::value - && std::is_signed::value, "SceneIndex and SimulatedBodyIndex must be signed integers."); + && std::is_signed::value + && std::is_signed::value, "SceneIndex, SimulatedBodyIndex and JointIndex must be signed integers."); //! A handle to a Scene within the physics simulation. @@ -69,12 +71,27 @@ namespace AzPhysics static constexpr SimulatedBodyHandle InvalidSimulatedBodyHandle = { AZ::Crc32(), -1 }; using SimulatedBodyHandleList = AZStd::vector; + //! A handle to a Joint within a physics scene. + //! A JointHandle is a tuple of a Crc of the scene's name and the index in the Joint list. + using JointHandle = AZStd::tuple; + static constexpr JointHandle InvalidJointHandle = { AZ::Crc32(), -1 }; + //! Helper used for pairing the ShapeConfiguration and ColliderConfiguration together which is used when creating a Simulated Body. using ShapeColliderPair = AZStd::pair< AZStd::shared_ptr, AZStd::shared_ptr>; using ShapeColliderPairList = AZStd::vector; + //! Joint types are used to request for AZ::TypeId with the JointHelpersInterface::GetSupportedJointTypeId. + //! If the Physics backend supports this joint type JointHelpersInterface::GetSupportedJointTypeId will return a AZ::TypeId. + enum class JointType + { + D6Joint, + FixedJoint, + BallJoint, + HingeJoint + }; + //! Flags used to specifying which properties of a body to compute. enum class MassComputeFlags : AZ::u8 { diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp new file mode 100644 index 0000000000..aa58136944 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp @@ -0,0 +1,37 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +#include +#include + +namespace AzPhysics +{ + AZ_CLASS_ALLOCATOR_IMPL(JointConfiguration, AZ::SystemAllocator, 0); + + void JointConfiguration::Reflect(AZ::ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("Name", &JointConfiguration::m_debugName) + ->Field("ParentLocalRotation", &JointConfiguration::m_parentLocalRotation) + ->Field("ParentLocalPosition", &JointConfiguration::m_parentLocalPosition) + ->Field("ChildLocalRotation", &JointConfiguration::m_childLocalRotation) + ->Field("ChildLocalPosition", &JointConfiguration::m_childLocalPosition) + ->Field("StartSimulationEnabled", &JointConfiguration::m_startSimulationEnabled) + ; + } + } +} diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.h new file mode 100644 index 0000000000..8ebe1199a8 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.h @@ -0,0 +1,51 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + class ReflectContext; +} + +namespace AzPhysics +{ + //! Base Class of all Physics Joints that will be simulated. + struct JointConfiguration + { + AZ_CLASS_ALLOCATOR_DECL; + AZ_RTTI(AzPhysics::JointConfiguration, "{DF91D39A-4901-48C4-9159-93FD2ACA5252}"); + static void Reflect(AZ::ReflectContext* context); + + JointConfiguration() = default; + virtual ~JointConfiguration() = default; + + // Entity/object association. + void* m_customUserData = nullptr; + + // Basic initial settings. + AZ::Quaternion m_parentLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Parent joint frame relative to parent body. + AZ::Vector3 m_parentLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to parent body. + AZ::Quaternion m_childLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Child joint frame relative to child body. + AZ::Vector3 m_childLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to child body. + bool m_startSimulationEnabled = true; + + // For debugging/tracking purposes only. + AZStd::string m_debugName; + }; +} diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Joint.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Joint.cpp deleted file mode 100644 index 965583c113..0000000000 --- a/Code/Framework/AzFramework/AzFramework/Physics/Joint.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include -#include -#include -#include - -namespace Physics -{ - const char* JointLimitConfiguration::GetTypeName() - { - return "Base Joint"; - } - - void JointLimitConfiguration::Reflect(AZ::ReflectContext* context) - { - if (auto serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(1) - ->Field("ParentLocalRotation", &JointLimitConfiguration::m_parentLocalRotation) - ->Field("ParentLocalPosition", &JointLimitConfiguration::m_parentLocalPosition) - ->Field("ChildLocalRotation", &JointLimitConfiguration::m_childLocalRotation) - ->Field("ChildLocalPosition", &JointLimitConfiguration::m_childLocalPosition) - ; - - AZ::EditContext* editContext = serializeContext->GetEditContext(); - if (editContext) - { - editContext->Class( - "Joint Configuration", "") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->DataElement(AZ::Edit::UIHandlers::Default, &JointLimitConfiguration::m_parentLocalRotation, - "Parent local rotation", "The rotation of the parent joint frame relative to the parent body") - ->DataElement(AZ::Edit::UIHandlers::Default, &JointLimitConfiguration::m_parentLocalPosition, - "Parent local position", "The position of the joint in the frame of the parent body") - ->DataElement(AZ::Edit::UIHandlers::Default, &JointLimitConfiguration::m_childLocalRotation, - "Child local rotation", "The rotation of the child joint frame relative to the child body") - ->DataElement(AZ::Edit::UIHandlers::Default, &JointLimitConfiguration::m_childLocalPosition, - "Child local position", "The position of the joint in the frame of the child body") - ; - } - } - } -} // namespace Physics diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Joint.h b/Code/Framework/AzFramework/AzFramework/Physics/Joint.h deleted file mode 100644 index d78ce74611..0000000000 --- a/Code/Framework/AzFramework/AzFramework/Physics/Joint.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#include -#include - -namespace AzPhysics -{ - struct SimulatedBody; -} - -namespace Physics -{ - class JointLimitConfiguration - { - public: - AZ_CLASS_ALLOCATOR(JointLimitConfiguration, AZ::SystemAllocator, 0); - AZ_RTTI(JointLimitConfiguration, "{C9B70C4D-22D7-45AB-9B0A-30A4ED5E42DB}"); - static void Reflect(AZ::ReflectContext* context); - - JointLimitConfiguration() = default; - JointLimitConfiguration(const JointLimitConfiguration&) = default; - virtual ~JointLimitConfiguration() = default; - - virtual const char* GetTypeName(); - - AZ::Quaternion m_parentLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Parent joint frame relative to parent body. - AZ::Vector3 m_parentLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to parent body. - AZ::Quaternion m_childLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Child joint frame relative to child body. - AZ::Vector3 m_childLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to child body. - }; - - class Joint - { - public: - AZ_CLASS_ALLOCATOR(Joint, AZ::SystemAllocator, 0); - AZ_RTTI(Joint, "{405F517C-E986-4ACB-9606-D5D080DDE987}"); - - virtual AzPhysics::SimulatedBody* GetParentBody() const = 0; - virtual AzPhysics::SimulatedBody* GetChildBody() const = 0; - virtual void SetParentBody(AzPhysics::SimulatedBody* parentBody) = 0; - virtual void SetChildBody(AzPhysics::SimulatedBody* childBody) = 0; - virtual const AZStd::string& GetName() const = 0; - virtual void SetName(const AZStd::string& name) = 0; - virtual const AZ::Crc32 GetNativeType() const = 0; - virtual void* GetNativePointer() = 0; - /// Generates joint limit visualization data in appropriate format to pass to DebugDisplayRequests draw functions. - /// @param scale Scale factor for the output display data. - /// @param angularSubdivisions Level of detail in the angular direction (may be clamped in the implementation). - /// @param radialSubdivisions Level of detail in the radial direction (may be clamped in the implementation). - /// @param[out] vertexBufferOut Used with indexBufferOut to define triangles to be displayed. - /// @param[out] indexBufferOut Used with vertexBufferOut to define triangles to be displayed. - /// @param[out] lineBufferOut Used to define lines to be displayed. - /// @param[out] lineValidityBufferOut Whether each line in the line buffer is part of a valid or violated limit. - virtual void GenerateJointLimitVisualizationData( - float scale, - AZ::u32 angularSubdivisions, - AZ::u32 radialSubdivisions, - AZStd::vector& vertexBufferOut, - AZStd::vector& indexBufferOut, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut) = 0; - }; -} // namespace Physics diff --git a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.h b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.h index 58e53b0b0d..c93278e7bf 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include namespace AzPhysics @@ -103,6 +105,26 @@ namespace AzPhysics virtual void EnableSimulationOfBody(SceneHandle sceneHandle, SimulatedBodyHandle bodyHandle) = 0; virtual void DisableSimulationOfBody(SceneHandle sceneHandle, SimulatedBodyHandle bodyHandle) = 0; + //! Add a joint to the Scene. + //! @param sceneHandle A handle to the scene to add / remove the joint. + //! @param jointConfig The config of the joint. + //! @param parentBody The parent body of the joint. + //! @param childBody The child body of the joint + //! @return Returns a handle to the created joint. Will return AzPhyiscs::InvalidJointHandle if it fails. + virtual JointHandle AddJoint(SceneHandle sceneHandle, const JointConfiguration* jointConfig, + SimulatedBodyHandle parentBody, SimulatedBodyHandle childBody) = 0; + + //! Get the Raw pointer to the requested joint. + //! @param sceneHandle A handle to the scene to get the simulated bodies from. + //! @param jointHandle A handle to the joint to retrieve the raw pointer. + //! @return A raw pointer to the Joint body. If the either handle is invalid this will return null. + virtual Joint* GetJointFromHandle(SceneHandle sceneHandle, JointHandle jointHandle) = 0; + + //! Remove a joint from the Scene. + //! @param sceneHandle A handle to the scene to add / remove the joint. + //! @param jointHandle A handle to the joint being removed. + virtual void RemoveJoint(SceneHandle sceneHandle, JointHandle jointHandle) = 0; + //! Make a blocking query into the scene. //! @param sceneHandle A handle to the scene to make the scene query with. //! @param request The request to make. Should be one of RayCastRequest || ShapeCastRequest || OverlapRequest @@ -299,6 +321,23 @@ namespace AzPhysics virtual void EnableSimulationOfBody(SimulatedBodyHandle bodyHandle) = 0; virtual void DisableSimulationOfBody(SimulatedBodyHandle bodyHandle) = 0; + //! Add a joint to the Scene. + //! @param jointConfig The config of the joint. + //! @param parentBody The parent body of the joint. + //! @param childBody The child body of the joint + //! @return Returns a handle to the created joint. Will return AzPhyiscs::InvalidJointHandle if it fails. + virtual JointHandle AddJoint(const JointConfiguration* jointConfig, + SimulatedBodyHandle parentBody, SimulatedBodyHandle childBody) = 0; + + //! Get the Raw pointer to the requested joint. + //! @param jointHandle A handle to the joint to retrieve the raw pointer. + //! @return A raw pointer to the Joint body. If the either handle is invalid this will return null. + virtual Joint* GetJointFromHandle(JointHandle jointHandle) = 0; + + //! Remove a joint from the Scene. + //! @param jointHandle A handle to the joint being removed. + virtual void RemoveJoint(JointHandle jointHandle) = 0; + //! Make a blocking query into the scene. //! @param request The request to make. Should be one of RayCastRequest || ShapeCastRequest || OverlapRequest //! @return Returns a structure that contains a list of Hits. Depending on flags set in the request, this may only contain 1 result. diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.cpp index f634360445..f4b88fe087 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.cpp @@ -36,8 +36,8 @@ namespace Physics if (serializeContext) { serializeContext->Class() - ->Version(4, &ClassConverters::RagdollNodeConfigConverter) - ->Field("JointLimit", &RagdollNodeConfiguration::m_jointLimit) + ->Version(5, &ClassConverters::RagdollNodeConfigConverter) + ->Field("JointConfig", &RagdollNodeConfiguration::m_jointConfig) ; AZ::EditContext* editContext = serializeContext->GetEditContext(); diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.h b/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.h index 97c841e8f8..5dcc93c9da 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.h @@ -17,10 +17,11 @@ #include #include #include -#include #include +#include #include #include +#include namespace Physics { @@ -37,7 +38,7 @@ namespace Physics RagdollNodeConfiguration(); RagdollNodeConfiguration(const RagdollNodeConfiguration& settings) = default; - AZStd::shared_ptr m_jointLimit; + AZStd::shared_ptr m_jointConfig; }; class RagdollConfiguration @@ -73,7 +74,7 @@ namespace Physics virtual AzPhysics::RigidBody& GetRigidBody() = 0; virtual ~RagdollNode() = default; - virtual const AZStd::shared_ptr& GetJoint() const = 0; + virtual AzPhysics::Joint* GetJoint() = 0; virtual bool IsSimulating() const = 0; }; diff --git a/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h b/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h index 8cdd0e0cf0..73e9e4197a 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h @@ -26,23 +26,15 @@ namespace AZ namespace AzPhysics { struct SimulatedBody; - struct RigidBodyConfiguration; - struct RigidBody; } namespace Physics { - class WorldBody; class Shape; class Material; - class MaterialSelection; class MaterialConfiguration; class ColliderConfiguration; class ShapeConfiguration; - class JointLimitConfiguration; - class Joint; - class CharacterConfiguration; - class Character; /// Represents a debug vertex (position & color). struct DebugDrawVertex @@ -148,51 +140,6 @@ namespace Physics /// @param nativeMeshObject Pointer to the mesh object. virtual void ReleaseNativeMeshObject(void* nativeMeshObject) = 0; - ////////////////////////////////////////////////////////////////////////// - //// Joints - - virtual AZStd::vector GetSupportedJointTypes() = 0; - virtual AZStd::shared_ptr CreateJointLimitConfiguration(AZ::TypeId jointType) = 0; - virtual AZStd::shared_ptr CreateJoint(const AZStd::shared_ptr& configuration, - AzPhysics::SimulatedBody* parentBody, AzPhysics::SimulatedBody* childBody) = 0; - /// Generates joint limit visualization data in appropriate format to pass to DebugDisplayRequests draw functions. - /// @param configuration The joint configuration to generate visualization data for. - /// @param parentRotation The rotation of the joint's parent body (in the same frame as childRotation). - /// @param childRotation The rotation of the joint's child body (in the same frame as parentRotation). - /// @param scale Scale factor for the output display data. - /// @param angularSubdivisions Level of detail in the angular direction (may be clamped in the implementation). - /// @param radialSubdivisions Level of detail in the radial direction (may be clamped in the implementation). - /// @param[out] vertexBufferOut Used with indexBufferOut to define triangles to be displayed. - /// @param[out] indexBufferOut Used with vertexBufferOut to define triangles to be displayed. - /// @param[out] lineBufferOut Used to define lines to be displayed. - /// @param[out] lineValidityBufferOut Whether each line in the line buffer is part of a valid or violated limit. - virtual void GenerateJointLimitVisualizationData( - const JointLimitConfiguration& configuration, - const AZ::Quaternion& parentRotation, - const AZ::Quaternion& childRotation, - float scale, - AZ::u32 angularSubdivisions, - AZ::u32 radialSubdivisions, - AZStd::vector& vertexBufferOut, - AZStd::vector& indexBufferOut, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut) = 0; - - /// Computes parameters such as joint limit local rotations to give the desired initial joint limit orientation. - /// @param jointLimitTypeId The type ID used to identify the particular kind of joint limit configuration to be created. - /// @param parentWorldRotation The rotation in world space of the parent world body associated with the joint. - /// @param childWorldRotation The rotation in world space of the child world body associated with the joint. - /// @param axis Axis used to define the centre for limiting angular degrees of freedom. - /// @param exampleLocalRotations A vector (which may be empty) containing example valid rotations in the local space - /// of the child world body relative to the parent world body, which may optionally be used to help estimate the extents - /// of the joint limit. - virtual AZStd::unique_ptr ComputeInitialJointLimitConfiguration( - const AZ::TypeId& jointLimitTypeId, - const AZ::Quaternion& parentWorldRotation, - const AZ::Quaternion& childWorldRotation, - const AZ::Vector3& axis, - const AZStd::vector& exampleLocalRotations) = 0; - ////////////////////////////////////////////////////////////////////////// //// Cooking diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp index 17c09bb6ce..695fe1dcf5 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp @@ -34,6 +34,7 @@ #include #include #include +#include namespace Physics { @@ -121,9 +122,9 @@ namespace Physics DefaultMaterialConfiguration::Reflect(context); MaterialLibraryAsset::Reflect(context); MaterialInfoReflectionWrapper::Reflect(context); - JointLimitConfiguration::Reflect(context); AzPhysics::SimulatedBodyConfiguration::Reflect(context); AzPhysics::RigidBodyConfiguration::Reflect(context); + AzPhysics::JointConfiguration::Reflect(context); RagdollNodeConfiguration::Reflect(context); RagdollConfiguration::Reflect(context); CharacterColliderNodeConfiguration::Reflect(context); @@ -131,6 +132,7 @@ namespace Physics AnimationConfiguration::Reflect(context); CharacterConfiguration::Reflect(context); AzPhysics::SimulatedBody::Reflect(context); + AzPhysics::Joint::Reflect(context); ReflectSimulatedBodyComponentRequestsBus(context); CollisionFilteringRequests::Reflect(context); AzPhysics::SceneQuery::ReflectSceneQueryObjects(context); diff --git a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake index 13dff43f68..2a5d251400 100644 --- a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake +++ b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake @@ -204,6 +204,8 @@ set(FILES Physics/Collision/CollisionLayers.cpp Physics/Collision/CollisionGroups.h Physics/Collision/CollisionGroups.cpp + Physics/Common/PhysicsJoint.h + Physics/Common/PhysicsJoint.cpp Physics/Common/PhysicsSceneQueries.h Physics/Common/PhysicsSceneQueries.cpp Physics/Common/PhysicsEvents.h @@ -215,6 +217,8 @@ set(FILES Physics/Common/PhysicsSimulatedBodyEvents.cpp Physics/Common/PhysicsTypes.h Physics/Components/SimulatedBodyComponentBus.h + Physics/Configuration/JointConfiguration.h + Physics/Configuration/JointConfiguration.cpp Physics/Configuration/CollisionConfiguration.h Physics/Configuration/CollisionConfiguration.cpp Physics/Configuration/RigidBodyConfiguration.h @@ -259,8 +263,6 @@ set(FILES Physics/Ragdoll.h Physics/Utils.h Physics/Utils.cpp - Physics/Joint.h - Physics/Joint.cpp Physics/ClassConverters.cpp Physics/ClassConverters.h Physics/MaterialBus.h diff --git a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h index ca78623a8a..b1fea21550 100644 --- a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h +++ b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -221,23 +220,6 @@ namespace Blast AZStd::vector>(const Physics::MaterialSelection&)); MOCK_METHOD2( UpdateMaterialSelection, bool(const Physics::ShapeConfiguration&, Physics::ColliderConfiguration&)); - MOCK_METHOD0(GetSupportedJointTypes, AZStd::vector()); - MOCK_METHOD1(CreateJointLimitConfiguration, AZStd::shared_ptr(AZ::TypeId)); - MOCK_METHOD3( - CreateJoint, - AZStd::shared_ptr( - const AZStd::shared_ptr&, AzPhysics::SimulatedBody*, AzPhysics::SimulatedBody*)); - MOCK_METHOD10( - GenerateJointLimitVisualizationData, - void( - const Physics::JointLimitConfiguration&, const AZ::Quaternion&, const AZ::Quaternion&, float, AZ::u32, - AZ::u32, AZStd::vector&, AZStd::vector&, AZStd::vector&, - AZStd::vector&)); - MOCK_METHOD5( - ComputeInitialJointLimitConfiguration, - AZStd::unique_ptr( - const AZ::TypeId&, const AZ::Quaternion&, const AZ::Quaternion&, const AZ::Vector3&, - const AZStd::vector&)); MOCK_METHOD3(CookConvexMeshToFile, bool(const AZStd::string&, const AZ::Vector3*, AZ::u32)); MOCK_METHOD3(CookConvexMeshToMemory, bool(const AZ::Vector3*, AZ::u32, AZStd::vector&)); MOCK_METHOD5( diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/RagdollCommands.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/RagdollCommands.cpp index 22c875d8c3..864f80a2c5 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/RagdollCommands.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/RagdollCommands.cpp @@ -71,12 +71,7 @@ namespace EMotionFX newNodeConfig.m_debugName = jointName; // Create joint limit on default. - AZStd::vector supportedJointLimitTypes; - Physics::SystemRequestBus::BroadcastResult(supportedJointLimitTypes, &Physics::SystemRequests::GetSupportedJointTypes); - if (!supportedJointLimitTypes.empty()) - { - newNodeConfig.m_jointLimit = CommandRagdollHelpers::CreateJointLimitByType(supportedJointLimitTypes[0], skeleton, joint); - } + newNodeConfig.m_jointConfig = CommandRagdollHelpers::CreateJointLimitByType(AzPhysics::JointType::D6Joint, skeleton, joint); if (index) { @@ -91,8 +86,8 @@ namespace EMotionFX } } - AZStd::unique_ptr CommandRagdollHelpers::CreateJointLimitByType( - const AZ::TypeId& typeId, const Skeleton* skeleton, const Node* node) + AZStd::unique_ptr CommandRagdollHelpers::CreateJointLimitByType( + AzPhysics::JointType jointType, const Skeleton* skeleton, const Node* node) { const Pose* bindPose = skeleton->GetBindPose(); const Transform& nodeBindTransform = bindPose->GetModelSpaceTransform(node->GetNodeIndex()); @@ -105,12 +100,20 @@ namespace EMotionFX AZ::Vector3 boneDirection = GetBoneDirection(skeleton, node); AZStd::vector exampleRotationsLocal; - AZStd::unique_ptr jointLimitConfig = - AZ::Interface::Get()->ComputeInitialJointLimitConfiguration( - typeId, parentBindRotationWorld, nodeBindRotationWorld, boneDirection, exampleRotationsLocal); + if (auto* jointHelpers = AZ::Interface::Get()) + { + if (AZStd::optional jointTypeId = jointHelpers->GetSupportedJointTypeId(jointType); + jointTypeId.has_value()) + { + AZStd::unique_ptr jointLimitConfig = jointHelpers->ComputeInitialJointLimitConfiguration( + *jointTypeId, parentBindRotationWorld, nodeBindRotationWorld, boneDirection, exampleRotationsLocal); - AZ_Assert(jointLimitConfig, "Could not create joint limit configuration with type '%s'.", typeId.ToString().c_str()); - return jointLimitConfig; + AZ_Assert(jointLimitConfig, "Could not create joint limit configuration."); + return jointLimitConfig; + } + } + AZ_Assert(false, "Could not create joint limit configuration."); + return nullptr; } void CommandRagdollHelpers::AddJointsToRagdoll(AZ::u32 actorId, const AZStd::vector& jointNames, @@ -532,7 +535,7 @@ namespace EMotionFX if (m_serializedJointLimits) { AZ::Outcome oldSerializedJointLimits = SerializeJointLimits(nodeConfig); - success |= MCore::ReflectionSerializer::DeserializeMembers(nodeConfig->m_jointLimit.get(), m_serializedJointLimits.value()); + success |= MCore::ReflectionSerializer::DeserializeMembers(nodeConfig->m_jointConfig.get(), m_serializedJointLimits.value()); if (success && oldSerializedJointLimits.IsSuccess()) { m_oldSerializedJointLimits = oldSerializedJointLimits.GetValue(); @@ -565,7 +568,7 @@ namespace EMotionFX AZ::Outcome CommandAdjustRagdollJoint::SerializeJointLimits(const Physics::RagdollNodeConfiguration* ragdollNodeConfig) { return MCore::ReflectionSerializer::SerializeMembersExcept( - ragdollNodeConfig->m_jointLimit.get(), + ragdollNodeConfig->m_jointConfig.get(), {"ParentLocalRotation", "ParentLocalPosition", "ChildLocalRotation", "ChildLocalPosition", } ); } diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/RagdollCommands.h b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/RagdollCommands.h index 8e94f028db..36683c82b4 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/RagdollCommands.h +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/RagdollCommands.h @@ -45,8 +45,8 @@ namespace EMotionFX Physics::RagdollConfiguration& ragdollConfig, const AZStd::optional& index, AZStd::string& outResult); - static AZStd::unique_ptr CreateJointLimitByType(const AZ::TypeId& typeId, - const Skeleton* skeleton, const Node* node); + static AZStd::unique_ptr CreateJointLimitByType( + AzPhysics::JointType jointType, const Skeleton* skeleton, const Node* node); static void AddJointsToRagdoll(AZ::u32 actorId, const AZStd::vector& jointNames, MCore::CommandGroup* commandGroup = nullptr, bool executeInsideCommand = false, bool addDefaultCollider = true); diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollJointLimitWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollJointLimitWidget.cpp index 934bb113d5..96464de230 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollJointLimitWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollJointLimitWidget.cpp @@ -90,12 +90,15 @@ namespace EMotionFX if (serializeContext) { - AZStd::vector supportedJointLimitTypes; - Physics::SystemRequestBus::BroadcastResult(supportedJointLimitTypes, &Physics::SystemRequests::GetSupportedJointTypes); - for (const AZ::TypeId& jointLimitType : supportedJointLimitTypes) + //D6 joint is the only currently supported joint for ragdoll + if (auto* jointHelpers = AZ::Interface::Get()) { - const char* jointLimitName = serializeContext->FindClassData(jointLimitType)->m_editData->m_name; - m_typeComboBox->addItem(jointLimitName, jointLimitType.ToString().c_str()); + if (AZStd::optional d6jointTypeId = jointHelpers->GetSupportedJointTypeId(AzPhysics::JointType::D6Joint); + d6jointTypeId.has_value()) + { + const char* jointLimitName = serializeContext->FindClassData(*d6jointTypeId)->m_editData->m_name; + m_typeComboBox->addItem(jointLimitName, (*d6jointTypeId).ToString().c_str()); + } } // Reflected property editor for joint limit @@ -134,7 +137,7 @@ namespace EMotionFX Physics::RagdollNodeConfiguration* ragdollNodeConfig = GetRagdollNodeConfig(); if (ragdollNodeConfig) { - Physics::JointLimitConfiguration* jointLimitConfig = ragdollNodeConfig->m_jointLimit.get(); + AzPhysics::JointConfiguration* jointLimitConfig = ragdollNodeConfig->m_jointConfig.get(); if (jointLimitConfig) { const AZ::TypeId& jointTypeId = jointLimitConfig->RTTI_GetType(); @@ -262,13 +265,14 @@ namespace EMotionFX { if (type.IsNull()) { - ragdollNodeConfig->m_jointLimit = nullptr; + ragdollNodeConfig->m_jointConfig = nullptr; } else { const Node* node = m_nodeIndex.data(SkeletonModel::ROLE_POINTER).value(); const Skeleton* skeleton = m_nodeIndex.data(SkeletonModel::ROLE_ACTOR_POINTER).value()->GetSkeleton(); - ragdollNodeConfig->m_jointLimit = CommandRagdollHelpers::CreateJointLimitByType(type, skeleton, node); + ragdollNodeConfig->m_jointConfig = + CommandRagdollHelpers::CreateJointLimitByType(AzPhysics::JointType::D6Joint, skeleton, node); } Update(); diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollJointLimitWidget.h b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollJointLimitWidget.h index 211c70e9bf..7861334239 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollJointLimitWidget.h +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollJointLimitWidget.h @@ -13,7 +13,6 @@ #pragma once #if !defined(Q_MOC_RUN) -#include #include #include #include diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.cpp index e2fde04016..467c56f16a 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.cpp @@ -514,7 +514,7 @@ namespace EMotionFX if (renderJointLimits && jointSelected) { - const AZStd::shared_ptr& jointLimitConfig = ragdollNode.m_jointLimit; + const AZStd::shared_ptr& jointLimitConfig = ragdollNode.m_jointConfig; if (jointLimitConfig) { const Node* ragdollParentNode = physicsSetup->FindRagdollParentNode(joint); @@ -528,7 +528,8 @@ namespace EMotionFX } } - void RagdollNodeInspectorPlugin::RenderJointLimit(const Physics::JointLimitConfiguration& configuration, + void RagdollNodeInspectorPlugin::RenderJointLimit( + const AzPhysics::JointConfiguration& configuration, const ActorInstance* actorInstance, const Node* node, const Node* parentNode, @@ -549,9 +550,12 @@ namespace EMotionFX m_indexBuffer.clear(); m_lineBuffer.clear(); m_lineValidityBuffer.clear(); - Physics::SystemRequestBus::Broadcast(&Physics::SystemRequests::GenerateJointLimitVisualizationData, - configuration, parentOrientation, childOrientation, s_scale, s_angularSubdivisions, s_radialSubdivisions, - m_vertexBuffer, m_indexBuffer, m_lineBuffer, m_lineValidityBuffer); + if(auto* jointHelpers = AZ::Interface::Get()) + { + jointHelpers->GenerateJointLimitVisualizationData( + configuration, parentOrientation, childOrientation, s_scale, s_angularSubdivisions, s_radialSubdivisions, m_vertexBuffer, + m_indexBuffer, m_lineBuffer, m_lineValidityBuffer); + } Transform jointModelSpaceTransform = currentPose->GetModelSpaceTransform(parentNodeIndex); jointModelSpaceTransform.mPosition = currentPose->GetModelSpaceTransform(nodeIndex).mPosition; @@ -572,7 +576,8 @@ namespace EMotionFX } } - void RagdollNodeInspectorPlugin::RenderJointFrame(const Physics::JointLimitConfiguration& configuration, + void RagdollNodeInspectorPlugin::RenderJointFrame( + const AzPhysics::JointConfiguration& configuration, const ActorInstance* actorInstance, const Node* node, const Node* parentNode, diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h index f7aa1277b0..7bba1b0b4f 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h @@ -60,14 +60,16 @@ namespace EMotionFX void Render(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; void RenderRagdoll(ActorInstance* actorInstance, bool renderColliders, bool renderJointLimits, EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo); - void RenderJointLimit(const Physics::JointLimitConfiguration& jointConfiguration, + void RenderJointLimit( + const AzPhysics::JointConfiguration& jointConfiguration, const ActorInstance* actorInstance, const Node* node, const Node* parentNode, EMStudio::RenderPlugin* renderPlugin, EMStudio::EMStudioPlugin::RenderInfo* renderInfo, const MCore::RGBAColor& color); - void RenderJointFrame(const Physics::JointLimitConfiguration& jointConfiguration, + void RenderJointFrame( + const AzPhysics::JointConfiguration& jointConfiguration, const ActorInstance* actorInstance, const Node* node, const Node* parentNode, diff --git a/Gems/EMotionFX/Code/Tests/D6JointLimitConfiguration.cpp b/Gems/EMotionFX/Code/Tests/D6JointLimitConfiguration.cpp index 417946524d..73c0a9fdae 100644 --- a/Gems/EMotionFX/Code/Tests/D6JointLimitConfiguration.cpp +++ b/Gems/EMotionFX/Code/Tests/D6JointLimitConfiguration.cpp @@ -19,7 +19,7 @@ namespace EMotionFX { if (auto serializeContext = azrtti_cast(context)) { - serializeContext->Class() + serializeContext->Class() ->Version(1) ->Field("SwingLimitY", &D6JointLimitConfiguration::m_swingLimitY) ->Field("SwingLimitZ", &D6JointLimitConfiguration::m_swingLimitZ) diff --git a/Gems/EMotionFX/Code/Tests/D6JointLimitConfiguration.h b/Gems/EMotionFX/Code/Tests/D6JointLimitConfiguration.h index 47b6b4bef4..b41ba6435b 100644 --- a/Gems/EMotionFX/Code/Tests/D6JointLimitConfiguration.h +++ b/Gems/EMotionFX/Code/Tests/D6JointLimitConfiguration.h @@ -12,23 +12,22 @@ #pragma once -#include +#include namespace EMotionFX { // Add so that RagdollNodeInspectorPlugin::PhysXCharactersGemAvailable() will return the correct value // We duplicated the D6JointLimitConfiguration because it doesn't exist in the test environment. class D6JointLimitConfiguration - : public Physics::JointLimitConfiguration + : public AzPhysics::JointConfiguration { public: AZ_CLASS_ALLOCATOR(D6JointLimitConfiguration, AZ::SystemAllocator, 0); // This uses the same uuid as the production D6JointLimitConfiguration. // The Ragdoll UI uses this UUID to see if physx is available. - AZ_RTTI(D6JointLimitConfiguration, "{90C5C23D-16C0-4F23-AD50-A190E402388E}", Physics::JointLimitConfiguration); + AZ_RTTI(D6JointLimitConfiguration, "{90C5C23D-16C0-4F23-AD50-A190E402388E}", AzPhysics::JointConfiguration); static void Reflect(AZ::ReflectContext* context); - const char* GetTypeName() override { return "D6 Joint"; } float m_swingLimitY = 45.0f; ///< Maximum angle in degrees from the Y axis of the joint frame. float m_swingLimitZ = 45.0f; ///< Maximum angle in degrees from the Z axis of the joint frame. diff --git a/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h b/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h index 3aafdb4e2b..2ad3853edb 100644 --- a/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h +++ b/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h @@ -35,11 +35,6 @@ namespace Physics MOCK_METHOD2(CreateShape, AZStd::shared_ptr(const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& configuration)); MOCK_METHOD1(ReleaseNativeMeshObject, void(void* nativeMeshObject)); MOCK_METHOD1(CreateMaterial, AZStd::shared_ptr(const Physics::MaterialConfiguration& materialConfiguration)); - MOCK_METHOD0(GetSupportedJointTypes, AZStd::vector()); - MOCK_METHOD1(CreateJointLimitConfiguration, AZStd::shared_ptr(AZ::TypeId jointType)); - MOCK_METHOD3(CreateJoint, AZStd::shared_ptr(const AZStd::shared_ptr& configuration, AzPhysics::SimulatedBody* parentBody, AzPhysics::SimulatedBody* childBody)); - MOCK_METHOD10(GenerateJointLimitVisualizationData, void(const Physics::JointLimitConfiguration& configuration, const AZ::Quaternion& parentRotation, const AZ::Quaternion& childRotation, float scale, AZ::u32 angularSubdivisions, AZ::u32 radialSubdivisions, AZStd::vector& vertexBufferOut, AZStd::vector& indexBufferOut, AZStd::vector& lineBufferOut, AZStd::vector& lineValidityBufferOut)); - MOCK_METHOD5(ComputeInitialJointLimitConfiguration, AZStd::unique_ptr(const AZ::TypeId& jointLimitTypeId, const AZ::Quaternion& parentWorldRotation, const AZ::Quaternion& childWorldRotation, const AZ::Vector3& axis, const AZStd::vector& exampleLocalRotations)); MOCK_METHOD3(CookConvexMeshToFile, bool(const AZStd::string& filePath, const AZ::Vector3* vertices, AZ::u32 vertexCount)); MOCK_METHOD3(CookConvexMeshToMemory, bool(const AZ::Vector3* vertices, AZ::u32 vertexCount, AZStd::vector& result)); MOCK_METHOD5(CookTriangleMeshToFile, bool(const AZStd::string& filePath, const AZ::Vector3* vertices, AZ::u32 vertexCount, const AZ::u32* indices, AZ::u32 indexCount)); @@ -72,6 +67,36 @@ namespace Physics MOCK_CONST_METHOD0(GetDefaultSceneConfiguration, const AzPhysics::SceneConfiguration& ()); }; + class MockJointHelpersInterface : AZ::Interface::Registrar + { + public: + MOCK_CONST_METHOD0(GetSupportedJointTypeIds, const AZStd::vector()); + MOCK_CONST_METHOD1(GetSupportedJointTypeId, AZStd::optional(AzPhysics::JointType typeEnum)); + + MOCK_METHOD5( + ComputeInitialJointLimitConfiguration, + AZStd::unique_ptr( + const AZ::TypeId& jointLimitTypeId, + const AZ::Quaternion& parentWorldRotation, + const AZ::Quaternion& childWorldRotation, + const AZ::Vector3& axis, + const AZStd::vector& exampleLocalRotations)); + + MOCK_METHOD10( + GenerateJointLimitVisualizationData, + void( + const AzPhysics::JointConfiguration& configuration, + const AZ::Quaternion& parentRotation, + const AZ::Quaternion& childRotation, + float scale, + AZ::u32 angularSubdivisions, + AZ::u32 radialSubdivisions, + AZStd::vector& vertexBufferOut, + AZStd::vector& indexBufferOut, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut)); + }; + //Mocked of the AzPhysics Scene Interface. To keep things simple just mocked functions that have a return value OR required for a test. class MockPhysicsSceneInterface : AZ::Interface::Registrar @@ -97,6 +122,9 @@ namespace Physics void DisableSimulationOfBody( [[maybe_unused]] AzPhysics::SceneHandle sceneHandle, [[maybe_unused]] AzPhysics::SimulatedBodyHandle bodyHandle) override {} + void RemoveJoint( + [[maybe_unused]]AzPhysics::SceneHandle sceneHandle, + [[maybe_unused]] AzPhysics::JointHandle jointHandle) override {} void SuppressCollisionEvents( [[maybe_unused]] AzPhysics::SceneHandle sceneHandle, [[maybe_unused]] const AzPhysics::SimulatedBodyHandle& bodyHandleA, @@ -145,6 +173,9 @@ namespace Physics MOCK_METHOD2(AddSimulatedBodies, AzPhysics::SimulatedBodyHandleList(AzPhysics::SceneHandle sceneHandle, const AzPhysics::SimulatedBodyConfigurationList& simulatedBodyConfigs)); MOCK_METHOD2(GetSimulatedBodyFromHandle, AzPhysics::SimulatedBody* (AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle bodyHandle)); MOCK_METHOD2(GetSimulatedBodiesFromHandle, AzPhysics::SimulatedBodyList(AzPhysics::SceneHandle sceneHandle, const AzPhysics::SimulatedBodyHandleList& bodyHandles)); + MOCK_METHOD4(AddJoint, AzPhysics::JointHandle(AzPhysics::SceneHandle sceneHandle, const AzPhysics::JointConfiguration* jointConfig, + AzPhysics::SimulatedBodyHandle parentBody, AzPhysics::SimulatedBodyHandle childBody)); + MOCK_METHOD2(GetJointFromHandle, AzPhysics::Joint* (AzPhysics::SceneHandle sceneHandle, AzPhysics::JointHandle bodyHandle)); MOCK_CONST_METHOD1(GetGravity, AZ::Vector3(AzPhysics::SceneHandle sceneHandle)); MOCK_METHOD2(RegisterSceneSimulationFinishHandler, void(AzPhysics::SceneHandle sceneHandle, AzPhysics::SceneEvents::OnSceneSimulationFinishHandler& handler)); MOCK_CONST_METHOD2(GetLegacyBody, AzPhysics::SimulatedBody* (AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle handle)); diff --git a/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteColliders.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteColliders.cpp index eaef6f31ee..7e5f481453 100644 --- a/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteColliders.cpp +++ b/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteColliders.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -44,10 +45,21 @@ namespace EMotionFX D6JointLimitConfiguration::Reflect(GetSerializeContext()); - const AZ::TypeId& jointLimitTypeId = azrtti_typeid(); - EXPECT_CALL(m_physicsSystem, GetSupportedJointTypes) - .WillRepeatedly(testing::Return(AZStd::vector{jointLimitTypeId})); - EXPECT_CALL(m_physicsSystem, ComputeInitialJointLimitConfiguration(jointLimitTypeId, _, _, _, _)) + EXPECT_CALL(m_jointHelpers, GetSupportedJointTypeIds) + .WillRepeatedly(testing::Return(AZStd::vector{ azrtti_typeid() })); + + EXPECT_CALL(m_jointHelpers, GetSupportedJointTypeId(_)) + .WillRepeatedly( + [](AzPhysics::JointType jointType) -> AZStd::optional + { + if (jointType == AzPhysics::JointType::D6Joint) + { + return azrtti_typeid(); + } + return AZStd::nullopt; + }); + + EXPECT_CALL(m_jointHelpers, ComputeInitialJointLimitConfiguration(_, _, _, _, _)) .WillRepeatedly([]([[maybe_unused]] const AZ::TypeId& jointLimitTypeId, [[maybe_unused]] const AZ::Quaternion& parentWorldRotation, [[maybe_unused]] const AZ::Quaternion& childWorldRotation, @@ -59,6 +71,7 @@ namespace EMotionFX private: Physics::MockPhysicsSystem m_physicsSystem; Physics::MockPhysicsInterface m_physicsInterface; + Physics::MockJointHelpersInterface m_jointHelpers; }; #if AZ_TRAIT_DISABLE_FAILED_EMOTION_FX_EDITOR_TESTS diff --git a/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteJointLimits.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteJointLimits.cpp index 6223af3599..4f65220400 100644 --- a/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteJointLimits.cpp +++ b/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteJointLimits.cpp @@ -55,10 +55,27 @@ namespace EMotionFX Physics::MockPhysicsSystem physicsSystem; Physics::MockPhysicsInterface physicsInterface; - EXPECT_CALL(physicsSystem, GetSupportedJointTypes) - .WillRepeatedly(testing::Return(AZStd::vector{azrtti_typeid()})); - EXPECT_CALL(physicsSystem, ComputeInitialJointLimitConfiguration(azrtti_typeid(), _, _, _, _)) - .WillRepeatedly([]([[maybe_unused]] const AZ::TypeId& jointLimitTypeId, [[maybe_unused]] const AZ::Quaternion& parentWorldRotation, [[maybe_unused]] const AZ::Quaternion& childWorldRotation, [[maybe_unused]] const AZ::Vector3& axis, [[maybe_unused]] const AZStd::vector& exampleLocalRotations) { return AZStd::make_unique(); }); + Physics::MockJointHelpersInterface jointHelpers; + EXPECT_CALL(jointHelpers, GetSupportedJointTypeIds) + .WillRepeatedly(testing::Return(AZStd::vector{ azrtti_typeid() })); + EXPECT_CALL(jointHelpers, GetSupportedJointTypeId(_)) + .WillRepeatedly( + [](AzPhysics::JointType jointType) -> AZStd::optional + { + if (jointType == AzPhysics::JointType::D6Joint) + { + return azrtti_typeid(); + } + return AZStd::nullopt; + }); + EXPECT_CALL(jointHelpers, ComputeInitialJointLimitConfiguration(_, _, _, _, _)) + .WillRepeatedly( + []([[maybe_unused]] const AZ::TypeId& jointLimitTypeId, + [[maybe_unused]] const AZ::Quaternion& parentWorldRotation, [[maybe_unused]] const AZ::Quaternion& childWorldRotation, + [[maybe_unused]] const AZ::Vector3& axis, [[maybe_unused]] const AZStd::vector& exampleLocalRotations) + { + return AZStd::make_unique(); + }); AutoRegisteredActor actor {ActorFactory::CreateAndInit(4)}; @@ -70,7 +87,7 @@ namespace EMotionFX CommandRagdollHelpers::AddJointsToRagdoll(actor->GetID(), {"rootJoint", "joint1", "joint2", "joint3"}); const Physics::RagdollConfiguration& ragdollConfig = actor->GetPhysicsSetup()->GetRagdollConfig(); ASSERT_EQ(ragdollConfig.m_nodes.size(), 4); - AZStd::shared_ptr rootJointLimit = AZStd::rtti_pointer_cast(ragdollConfig.FindNodeConfigByName("rootJoint")->m_jointLimit); + AZStd::shared_ptr rootJointLimit = AZStd::rtti_pointer_cast(ragdollConfig.FindNodeConfigByName("rootJoint")->m_jointConfig); ASSERT_TRUE(rootJointLimit); // Set the initial joint limits on the rootJoint to be something different rootJointLimit->m_swingLimitY = 1.0f; @@ -119,7 +136,8 @@ namespace EMotionFX selectionModel.select(joint1Index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); // Verify initial state of joint1's limits - const AZStd::shared_ptr joint1Limit = AZStd::rtti_pointer_cast(ragdollConfig.FindNodeConfigByName("joint1")->m_jointLimit); + const AZStd::shared_ptr joint1Limit = + AZStd::rtti_pointer_cast(ragdollConfig.FindNodeConfigByName("joint1")->m_jointConfig); EXPECT_EQ(joint1Limit->m_swingLimitY, 45.0f); EXPECT_EQ(joint1Limit->m_swingLimitZ, 45.0f); @@ -141,10 +159,12 @@ namespace EMotionFX selectionModel.select(joint2Index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); selectionModel.select(joint3Index, QItemSelectionModel::Select | QItemSelectionModel::Rows); - const AZStd::shared_ptr joint2Limit = AZStd::rtti_pointer_cast(ragdollConfig.FindNodeConfigByName("joint2")->m_jointLimit); + const AZStd::shared_ptr joint2Limit = + AZStd::rtti_pointer_cast(ragdollConfig.FindNodeConfigByName("joint2")->m_jointConfig); EXPECT_EQ(joint2Limit->m_swingLimitY, 45.0f); EXPECT_EQ(joint2Limit->m_swingLimitZ, 45.0f); - const AZStd::shared_ptr joint3Limit = AZStd::rtti_pointer_cast(ragdollConfig.FindNodeConfigByName("joint3")->m_jointLimit); + const AZStd::shared_ptr joint3Limit = + AZStd::rtti_pointer_cast(ragdollConfig.FindNodeConfigByName("joint3")->m_jointConfig); EXPECT_EQ(joint3Limit->m_swingLimitY, 45.0f); EXPECT_EQ(joint3Limit->m_swingLimitZ, 45.0f); diff --git a/Gems/EMotionFX/Code/Tests/RagdollCommandTests.cpp b/Gems/EMotionFX/Code/Tests/RagdollCommandTests.cpp index 279d3ff38c..f78dc417ac 100644 --- a/Gems/EMotionFX/Code/Tests/RagdollCommandTests.cpp +++ b/Gems/EMotionFX/Code/Tests/RagdollCommandTests.cpp @@ -18,11 +18,47 @@ #include #include #include - +#include +#include namespace EMotionFX { - using RagdollCommandTests = ActorFixture; + class RagdollCommandTests : public ActorFixture + { + public: + void SetUp() override + { + using ::testing::_; + + ActorFixture::SetUp(); + + D6JointLimitConfiguration::Reflect(GetSerializeContext()); + + EXPECT_CALL(m_jointHelpers, GetSupportedJointTypeIds) + .WillRepeatedly(testing::Return(AZStd::vector{ azrtti_typeid() })); + + EXPECT_CALL(m_jointHelpers, GetSupportedJointTypeId(_)) + .WillRepeatedly( + [](AzPhysics::JointType jointType) -> AZStd::optional + { + if (jointType == AzPhysics::JointType::D6Joint) + { + return azrtti_typeid(); + } + return AZStd::nullopt; + }); + + EXPECT_CALL(m_jointHelpers, ComputeInitialJointLimitConfiguration(_, _, _, _, _)) + .WillRepeatedly([]([[maybe_unused]] const AZ::TypeId& jointLimitTypeId, + [[maybe_unused]] const AZ::Quaternion& parentWorldRotation, + [[maybe_unused]] const AZ::Quaternion& childWorldRotation, + [[maybe_unused]] const AZ::Vector3& axis, + [[maybe_unused]] const AZStd::vector& exampleLocalRotations) + { return AZStd::make_unique(); }); + } + protected: + Physics::MockJointHelpersInterface m_jointHelpers; + }; AZStd::vector GetRagdollJointNames(const Actor* actor) { diff --git a/Gems/EMotionFX/Code/Tests/UI/CanAddToSimulatedObject.cpp b/Gems/EMotionFX/Code/Tests/UI/CanAddToSimulatedObject.cpp index 50fb434a26..8b75cfbf37 100644 --- a/Gems/EMotionFX/Code/Tests/UI/CanAddToSimulatedObject.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanAddToSimulatedObject.cpp @@ -10,6 +10,7 @@ * */ +#include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include namespace EMotionFX { @@ -140,6 +142,31 @@ namespace EMotionFX TEST_F(CanAddToSimulatedObjectFixture, CanAddCollidersfromRagdoll) { + using ::testing::_; + Physics::MockJointHelpersInterface jointHelpers; + EXPECT_CALL(jointHelpers, GetSupportedJointTypeIds) + .WillRepeatedly(testing::Return(AZStd::vector{ azrtti_typeid() })); + + EXPECT_CALL(jointHelpers, GetSupportedJointTypeId(_)) + .WillRepeatedly( + [](AzPhysics::JointType jointType) -> AZStd::optional + { + if (jointType == AzPhysics::JointType::D6Joint) + { + return azrtti_typeid(); + } + return AZStd::nullopt; + }); + + EXPECT_CALL(jointHelpers, ComputeInitialJointLimitConfiguration(_, _, _, _, _)) + .WillRepeatedly( + []([[maybe_unused]] const AZ::TypeId& jointLimitTypeId, [[maybe_unused]] const AZ::Quaternion& parentWorldRotation, + [[maybe_unused]] const AZ::Quaternion& childWorldRotation, [[maybe_unused]] const AZ::Vector3& axis, + [[maybe_unused]] const AZStd::vector& exampleLocalRotations) + { + return AZStd::make_unique(); + }); + RecordProperty("test_case_id", "C13291807"); AutoRegisteredActor actor = ActorFactory::CreateAndInit(7, "CanAddToSimulatedObjectActor"); diff --git a/Gems/EMotionFX/Code/Tests/UI/RagdollEditTests.cpp b/Gems/EMotionFX/Code/Tests/UI/RagdollEditTests.cpp index bd30ac378d..9cc2a3add2 100644 --- a/Gems/EMotionFX/Code/Tests/UI/RagdollEditTests.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/RagdollEditTests.cpp @@ -10,6 +10,7 @@ * */ +#include #include #include @@ -27,6 +28,7 @@ #include #include +#include namespace EMotionFX { @@ -35,6 +37,8 @@ namespace EMotionFX public: void SetUp() override { + using ::testing::_; + SetupQtAndFixtureBase(); AZ::SerializeContext* serializeContext = nullptr; @@ -42,6 +46,29 @@ namespace EMotionFX D6JointLimitConfiguration::Reflect(serializeContext); + EXPECT_CALL(m_jointHelpers, GetSupportedJointTypeIds) + .WillRepeatedly(testing::Return(AZStd::vector{ azrtti_typeid() })); + + EXPECT_CALL(m_jointHelpers, GetSupportedJointTypeId(_)) + .WillRepeatedly( + [](AzPhysics::JointType jointType) -> AZStd::optional + { + if (jointType == AzPhysics::JointType::D6Joint) + { + return azrtti_typeid(); + } + return AZStd::nullopt; + }); + + EXPECT_CALL(m_jointHelpers, ComputeInitialJointLimitConfiguration(_, _, _, _, _)) + .WillRepeatedly( + []([[maybe_unused]] const AZ::TypeId& jointLimitTypeId, [[maybe_unused]] const AZ::Quaternion& parentWorldRotation, + [[maybe_unused]] const AZ::Quaternion& childWorldRotation, [[maybe_unused]] const AZ::Vector3& axis, + [[maybe_unused]] const AZStd::vector& exampleLocalRotations) + { + return AZStd::make_unique(); + }); + SetupPluginWindows(); } @@ -73,6 +100,7 @@ namespace EMotionFX QModelIndexList m_indexList; ReselectingTreeView* m_treeView; EMotionFX::SkeletonOutlinerPlugin* m_skeletonOutliner; + Physics::MockJointHelpersInterface m_jointHelpers; }; diff --git a/Gems/EMotionFX/Code/emotionfx_editor_tests_files.cmake b/Gems/EMotionFX/Code/emotionfx_editor_tests_files.cmake index e2670c20f7..58366f8ffb 100644 --- a/Gems/EMotionFX/Code/emotionfx_editor_tests_files.cmake +++ b/Gems/EMotionFX/Code/emotionfx_editor_tests_files.cmake @@ -63,8 +63,6 @@ set(FILES Tests/Integration/CanDeleteJackEntity.cpp Tests/Bugs/CanDeleteMotionWhenMotionIsBeingBlended.cpp Tests/Bugs/CanUndoParameterDeletionAndRestoreBlendTreeConnections.cpp - Tests/D6JointLimitConfiguration.cpp - Tests/D6JointLimitConfiguration.h Tests/Editor/FileManagerTests.cpp Tests/Editor/ParametersGroupDefaultValues.cpp Tests/Editor/MotionSetLoadEscalation.cpp diff --git a/Gems/EMotionFX/Code/emotionfx_shared_tests_files.cmake b/Gems/EMotionFX/Code/emotionfx_shared_tests_files.cmake index 968dbd0f52..f1a0be0b8d 100644 --- a/Gems/EMotionFX/Code/emotionfx_shared_tests_files.cmake +++ b/Gems/EMotionFX/Code/emotionfx_shared_tests_files.cmake @@ -21,4 +21,6 @@ set(FILES Tests/TestAssetCode/AnimGraphAssetFactory.h Tests/TestAssetCode/ActorAssetFactory.h Tests/TestAssetCode/MotionSetAssetFactory.h + Tests/D6JointLimitConfiguration.cpp + Tests/D6JointLimitConfiguration.h ) diff --git a/Gems/PhysX/Code/CMakeLists.txt b/Gems/PhysX/Code/CMakeLists.txt index d281270ce4..caeaf884bf 100644 --- a/Gems/PhysX/Code/CMakeLists.txt +++ b/Gems/PhysX/Code/CMakeLists.txt @@ -156,6 +156,8 @@ endif() ################################################################################ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + + ly_add_target( NAME PhysX.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} NAMESPACE Gem @@ -174,6 +176,15 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) RUNTIME_DEPENDENCIES Gem::LmbrCentral ) + + if(PAL_TRAIT_JOINTS_TYPED_TEST_CASE) + ly_add_source_properties( + SOURCES Tests/PhysXJointsTest.cpp + PROPERTY COMPILE_DEFINITIONS + VALUES ENABLE_JOINTS_TYPED_TEST_CASE + ) + endif() + ly_add_googletest( NAME Gem::PhysX.Tests ) diff --git a/Gems/PhysX/Code/Editor/EditorJointConfiguration.cpp b/Gems/PhysX/Code/Editor/EditorJointConfiguration.cpp index 7b4ec15880..0ceec25918 100644 --- a/Gems/PhysX/Code/Editor/EditorJointConfiguration.cpp +++ b/Gems/PhysX/Code/Editor/EditorJointConfiguration.cpp @@ -144,11 +144,12 @@ namespace PhysX { return m_standardLimitConfig.m_isLimited; } - GenericJointLimitsConfiguration EditorJointLimitPairConfig::ToGameTimeConfig() const + + JointLimitProperties EditorJointLimitPairConfig::ToGameTimeConfig() const { - return GenericJointLimitsConfiguration(m_standardLimitConfig.m_damping - , m_standardLimitConfig.m_isLimited + return JointLimitProperties(m_standardLimitConfig.m_isLimited , m_standardLimitConfig.m_isSoftLimit + , m_standardLimitConfig.m_damping , m_limitPositive, m_limitNegative , m_standardLimitConfig.m_stiffness , m_standardLimitConfig.m_tolerance); @@ -193,11 +194,11 @@ namespace PhysX return m_standardLimitConfig.m_isLimited; } - GenericJointLimitsConfiguration EditorJointLimitConeConfig::ToGameTimeConfig() const + JointLimitProperties EditorJointLimitConeConfig::ToGameTimeConfig() const { - return GenericJointLimitsConfiguration(m_standardLimitConfig.m_damping - , m_standardLimitConfig.m_isLimited + return JointLimitProperties(m_standardLimitConfig.m_isLimited , m_standardLimitConfig.m_isSoftLimit + , m_standardLimitConfig.m_damping , m_limitY , m_limitZ , m_standardLimitConfig.m_stiffness @@ -275,27 +276,31 @@ namespace PhysX , AzToolsFramework::Refresh_AttributesAndValues); } - GenericJointConfiguration EditorJointConfig::ToGameTimeConfig() const + JointGenericProperties EditorJointConfig::ToGenericProperties() const { - GenericJointConfiguration::GenericJointFlag flags = GenericJointConfiguration::GenericJointFlag::None; + JointGenericProperties::GenericJointFlag flags = JointGenericProperties::GenericJointFlag::None; if (m_breakable) { - flags |= GenericJointConfiguration::GenericJointFlag::Breakable; + flags |= JointGenericProperties::GenericJointFlag::Breakable; } if (m_selfCollide) { - flags |= GenericJointConfiguration::GenericJointFlag::SelfCollide; + flags |= JointGenericProperties::GenericJointFlag::SelfCollide; } + return JointGenericProperties(flags, m_forceMax, m_torqueMax); + } + + JointComponentConfiguration EditorJointConfig::ToGameTimeConfig() const + { AZ::Vector3 localRotation(m_localRotation); - return GenericJointConfiguration(m_forceMax - , m_torqueMax - , AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateFromEulerAnglesDegrees(localRotation), - m_localPosition) - , m_leadEntity - , m_followerEntity - , flags); + return JointComponentConfiguration( + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateFromEulerAnglesDegrees(localRotation), + m_localPosition), + m_leadEntity, + m_followerEntity); } bool EditorJointConfig::IsInComponentMode() const diff --git a/Gems/PhysX/Code/Editor/EditorJointConfiguration.h b/Gems/PhysX/Code/Editor/EditorJointConfiguration.h index a7146124f0..aa0062b3ac 100644 --- a/Gems/PhysX/Code/Editor/EditorJointConfiguration.h +++ b/Gems/PhysX/Code/Editor/EditorJointConfiguration.h @@ -15,7 +15,7 @@ #include #include -#include +#include namespace PhysX { @@ -68,7 +68,7 @@ namespace PhysX static void Reflect(AZ::ReflectContext* context); bool IsLimited() const; - GenericJointLimitsConfiguration ToGameTimeConfig() const; + JointLimitProperties ToGameTimeConfig() const; EditorJointLimitConfig m_standardLimitConfig; float m_limitPositive = 45.0f; @@ -87,7 +87,7 @@ namespace PhysX static void Reflect(AZ::ReflectContext* context); bool IsLimited() const; - GenericJointLimitsConfiguration ToGameTimeConfig() const; + JointLimitProperties ToGameTimeConfig() const; EditorJointLimitConfig m_standardLimitConfig; float m_limitY = 45.0f; @@ -105,7 +105,8 @@ namespace PhysX static void Reflect(AZ::ReflectContext* context); void SetLeadEntityId(AZ::EntityId leadEntityId); - GenericJointConfiguration ToGameTimeConfig() const; + JointGenericProperties ToGenericProperties() const; + JointComponentConfiguration ToGameTimeConfig() const; bool m_breakable = false; bool m_displayJointSetup = false; diff --git a/Gems/PhysX/Code/Include/PhysX/Joint/Configuration/PhysXJointConfiguration.h b/Gems/PhysX/Code/Include/PhysX/Joint/Configuration/PhysXJointConfiguration.h new file mode 100644 index 0000000000..873a939192 --- /dev/null +++ b/Gems/PhysX/Code/Include/PhysX/Joint/Configuration/PhysXJointConfiguration.h @@ -0,0 +1,107 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace PhysX +{ + struct D6JointLimitConfiguration + : public AzPhysics::JointConfiguration + { + AZ_CLASS_ALLOCATOR(D6JointLimitConfiguration, AZ::SystemAllocator, 0); + AZ_RTTI(D6JointLimitConfiguration, "{88E067B4-21E8-4FFA-9142-6C52605B704C}", AzPhysics::JointConfiguration); + static void Reflect(AZ::ReflectContext* context); + + float m_swingLimitY = 45.0f; ///< Maximum angle in degrees from the Y axis of the joint frame. + float m_swingLimitZ = 45.0f; ///< Maximum angle in degrees from the Z axis of the joint frame. + float m_twistLimitLower = -45.0f; ///< Lower limit in degrees for rotation about the X axis of the joint frame. + float m_twistLimitUpper = 45.0f; ///< Upper limit in degrees for rotation about the X axis of the joint frame. + }; + + //! Properties that are common for several types of joints. + struct JointGenericProperties + { + enum class GenericJointFlag : AZ::u16 + { + None = 0, + Breakable = 1, + SelfCollide = 1 << 1 + }; + + AZ_CLASS_ALLOCATOR(JointGenericProperties, AZ::SystemAllocator, 0); + AZ_TYPE_INFO(JointGenericProperties, "{6CB15399-24F6-4F03-AAEF-1AE013B683E0}"); + static void Reflect(AZ::ReflectContext* context); + + JointGenericProperties() = default; + JointGenericProperties(GenericJointFlag flags, float forceMax, float torqueMax); + + bool IsFlagSet(GenericJointFlag flag) const; ///< Returns if a particular flag is set as a bool. + + /// Flags that indicates if joint is breakable, self-colliding, etc. + /// Converting joint between breakable/non-breakable at game time is allowed. + GenericJointFlag m_flags = GenericJointFlag::None; + float m_forceMax = 1.0f; ///< Max force joint can tolerate before breaking. + float m_torqueMax = 1.0f; ///< Max torque joint can tolerate before breaking. + }; + AZ_DEFINE_ENUM_BITWISE_OPERATORS(PhysX::JointGenericProperties::GenericJointFlag) + + struct JointLimitProperties + { + AZ_CLASS_ALLOCATOR(JointLimitProperties, AZ::SystemAllocator, 0); + AZ_TYPE_INFO(JointLimitProperties, "{31F941CB-6699-48BB-B12D-61874B52B984}"); + static void Reflect(AZ::ReflectContext* context); + + JointLimitProperties() = default; + JointLimitProperties( + bool isLimited, bool isSoftLimit, + float damping, float limitFirst, float limitSecond, float stiffness, float tolerance); + + bool m_isLimited = true; ///< Specifies if limits are applied to the joint constraints. E.g. if the swing angles are limited. + bool m_isSoftLimit = false; ///< If limit is soft, spring and damping are used, otherwise tolerance is used. Converting between soft/hard limit at game time is allowed. + float m_damping = 20.0f; ///< The damping strength of the drive, the force proportional to the velocity error. Used if limit is soft. + float m_limitFirst = 45.0f; ///< Positive angle limit in the case of twist angle limits, Y-axis swing limit in the case of cone limits. + float m_limitSecond = 45.0f; ///< Negative angle limit in the case of twist angle limits, Z-axis swing limit in the case of cone limits. + float m_stiffness = 100.0f; ///< The spring strength of the drive, the force proportional to the position error. Used if limit is soft. + float m_tolerance = 0.1f; ///< Distance from the joint at which limits becomes enforced. Used if limit is hard. + }; + + struct FixedJointConfiguration : public AzPhysics::JointConfiguration + { + AZ_CLASS_ALLOCATOR(FixedJointConfiguration, AZ::SystemAllocator, 0); + AZ_RTTI(FixedJointConfiguration, "{9BCB368B-8D71-4928-B231-0225907E3BD9}", AzPhysics::JointConfiguration); + static void Reflect(AZ::ReflectContext* context); + + JointGenericProperties m_genericProperties; + }; + + struct BallJointConfiguration : public AzPhysics::JointConfiguration + { + AZ_CLASS_ALLOCATOR(BallJointConfiguration, AZ::SystemAllocator, 0); + AZ_RTTI(BallJointConfiguration, "{C2DE2479-B752-469D-BE05-900CD2CD8481}", AzPhysics::JointConfiguration); + static void Reflect(AZ::ReflectContext* context); + + JointGenericProperties m_genericProperties; + JointLimitProperties m_limitProperties; + }; + + struct HingeJointConfiguration : public AzPhysics::JointConfiguration + { + AZ_CLASS_ALLOCATOR(HingeJointConfiguration, AZ::SystemAllocator, 0); + AZ_RTTI(HingeJointConfiguration, "{FB04198E-0BA5-45C2-8343-66DA28ED45EA}", AzPhysics::JointConfiguration); + static void Reflect(AZ::ReflectContext* context); + + JointGenericProperties m_genericProperties; + JointLimitProperties m_limitProperties; + }; +} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/BallJointComponent.cpp b/Gems/PhysX/Code/Source/BallJointComponent.cpp index b37efd5a33..0f494c6714 100644 --- a/Gems/PhysX/Code/Source/BallJointComponent.cpp +++ b/Gems/PhysX/Code/Source/BallJointComponent.cpp @@ -16,8 +16,10 @@ #include #include #include +#include #include #include +#include #include @@ -33,15 +35,17 @@ namespace PhysX } } - BallJointComponent::BallJointComponent(const GenericJointConfiguration& config - , const GenericJointLimitsConfiguration& swingLimit) - : JointComponent(config, swingLimit) + BallJointComponent::BallJointComponent( + const JointComponentConfiguration& configuration, + const JointGenericProperties& genericProperties, + const JointLimitProperties& limitProperties) + : JointComponent(configuration, genericProperties, limitProperties) { } void BallJointComponent::InitNativeJoint() { - if (m_joint) + if (m_jointHandle != AzPhysics::InvalidJointHandle) { return; } @@ -52,50 +56,24 @@ namespace PhysX { return; } - PHYSX_SCENE_READ_LOCK(leadFollowerInfo.m_followerActor->getScene()); - m_joint = AZStd::make_shared(physx::PxSphericalJointCreate( - PxGetPhysics(), - leadFollowerInfo.m_leadActor, - leadFollowerInfo.m_leadLocal, - leadFollowerInfo.m_followerActor, - leadFollowerInfo.m_followerLocal), - leadFollowerInfo.m_leadBody, - leadFollowerInfo.m_followerBody); - InitSwingLimits(); - } - - void BallJointComponent::InitSwingLimits() - { - if (!m_joint) - { - return; - } - - physx::PxSphericalJoint* ballJointNative = static_cast(m_joint->GetNativePointer()); - if (!ballJointNative) - { - return; - } + BallJointConfiguration configuration; + configuration.m_parentLocalPosition = leadFollowerInfo.m_leadLocal.GetTranslation(); + configuration.m_parentLocalRotation = leadFollowerInfo.m_leadLocal.GetRotation(); + configuration.m_childLocalPosition = leadFollowerInfo.m_followerLocal.GetTranslation(); + configuration.m_childLocalRotation = leadFollowerInfo.m_followerLocal.GetRotation(); - if (!m_limits.m_isLimited) - { - ballJointNative->setSphericalJointFlag(physx::PxSphericalJointFlag::eLIMIT_ENABLED, false); - return; - } + configuration.m_genericProperties = m_genericProperties; + configuration.m_limitProperties = m_limits; - // Hard limit uses a tolerance value (distance to limit at which limit becomes active). - // Soft limit allows angle to exceed limit but springs back with configurable spring stiffness and damping. - physx::PxJointLimitCone swingLimit(AZ::DegToRad(m_limits.m_limitFirst) - , AZ::DegToRad(m_limits.m_limitSecond) - , m_limits.m_tolerance); - if (m_limits.m_isSoftLimit) + if (auto* sceneInterface = AZ::Interface::Get()) { - swingLimit.stiffness = m_limits.m_stiffness; - swingLimit.damping = m_limits.m_damping; + m_jointHandle = sceneInterface->AddJoint( + leadFollowerInfo.m_followerBody->m_sceneOwner, + &configuration, + leadFollowerInfo.m_leadBody->m_bodyHandle, + leadFollowerInfo.m_followerBody->m_bodyHandle); + m_jointSceneOwner = leadFollowerInfo.m_followerBody->m_sceneOwner; } - - ballJointNative->setLimitCone(swingLimit); - ballJointNative->setSphericalJointFlag(physx::PxSphericalJointFlag::eLIMIT_ENABLED, true); } } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/BallJointComponent.h b/Gems/PhysX/Code/Source/BallJointComponent.h index 4ac489a5fb..9741193e81 100644 --- a/Gems/PhysX/Code/Source/BallJointComponent.h +++ b/Gems/PhysX/Code/Source/BallJointComponent.h @@ -25,15 +25,14 @@ namespace PhysX static void Reflect(AZ::ReflectContext* context); BallJointComponent() = default; - BallJointComponent(const GenericJointConfiguration& config - , const GenericJointLimitsConfiguration& swingLimit); + BallJointComponent( + const JointComponentConfiguration& configuration, + const JointGenericProperties& genericProperties, + const JointLimitProperties& limitProperties); ~BallJointComponent() = default; protected: // JointComponent void InitNativeJoint() override; - - private: - void InitSwingLimits(); }; } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp b/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp index deb1d0d231..37203330b2 100644 --- a/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp @@ -99,6 +99,7 @@ namespace PhysX m_config.m_followerEntity = GetEntityId(); // joint is always in the same entity as the follower body. gameEntity->CreateComponent( m_config.ToGameTimeConfig(), + m_config.ToGenericProperties(), m_swingLimit.ToGameTimeConfig()); } diff --git a/Gems/PhysX/Code/Source/EditorBallJointComponent.h b/Gems/PhysX/Code/Source/EditorBallJointComponent.h index 44f96b3fa8..e06c026a97 100644 --- a/Gems/PhysX/Code/Source/EditorBallJointComponent.h +++ b/Gems/PhysX/Code/Source/EditorBallJointComponent.h @@ -13,7 +13,6 @@ #pragma once -#include #include #include #include diff --git a/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp b/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp index 1645d8c932..8af78b005f 100644 --- a/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp @@ -94,6 +94,6 @@ namespace PhysX void EditorFixedJointComponent::BuildGameEntity(AZ::Entity* gameEntity) { m_config.m_followerEntity = GetEntityId(); // joint is always in the same entity as the follower body. - gameEntity->CreateComponent(m_config.ToGameTimeConfig()); + gameEntity->CreateComponent(m_config.ToGameTimeConfig(), m_config.ToGenericProperties()); } } diff --git a/Gems/PhysX/Code/Source/EditorFixedJointComponent.h b/Gems/PhysX/Code/Source/EditorFixedJointComponent.h index 8642f83472..b9275186e9 100644 --- a/Gems/PhysX/Code/Source/EditorFixedJointComponent.h +++ b/Gems/PhysX/Code/Source/EditorFixedJointComponent.h @@ -13,7 +13,6 @@ #pragma once -#include #include #include #include diff --git a/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp b/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp index 6d136e898b..3f263c11d4 100644 --- a/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp @@ -99,6 +99,7 @@ namespace PhysX m_config.m_followerEntity = GetEntityId(); // joint is always in the same entity as the follower body. gameEntity->CreateComponent( m_config.ToGameTimeConfig(), + m_config.ToGenericProperties(), m_angularLimit.ToGameTimeConfig()); } diff --git a/Gems/PhysX/Code/Source/EditorHingeJointComponent.h b/Gems/PhysX/Code/Source/EditorHingeJointComponent.h index ef555d145e..abb6baf3ea 100644 --- a/Gems/PhysX/Code/Source/EditorHingeJointComponent.h +++ b/Gems/PhysX/Code/Source/EditorHingeJointComponent.h @@ -13,7 +13,6 @@ #pragma once -#include #include #include #include diff --git a/Gems/PhysX/Code/Source/EditorJointComponent.h b/Gems/PhysX/Code/Source/EditorJointComponent.h index 10f2a6fc72..9082f6bc33 100644 --- a/Gems/PhysX/Code/Source/EditorJointComponent.h +++ b/Gems/PhysX/Code/Source/EditorJointComponent.h @@ -13,7 +13,6 @@ #pragma once -#include #include #include diff --git a/Gems/PhysX/Code/Source/FixedJointComponent.cpp b/Gems/PhysX/Code/Source/FixedJointComponent.cpp index 8170477aeb..13a42ffbac 100644 --- a/Gems/PhysX/Code/Source/FixedJointComponent.cpp +++ b/Gems/PhysX/Code/Source/FixedJointComponent.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include @@ -33,20 +36,24 @@ namespace PhysX } } - FixedJointComponent::FixedJointComponent(const GenericJointConfiguration& config) - : JointComponent(config) + FixedJointComponent::FixedJointComponent( + const JointComponentConfiguration& configuration, + const JointGenericProperties& genericProperties) + : JointComponent(configuration, genericProperties) { } - FixedJointComponent::FixedJointComponent(const GenericJointConfiguration& config, - const GenericJointLimitsConfiguration& limitConfig) - : JointComponent(config, limitConfig) + FixedJointComponent::FixedJointComponent( + const JointComponentConfiguration& configuration, + const JointGenericProperties& genericProperties, + const JointLimitProperties& limitProperties) + : JointComponent(configuration, genericProperties, limitProperties) { } void FixedJointComponent::InitNativeJoint() { - if (m_joint) + if (m_jointHandle != AzPhysics::InvalidJointHandle) { return; } @@ -57,14 +64,23 @@ namespace PhysX { return; } - PHYSX_SCENE_READ_LOCK(leadFollowerInfo.m_followerActor->getScene()); - m_joint = AZStd::make_shared(physx::PxFixedJointCreate( - PxGetPhysics(), - leadFollowerInfo.m_leadActor, - leadFollowerInfo.m_leadLocal, - leadFollowerInfo.m_followerActor, - leadFollowerInfo.m_followerLocal), - leadFollowerInfo.m_leadBody, - leadFollowerInfo.m_followerBody); + + FixedJointConfiguration configuration; + configuration.m_parentLocalPosition = leadFollowerInfo.m_leadLocal.GetTranslation(); + configuration.m_parentLocalRotation = leadFollowerInfo.m_leadLocal.GetRotation(); + configuration.m_childLocalPosition = leadFollowerInfo.m_followerLocal.GetTranslation(); + configuration.m_childLocalRotation = leadFollowerInfo.m_followerLocal.GetRotation(); + + configuration.m_genericProperties = m_genericProperties; + + if (auto* sceneInterface = AZ::Interface::Get()) + { + m_jointHandle = sceneInterface->AddJoint( + leadFollowerInfo.m_followerBody->m_sceneOwner, + &configuration, + leadFollowerInfo.m_leadBody->m_bodyHandle, + leadFollowerInfo.m_followerBody->m_bodyHandle); + m_jointSceneOwner = leadFollowerInfo.m_followerBody->m_sceneOwner; + } } } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/FixedJointComponent.h b/Gems/PhysX/Code/Source/FixedJointComponent.h index 0f64eea2bb..1fd7d4d999 100644 --- a/Gems/PhysX/Code/Source/FixedJointComponent.h +++ b/Gems/PhysX/Code/Source/FixedJointComponent.h @@ -25,9 +25,13 @@ namespace PhysX static void Reflect(AZ::ReflectContext* context); FixedJointComponent() = default; - explicit FixedJointComponent(const GenericJointConfiguration& config); - FixedJointComponent(const GenericJointConfiguration& config, - const GenericJointLimitsConfiguration& limitConfig); + FixedJointComponent( + const JointComponentConfiguration& configuration, + const JointGenericProperties& genericProperties); + FixedJointComponent( + const JointComponentConfiguration& configuration, + const JointGenericProperties& genericProperties, + const JointLimitProperties& limitProperties); ~FixedJointComponent() = default; protected: diff --git a/Gems/PhysX/Code/Source/HingeJointComponent.cpp b/Gems/PhysX/Code/Source/HingeJointComponent.cpp index 7b0702559c..41d948bd5d 100644 --- a/Gems/PhysX/Code/Source/HingeJointComponent.cpp +++ b/Gems/PhysX/Code/Source/HingeJointComponent.cpp @@ -16,8 +16,10 @@ #include #include #include +#include #include #include +#include #include @@ -34,67 +36,45 @@ namespace PhysX } } - HingeJointComponent::HingeJointComponent(const GenericJointConfiguration& config - , const GenericJointLimitsConfiguration& angularLimitConfig) - : JointComponent(config, angularLimitConfig) + HingeJointComponent::HingeJointComponent( + const JointComponentConfiguration& configuration, + const JointGenericProperties& genericProperties, + const JointLimitProperties& limitProperties) + : JointComponent(configuration, genericProperties, limitProperties) { } void HingeJointComponent::InitNativeJoint() { - if (m_joint) + if (m_jointHandle != AzPhysics::InvalidJointHandle) { return; } JointComponent::LeadFollowerInfo leadFollowerInfo; ObtainLeadFollowerInfo(leadFollowerInfo); - if (!leadFollowerInfo.m_followerActor) + if (leadFollowerInfo.m_followerActor == nullptr || + leadFollowerInfo.m_leadBody == nullptr || + leadFollowerInfo.m_followerBody == nullptr) { return; } - PHYSX_SCENE_READ_LOCK(leadFollowerInfo.m_followerActor->getScene()); - m_joint = AZStd::make_shared(physx::PxRevoluteJointCreate( - PxGetPhysics(), - leadFollowerInfo.m_leadActor, - leadFollowerInfo.m_leadLocal, - leadFollowerInfo.m_followerActor, - leadFollowerInfo.m_followerLocal), - leadFollowerInfo.m_leadBody, - leadFollowerInfo.m_followerBody); - InitAngularLimits(); - } - - void HingeJointComponent::InitAngularLimits() - { - if (!m_joint) - { - return; - } - - physx::PxRevoluteJoint* revoluteJointNative = static_cast(m_joint->GetNativePointer()); - if (!revoluteJointNative) - { - return; - } + HingeJointConfiguration configuration; + configuration.m_parentLocalPosition = leadFollowerInfo.m_leadLocal.GetTranslation(); + configuration.m_parentLocalRotation = leadFollowerInfo.m_leadLocal.GetRotation(); + configuration.m_childLocalPosition = leadFollowerInfo.m_followerLocal.GetTranslation(); + configuration.m_childLocalRotation = leadFollowerInfo.m_followerLocal.GetRotation(); - if (!m_limits.m_isLimited) - { - revoluteJointNative->setRevoluteJointFlag(physx::PxRevoluteJointFlag::eLIMIT_ENABLED, false); - return; - } + configuration.m_genericProperties = m_genericProperties; + configuration.m_limitProperties = m_limits; - physx::PxJointAngularLimitPair limitPair(AZ::DegToRad(m_limits.m_limitSecond) - , AZ::DegToRad(m_limits.m_limitFirst) - , m_limits.m_tolerance); - if (m_limits.m_isSoftLimit) + if (auto* sceneInterface = AZ::Interface::Get()) { - limitPair.stiffness = m_limits.m_stiffness; - limitPair.damping = m_limits.m_damping; + m_jointHandle = sceneInterface->AddJoint( + leadFollowerInfo.m_followerBody->m_sceneOwner, &configuration, leadFollowerInfo.m_leadBody->m_bodyHandle, + leadFollowerInfo.m_followerBody->m_bodyHandle); + m_jointSceneOwner = leadFollowerInfo.m_followerBody->m_sceneOwner; } - - revoluteJointNative->setLimit(limitPair); - revoluteJointNative->setRevoluteJointFlag(physx::PxRevoluteJointFlag::eLIMIT_ENABLED, true); } } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/HingeJointComponent.h b/Gems/PhysX/Code/Source/HingeJointComponent.h index 357fe61b1a..8bc11a0051 100644 --- a/Gems/PhysX/Code/Source/HingeJointComponent.h +++ b/Gems/PhysX/Code/Source/HingeJointComponent.h @@ -25,15 +25,14 @@ namespace PhysX static void Reflect(AZ::ReflectContext* context); HingeJointComponent() = default; - explicit HingeJointComponent(const GenericJointConfiguration& config - , const GenericJointLimitsConfiguration& angularLimit); + HingeJointComponent( + const JointComponentConfiguration& configuration, + const JointGenericProperties& genericProperties, + const JointLimitProperties& limitProperties); ~HingeJointComponent() = default; protected: // JointComponent void InitNativeJoint() override; - - private: - void InitAngularLimits(); }; } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/Joint.cpp b/Gems/PhysX/Code/Source/Joint.cpp deleted file mode 100644 index 80575607c4..0000000000 --- a/Gems/PhysX/Code/Source/Joint.cpp +++ /dev/null @@ -1,646 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include -#include -#include -#include -#include -#include -#include - -namespace PhysX -{ - namespace JointConstants - { - // Setting swing limits to very small values can cause extreme stability problems, so clamp above a small - // threshold. - static const float MinSwingLimitDegrees = 1.0f; - } // namespace JointConstants - - AzPhysics::SimulatedBody* Joint::GetParentBody() const - { - return m_parentBody; - } - - AzPhysics::SimulatedBody* Joint::GetChildBody() const - { - return m_childBody; - } - - bool IsAtLeastOneDynamic(AzPhysics::SimulatedBody* body0, - AzPhysics::SimulatedBody* body1) - { - for (const AzPhysics::SimulatedBody* body : { body0, body1 }) - { - if (body) - { - if (body->GetNativeType() == NativeTypeIdentifiers::RigidBody || - body->GetNativeType() == NativeTypeIdentifiers::ArticulationLink) - { - return true; - } - } - } - return false; - } - - physx::PxRigidActor* GetPxRigidActor(AzPhysics::SimulatedBody* worldBody) - { - if (worldBody && static_cast(worldBody->GetNativePointer())->is()) - { - return static_cast(worldBody->GetNativePointer()); - } - - return nullptr; - } - - void releasePxJoint(physx::PxJoint* joint) - { - PHYSX_SCENE_WRITE_LOCK(joint->getScene()); - joint->userData = nullptr; - joint->release(); - } - - Joint::Joint(physx::PxJoint* pxJoint, AzPhysics::SimulatedBody* parentBody, - AzPhysics::SimulatedBody* childBody) - : m_parentBody(parentBody) - , m_childBody(childBody) - { - m_pxJoint = PxJointUniquePtr(pxJoint, releasePxJoint); - } - - bool Joint::SetPxActors() - { - physx::PxRigidActor* parentActor = GetPxRigidActor(m_parentBody); - physx::PxRigidActor* childActor = GetPxRigidActor(m_childBody); - if (!parentActor && !childActor) - { - AZ_Error("PhysX Joint", false, "Invalid PhysX actors in joint - at least one must be a PxRigidActor."); - return false; - } - - m_pxJoint->setActors(parentActor, childActor); - return true; - } - - void Joint::SetParentBody(AzPhysics::SimulatedBody* parentBody) - { - if (IsAtLeastOneDynamic(parentBody, m_childBody)) - { - m_parentBody = parentBody; - SetPxActors(); - } - else - { - AZ_Warning("PhysX Joint", false, "Call to SetParentBody would result in invalid joint - at least one " - "body in a joint must be dynamic."); - } - } - - void Joint::SetChildBody(AzPhysics::SimulatedBody* childBody) - { - if (IsAtLeastOneDynamic(m_parentBody, childBody)) - { - m_childBody = childBody; - SetPxActors(); - } - else - { - AZ_Warning("PhysX Joint", false, "Call to SetChildBody would result in invalid joint - at least one " - "body in a joint must be dynamic."); - } - } - - const AZStd::string& Joint::GetName() const - { - return m_name; - } - - void Joint::SetName(const AZStd::string& name) - { - m_name = name; - } - - void* Joint::GetNativePointer() - { - return m_pxJoint.get(); - } - - const AZ::Crc32 D6Joint::GetNativeType() const - { - return NativeTypeIdentifiers::D6Joint; - } - - void D6Joint::GenerateJointLimitVisualizationData( - float scale, - AZ::u32 angularSubdivisions, - AZ::u32 radialSubdivisions, - [[maybe_unused]] AZStd::vector& vertexBufferOut, - [[maybe_unused]] AZStd::vector& indexBufferOut, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut) - { - const AZ::u32 angularSubdivisionsClamped = AZ::GetClamp(angularSubdivisions, 4u, 32u); - const AZ::u32 radialSubdivisionsClamped = AZ::GetClamp(radialSubdivisions, 1u, 4u); - - const physx::PxD6Joint* joint = static_cast(m_pxJoint.get()); - const AZ::Quaternion parentLocalRotation = PxMathConvert(joint->getLocalPose(physx::PxJointActorIndex::eACTOR0).q); - const AZ::Quaternion parentWorldRotation = m_parentBody ? m_parentBody->GetOrientation() : AZ::Quaternion::CreateIdentity(); - const AZ::Quaternion childLocalRotation = PxMathConvert(joint->getLocalPose(physx::PxJointActorIndex::eACTOR1).q); - const AZ::Quaternion childWorldRotation = m_childBody ? m_childBody->GetOrientation() : AZ::Quaternion::CreateIdentity(); - - const float swingAngleY = joint->getSwingYAngle(); - const float swingAngleZ = joint->getSwingZAngle(); - const float swingLimitY = joint->getSwingLimit().yAngle; - const float swingLimitZ = joint->getSwingLimit().zAngle; - const float twistAngle = joint->getTwist(); - const float twistLimitLower = joint->getTwistLimit().lower; - const float twistLimitUpper = joint->getTwistLimit().upper; - - JointUtils::AppendD6SwingConeToLineBuffer(parentLocalRotation, swingAngleY, swingAngleZ, swingLimitY, swingLimitZ, - scale, angularSubdivisionsClamped, radialSubdivisionsClamped, lineBufferOut, lineValidityBufferOut); - JointUtils::AppendD6TwistArcToLineBuffer(parentLocalRotation, twistAngle, twistLimitLower, twistLimitUpper, - scale, angularSubdivisionsClamped, radialSubdivisionsClamped, lineBufferOut, lineValidityBufferOut); - JointUtils::AppendD6CurrentTwistToLineBuffer(parentLocalRotation, twistAngle, twistLimitLower, twistLimitUpper, - scale, lineBufferOut, lineValidityBufferOut); - - // draw the X-axis of the child joint frame - // make the axis slightly longer than the radius of the twist arc so that it is easy to see - float axisLength = 1.25f * scale; - AZ::Vector3 childAxis = (parentWorldRotation.GetConjugate() * childWorldRotation * childLocalRotation).TransformVector( - AZ::Vector3::CreateAxisX(axisLength)); - lineBufferOut.push_back(AZ::Vector3::CreateZero()); - lineBufferOut.push_back(childAxis); - } - - const AZ::Crc32 FixedJoint::GetNativeType() const - { - return NativeTypeIdentifiers::FixedJoint; - } - - const AZ::Crc32 HingeJoint::GetNativeType() const - { - return NativeTypeIdentifiers::HingeJoint; - } - - const AZ::Crc32 BallJoint::GetNativeType() const - { - return NativeTypeIdentifiers::BallJoint; - } - - void GenericJointConfiguration::Reflect(AZ::ReflectContext* context) - { - if (auto* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(2, &VersionConverter) - ->Field("Follower Local Transform", &GenericJointConfiguration::m_localTransformFromFollower) - ->Field("Maximum Force", &GenericJointConfiguration::m_forceMax) - ->Field("Maximum Torque", &GenericJointConfiguration::m_torqueMax) - ->Field("Lead Entity", &GenericJointConfiguration::m_leadEntity) - ->Field("Follower Entity", &GenericJointConfiguration::m_followerEntity) - ->Field("Flags", &GenericJointConfiguration::m_flags) - ; - } - } - - bool GenericJointConfiguration::VersionConverter( - AZ::SerializeContext& context, - AZ::SerializeContext::DataElementNode& classElement) - { - if (classElement.GetVersion() <= 1) - { - // Convert bool breakable to GenericJointConfiguration::GenericJointFlag - const int breakableElementIndex = classElement.FindElement(AZ_CRC("Breakable", 0xb274ecd4)); - if (breakableElementIndex >= 0) - { - bool breakable = false; - AZ::SerializeContext::DataElementNode& breakableNode = classElement.GetSubElement(breakableElementIndex); - breakableNode.GetData(breakable); - if (!breakableNode.GetData(breakable)) - { - return false; - } - classElement.RemoveElement(breakableElementIndex); - GenericJointConfiguration::GenericJointFlag flags = breakable ? GenericJointConfiguration::GenericJointFlag::Breakable : GenericJointConfiguration::GenericJointFlag::None; - classElement.AddElementWithData(context, "Flags", flags); - } - } - - return true; - } - - GenericJointConfiguration::GenericJointConfiguration(float forceMax, - float torqueMax, - AZ::Transform localTransformFromFollower, - AZ::EntityId leadEntity, - AZ::EntityId followerEntity, - GenericJointFlag flags) - : m_forceMax(forceMax) - , m_torqueMax(torqueMax) - , m_localTransformFromFollower(localTransformFromFollower) - , m_leadEntity(leadEntity) - , m_followerEntity(followerEntity) - , m_flags(flags) - { - } - - bool GenericJointConfiguration::GetFlag(GenericJointFlag flag) - { - return static_cast(m_flags & flag); - } - - void GenericJointLimitsConfiguration::Reflect(AZ::ReflectContext* context) - { - if (auto* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(1) - ->Field("First Limit", &GenericJointLimitsConfiguration::m_limitFirst) - ->Field("Second Limit", &GenericJointLimitsConfiguration::m_limitSecond) - ->Field("Tolerance", &GenericJointLimitsConfiguration::m_tolerance) - ->Field("Is Limited", &GenericJointLimitsConfiguration::m_isLimited) - ->Field("Is Soft Limit", &GenericJointLimitsConfiguration::m_isSoftLimit) - ->Field("Damping", &GenericJointLimitsConfiguration::m_damping) - ->Field("Spring", &GenericJointLimitsConfiguration::m_stiffness) - ; - } - } - - GenericJointLimitsConfiguration::GenericJointLimitsConfiguration(float damping - , bool isLimited - , bool isSoftLimit - , float limitFirst - , float limitSecond - , float stiffness - , float tolerance) - : m_damping(damping) - , m_isLimited(isLimited) - , m_isSoftLimit(isSoftLimit) - , m_limitFirst(limitFirst) - , m_limitSecond(limitSecond) - , m_stiffness(stiffness) - , m_tolerance(tolerance) - { - } - - AZStd::vector JointUtils::GetSupportedJointTypes() - { - return AZStd::vector - { - D6JointLimitConfiguration::RTTI_Type() - }; - } - - AZStd::shared_ptr JointUtils::CreateJointLimitConfiguration([[maybe_unused]] AZ::TypeId jointType) - { - return AZStd::make_shared(); - } - - AZStd::shared_ptr JointUtils::CreateJoint(const AZStd::shared_ptr& configuration, - AzPhysics::SimulatedBody* parentBody, AzPhysics::SimulatedBody* childBody) - { - if (!configuration) - { - AZ_Warning("PhysX Joint", false, "CreateJoint failed - configuration was nullptr."); - return nullptr; - } - - if (auto d6Config = AZStd::rtti_pointer_cast(configuration)) - { - if (!IsAtLeastOneDynamic(parentBody, childBody)) - { - AZ_Warning("PhysX Joint", false, "CreateJoint failed - at least one body must be dynamic."); - return nullptr; - } - - physx::PxRigidActor* parentActor = GetPxRigidActor(parentBody); - physx::PxRigidActor* childActor = GetPxRigidActor(childBody); - - if (!parentActor && !childActor) - { - AZ_Warning("PhysX Joint", false, "CreateJoint failed - at least one body must be a PxRigidActor."); - return nullptr; - } - - const physx::PxTransform parentWorldTransform = parentActor ? parentActor->getGlobalPose() : physx::PxTransform(physx::PxIdentity); - const physx::PxTransform childWorldTransform = childActor ? childActor->getGlobalPose() : physx::PxTransform(physx::PxIdentity); - const physx::PxVec3 childOffset = childWorldTransform.p - parentWorldTransform.p; - physx::PxTransform parentLocalTransform(PxMathConvert(d6Config->m_parentLocalRotation).getNormalized()); - const physx::PxTransform childLocalTransform(PxMathConvert(d6Config->m_childLocalRotation).getNormalized()); - parentLocalTransform.p = parentWorldTransform.q.rotateInv(childOffset); - - physx::PxD6Joint* joint = PxD6JointCreate(PxGetPhysics(), parentActor, parentLocalTransform, - childActor, childLocalTransform); - - joint->setMotion(physx::PxD6Axis::eTWIST, physx::PxD6Motion::eLIMITED); - joint->setMotion(physx::PxD6Axis::eSWING1, physx::PxD6Motion::eLIMITED); - joint->setMotion(physx::PxD6Axis::eSWING2, physx::PxD6Motion::eLIMITED); - - AZ_Warning("PhysX Joint", - d6Config->m_swingLimitY >= JointConstants::MinSwingLimitDegrees && d6Config->m_swingLimitZ >= JointConstants::MinSwingLimitDegrees, - "Very small swing limit requested for joint between \"%s\" and \"%s\", increasing to %f degrees to improve stability", - parentActor ? parentActor->getName() : "world", childActor ? childActor->getName() : "world", - JointConstants::MinSwingLimitDegrees); - float swingLimitY = AZ::DegToRad(AZ::GetMax(JointConstants::MinSwingLimitDegrees, d6Config->m_swingLimitY)); - float swingLimitZ = AZ::DegToRad(AZ::GetMax(JointConstants::MinSwingLimitDegrees, d6Config->m_swingLimitZ)); - physx::PxJointLimitCone limitCone(swingLimitY, swingLimitZ); - joint->setSwingLimit(limitCone); - - float twistLower = AZ::DegToRad(AZStd::GetMin(d6Config->m_twistLimitLower, d6Config->m_twistLimitUpper)); - float twistUpper = AZ::DegToRad(AZStd::GetMax(d6Config->m_twistLimitLower, d6Config->m_twistLimitUpper)); - physx::PxJointAngularLimitPair twistLimitPair(twistLower, twistUpper); - joint->setTwistLimit(twistLimitPair); - - return AZStd::make_shared(joint, parentBody, childBody); - } - else - { - AZ_Warning("PhysX Joint", false, "Unrecognized joint limit configuration."); - return nullptr; - } - } - - D6JointState JointUtils::CalculateD6JointState( - const AZ::Quaternion& parentWorldRotation, - const AZ::Quaternion& parentLocalRotation, - const AZ::Quaternion& childWorldRotation, - const AZ::Quaternion& childLocalRotation) - { - D6JointState result; - - const AZ::Quaternion parentRotation = parentWorldRotation * parentLocalRotation; - const AZ::Quaternion childRotation = childWorldRotation * childLocalRotation; - const AZ::Quaternion relativeRotation = parentRotation.GetConjugate() * childRotation; - AZ::Quaternion twistQuat = AZ::IsClose(relativeRotation.GetX(), 0.0f, AZ::Constants::FloatEpsilon) - ? AZ::Quaternion::CreateIdentity() - : AZ::Quaternion(relativeRotation.GetX(), 0.0f, 0.0f, relativeRotation.GetW()).GetNormalized(); - AZ::Quaternion swingQuat = relativeRotation * twistQuat.GetConjugate(); - - // make sure the twist angle has the correct sign for the rotation - twistQuat *= AZ::GetSign(twistQuat.GetX()); - // make sure we get the shortest arcs for the swing degrees of freedom - swingQuat *= AZ::GetSign(swingQuat.GetW()); - // the PhysX swing limits work in terms of tan quarter angles - result.m_swingAngleY = 4.0f * atan2f(swingQuat.GetY(), 1.0f + swingQuat.GetW()); - result.m_swingAngleZ = 4.0f * atan2f(swingQuat.GetZ(), 1.0f + swingQuat.GetW()); - const float twistAngle = twistQuat.GetAngle(); - // GetAngle returns an angle in the range 0..2 pi, but the twist limits work in the range -pi..pi - const float wrappedTwistAngle = twistAngle > AZ::Constants::Pi ? twistAngle - AZ::Constants::TwoPi : twistAngle; - result.m_twistAngle = wrappedTwistAngle; - - return result; - } - - bool JointUtils::IsD6SwingValid( - float swingAngleY, - float swingAngleZ, - float swingLimitY, - float swingLimitZ) - { - const float epsilon = AZ::Constants::FloatEpsilon; - const float yFactor = tanf(0.25f * swingAngleY) / AZStd::GetMax(epsilon, tanf(0.25f * swingLimitY)); - const float zFactor = tanf(0.25f * swingAngleZ) / AZStd::GetMax(epsilon, tanf(0.25f * swingLimitZ)); - - return (yFactor * yFactor + zFactor * zFactor <= 1.0f + epsilon); - } - - void JointUtils::AppendD6SwingConeToLineBuffer( - const AZ::Quaternion& parentLocalRotation, - float swingAngleY, - float swingAngleZ, - float swingLimitY, - float swingLimitZ, - float scale, - AZ::u32 angularSubdivisions, - AZ::u32 radialSubdivisions, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut) - { - const AZ::u32 numLinesSwingCone = angularSubdivisions * (1u + radialSubdivisions); - lineBufferOut.reserve(lineBufferOut.size() + 2u * numLinesSwingCone); - lineValidityBufferOut.reserve(lineValidityBufferOut.size() + numLinesSwingCone); - - // the orientation quat for a radial line in the cone can be represented in terms of sin and cos half angles - // these expressions can be efficiently calculated using tan quarter angles as follows: - // writing t = tan(x / 4) - // sin(x / 2) = 2 * t / (1 + t * t) - // cos(x / 2) = (1 - t * t) / (1 + t * t) - const float tanQuarterSwingZ = tanf(0.25f * swingLimitZ); - const float tanQuarterSwingY = tanf(0.25f * swingLimitY); - - AZ::Vector3 previousRadialVector = AZ::Vector3::CreateZero(); - for (AZ::u32 angularIndex = 0; angularIndex <= angularSubdivisions; angularIndex++) - { - const float angle = AZ::Constants::TwoPi / angularSubdivisions * angularIndex; - // the axis about which to rotate the x-axis to get the radial vector for this segment of the cone - const AZ::Vector3 rotationAxis(0, -tanQuarterSwingY * sinf(angle), tanQuarterSwingZ * cosf(angle)); - const float normalizationFactor = rotationAxis.GetLengthSq(); - const AZ::Quaternion radialVectorRotation = 1.0f / (1.0f + normalizationFactor) * - AZ::Quaternion::CreateFromVector3AndValue(2.0f * rotationAxis, 1.0f - normalizationFactor); - const AZ::Vector3 radialVector = (parentLocalRotation * radialVectorRotation).TransformVector(AZ::Vector3::CreateAxisX(scale)); - - if (angularIndex > 0) - { - for (AZ::u32 radialIndex = 1; radialIndex <= radialSubdivisions; radialIndex++) - { - float radiusFraction = 1.0f / radialSubdivisions * radialIndex; - lineBufferOut.push_back(radiusFraction * radialVector); - lineBufferOut.push_back(radiusFraction * previousRadialVector); - } - } - - if (angularIndex < angularSubdivisions) - { - lineBufferOut.push_back(AZ::Vector3::CreateZero()); - lineBufferOut.push_back(radialVector); - } - - previousRadialVector = radialVector; - } - - const bool swingValid = IsD6SwingValid(swingAngleY, swingAngleZ, swingLimitY, swingLimitZ); - lineValidityBufferOut.insert(lineValidityBufferOut.end(), numLinesSwingCone, swingValid); - } - - void JointUtils::AppendD6TwistArcToLineBuffer( - const AZ::Quaternion& parentLocalRotation, - float twistAngle, - float twistLimitLower, - float twistLimitUpper, - float scale, - AZ::u32 angularSubdivisions, - AZ::u32 radialSubdivisions, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut) - { - const AZ::u32 numLinesTwistArc = angularSubdivisions * (1u + radialSubdivisions) + 1u; - lineBufferOut.reserve(lineBufferOut.size() + 2u * numLinesTwistArc); - - AZ::Vector3 previousRadialVector = AZ::Vector3::CreateZero(); - const float twistRange = twistLimitUpper - twistLimitLower; - - for (AZ::u32 angularIndex = 0; angularIndex <= angularSubdivisions; angularIndex++) - { - const float angle = twistLimitLower + twistRange / angularSubdivisions * angularIndex; - const AZ::Vector3 radialVector = parentLocalRotation.TransformVector(scale * AZ::Vector3(0.0f, cosf(angle), sinf(angle))); - - if (angularIndex > 0) - { - for (AZ::u32 radialIndex = 1; radialIndex <= radialSubdivisions; radialIndex++) - { - const float radiusFraction = 1.0f / radialSubdivisions * radialIndex; - lineBufferOut.push_back(radiusFraction * radialVector); - lineBufferOut.push_back(radiusFraction * previousRadialVector); - } - } - - lineBufferOut.push_back(AZ::Vector3::CreateZero()); - lineBufferOut.push_back(radialVector); - - previousRadialVector = radialVector; - } - - const bool twistValid = (twistAngle >= twistLimitLower && twistAngle <= twistLimitUpper); - lineValidityBufferOut.insert(lineValidityBufferOut.end(), numLinesTwistArc, twistValid); - } - - void JointUtils::AppendD6CurrentTwistToLineBuffer( - const AZ::Quaternion& parentLocalRotation, - float twistAngle, - [[maybe_unused]] float twistLimitLower, - [[maybe_unused]] float twistLimitUpper, - float scale, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut - ) - { - const AZ::Vector3 twistVector = parentLocalRotation.TransformVector(1.25f * scale * AZ::Vector3(0.0f, cosf(twistAngle), sinf(twistAngle))); - lineBufferOut.push_back(AZ::Vector3::CreateZero()); - lineBufferOut.push_back(twistVector); - lineValidityBufferOut.push_back(true); - } - - void JointUtils::GenerateJointLimitVisualizationData( - const Physics::JointLimitConfiguration& configuration, - const AZ::Quaternion& parentRotation, - const AZ::Quaternion& childRotation, - float scale, - AZ::u32 angularSubdivisions, - AZ::u32 radialSubdivisions, - [[maybe_unused]] AZStd::vector& vertexBufferOut, - [[maybe_unused]] AZStd::vector& indexBufferOut, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut) - { - if (const auto d6JointConfiguration = azrtti_cast(&configuration)) - { - const AZ::u32 angularSubdivisionsClamped = AZ::GetClamp(angularSubdivisions, 4u, 32u); - const AZ::u32 radialSubdivisionsClamped = AZ::GetClamp(radialSubdivisions, 1u, 4u); - - const D6JointState jointState = CalculateD6JointState(parentRotation, d6JointConfiguration->m_parentLocalRotation, - childRotation, d6JointConfiguration->m_childLocalRotation); - const float swingAngleY = jointState.m_swingAngleY; - const float swingAngleZ = jointState.m_swingAngleZ; - const float twistAngle = jointState.m_twistAngle; - const float swingLimitY = AZ::DegToRad(d6JointConfiguration->m_swingLimitY); - const float swingLimitZ = AZ::DegToRad(d6JointConfiguration->m_swingLimitZ); - const float twistLimitLower = AZ::DegToRad(d6JointConfiguration->m_twistLimitLower); - const float twistLimitUpper = AZ::DegToRad(d6JointConfiguration->m_twistLimitUpper); - - AppendD6SwingConeToLineBuffer(d6JointConfiguration->m_parentLocalRotation, swingAngleY, swingAngleZ, swingLimitY, - swingLimitZ, scale, angularSubdivisionsClamped, radialSubdivisionsClamped, lineBufferOut, lineValidityBufferOut); - AppendD6TwistArcToLineBuffer(d6JointConfiguration->m_parentLocalRotation, twistAngle, twistLimitLower, twistLimitUpper, - scale, angularSubdivisionsClamped, radialSubdivisionsClamped, lineBufferOut, lineValidityBufferOut); - AppendD6CurrentTwistToLineBuffer(d6JointConfiguration->m_parentLocalRotation, twistAngle, twistLimitLower, - twistLimitUpper, scale, lineBufferOut, lineValidityBufferOut); - } - } - - AZStd::unique_ptr JointUtils::ComputeInitialJointLimitConfiguration( - const AZ::TypeId& jointLimitTypeId, - const AZ::Quaternion& parentWorldRotation, - const AZ::Quaternion& childWorldRotation, - const AZ::Vector3& axis, - const AZStd::vector& exampleLocalRotations) - { - AZ_UNUSED(exampleLocalRotations); - - if (jointLimitTypeId == D6JointLimitConfiguration::RTTI_Type()) - { - const AZ::Vector3& normalizedAxis = axis.IsZero() - ? AZ::Vector3::CreateAxisX() - : axis.GetNormalized(); - - D6JointLimitConfiguration d6JointLimitConfig; - const AZ::Quaternion childLocalRotation = AZ::Quaternion::CreateShortestArc(AZ::Vector3::CreateAxisX(), - childWorldRotation.GetConjugate().TransformVector(normalizedAxis)); - d6JointLimitConfig.m_childLocalRotation = childLocalRotation; - d6JointLimitConfig.m_parentLocalRotation = parentWorldRotation.GetConjugate() * childWorldRotation * childLocalRotation; - - return AZStd::make_unique(d6JointLimitConfig); - } - - AZ_Warning("PhysX Joint Utils", false, "Unsupported joint type in ComputeInitialJointLimitConfiguration"); - return nullptr; - } - - const char* D6JointLimitConfiguration::GetTypeName() - { - return "D6 Joint"; - } - - void D6JointLimitConfiguration::Reflect(AZ::ReflectContext* context) - { - if (auto serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(1) - ->Field("SwingLimitY", &D6JointLimitConfiguration::m_swingLimitY) - ->Field("SwingLimitZ", &D6JointLimitConfiguration::m_swingLimitZ) - ->Field("TwistLowerLimit", &D6JointLimitConfiguration::m_twistLimitLower) - ->Field("TwistUpperLimit", &D6JointLimitConfiguration::m_twistLimitUpper) - ; - - AZ::EditContext* editContext = serializeContext->GetEditContext(); - if (editContext) - { - editContext->Class( - "PhysX D6 Joint Configuration", "") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_swingLimitY, "Swing limit Y", - "Maximum angle from the Y axis of the joint frame") - ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") - ->Attribute(AZ::Edit::Attributes::Min, JointConstants::MinSwingLimitDegrees) - ->Attribute(AZ::Edit::Attributes::Max, 180.0f) - ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_swingLimitZ, "Swing limit Z", - "Maximum angle from the Z axis of the joint frame") - ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") - ->Attribute(AZ::Edit::Attributes::Min, JointConstants::MinSwingLimitDegrees) - ->Attribute(AZ::Edit::Attributes::Max, 180.0f) - ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_twistLimitLower, "Twist lower limit", - "Lower limit for rotation about the X axis of the joint frame") - ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") - ->Attribute(AZ::Edit::Attributes::Min, -180.0f) - ->Attribute(AZ::Edit::Attributes::Max, 180.0f) - ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_twistLimitUpper, "Twist upper limit", - "Upper limit for rotation about the X axis of the joint frame") - ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") - ->Attribute(AZ::Edit::Attributes::Min, -180.0f) - ->Attribute(AZ::Edit::Attributes::Max, 180.0f) - ; - } - } - } -} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/Joint.h b/Gems/PhysX/Code/Source/Joint.h deleted file mode 100644 index 01214a0fec..0000000000 --- a/Gems/PhysX/Code/Source/Joint.h +++ /dev/null @@ -1,311 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#include - -namespace AzPhysics -{ - struct SimulatedBody; -} - -namespace PhysX -{ - class D6JointLimitConfiguration - : public Physics::JointLimitConfiguration - { - public: - AZ_CLASS_ALLOCATOR(D6JointLimitConfiguration, AZ::SystemAllocator, 0); - AZ_RTTI(D6JointLimitConfiguration, "{90C5C23D-16C0-4F23-AD50-A190E402388E}", Physics::JointLimitConfiguration); - static void Reflect(AZ::ReflectContext* context); - - const char* GetTypeName() override; - - float m_swingLimitY = 45.0f; ///< Maximum angle in degrees from the Y axis of the joint frame. - float m_swingLimitZ = 45.0f; ///< Maximum angle in degrees from the Z axis of the joint frame. - float m_twistLimitLower = -45.0f; ///< Lower limit in degrees for rotation about the X axis of the joint frame. - float m_twistLimitUpper = 45.0f; ///< Upper limit in degrees for rotation about the X axis of the joint frame. - }; - - class Joint - : public Physics::Joint - { - public: - AZ_CLASS_ALLOCATOR(Joint, AZ::SystemAllocator, 0); - AZ_RTTI(Joint, "{3C739E22-8EF0-419F-966B-C575A1F5A08B}", Physics::Joint); - - Joint(physx::PxJoint* pxJoint, AzPhysics::SimulatedBody* parentBody, - AzPhysics::SimulatedBody* childBody); - - virtual ~Joint() = default; - - AzPhysics::SimulatedBody* GetParentBody() const override; - AzPhysics::SimulatedBody* GetChildBody() const override; - void SetParentBody(AzPhysics::SimulatedBody* parentBody) override; - void SetChildBody(AzPhysics::SimulatedBody* childBody) override; - const AZStd::string& GetName() const override; - void SetName(const AZStd::string& name) override; - void* GetNativePointer() override; - - protected: - bool SetPxActors(); - - using PxJointUniquePtr = AZStd::unique_ptr>; - PxJointUniquePtr m_pxJoint; - AzPhysics::SimulatedBody* m_parentBody; - AzPhysics::SimulatedBody* m_childBody; - AZStd::string m_name; - }; - - class D6Joint - : public Joint - { - public: - AZ_CLASS_ALLOCATOR(D6Joint, AZ::SystemAllocator, 0); - AZ_RTTI(D6Joint, "{962C4044-2BD2-4E4C-913C-FB8E85A2A12A}", Joint); - - D6Joint(physx::PxJoint* pxJoint, AzPhysics::SimulatedBody* parentBody, - AzPhysics::SimulatedBody* childBody) - : Joint(pxJoint, parentBody, childBody) - { - } - virtual ~D6Joint() = default; - - const AZ::Crc32 GetNativeType() const override; - void GenerateJointLimitVisualizationData( - float scale, - AZ::u32 angularSubdivisions, - AZ::u32 radialSubdivisions, - AZStd::vector& vertexBufferOut, - AZStd::vector& indexBufferOut, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut) override; - }; - - struct D6JointState - { - float m_swingAngleY; - float m_swingAngleZ; - float m_twistAngle; - }; - - /// A fixed joint locks 2 bodies relative to one another on all axes of freedom. - class FixedJoint : public Joint - { - public: - AZ_CLASS_ALLOCATOR(FixedJoint, AZ::SystemAllocator, 0); - AZ_TYPE_INFO(FixedJoint, "{203FB99C-7DC5-478A-A52C-A1F2AAF61FB8}"); - - FixedJoint(physx::PxJoint* pxJoint, AzPhysics::SimulatedBody* parentBody, - AzPhysics::SimulatedBody* childBody) - : Joint(pxJoint, parentBody, childBody) - { - } - - const AZ::Crc32 GetNativeType() const override; - void GenerateJointLimitVisualizationData( - float /*scale*/, - AZ::u32 /*angularSubdivisions*/, - AZ::u32 /*radialSubdivisions*/, - AZStd::vector& /*vertexBufferOut*/, - AZStd::vector& /*indexBufferOut*/, - AZStd::vector& /*lineBufferOut*/, - AZStd::vector& /*lineValidityBufferOut*/) override {} - }; - - /// A hinge joint locks 2 bodies relative to one another except about the x-axis of the joint between them. - class HingeJoint : public Joint - { - public: - AZ_CLASS_ALLOCATOR(HingeJoint, AZ::SystemAllocator, 0); - AZ_TYPE_INFO(HingeJoint, "{8EFF1002-B08C-47CE-883C-82F0CF3736E0}"); - - HingeJoint(physx::PxJoint* pxJoint, AzPhysics::SimulatedBody* parentBody, - AzPhysics::SimulatedBody* childBody) - : Joint(pxJoint, parentBody, childBody) - { - } - - const AZ::Crc32 GetNativeType() const override; - void GenerateJointLimitVisualizationData( - float /*scale*/, - AZ::u32 /*angularSubdivisions*/, - AZ::u32 /*radialSubdivisions*/, - AZStd::vector& /*vertexBufferOut*/, - AZStd::vector& /*indexBufferOut*/, - AZStd::vector& /*lineBufferOut*/, - AZStd::vector& /*lineValidityBufferOut*/) override {} - }; - - /// A ball joint locks 2 bodies relative to one another except about the y and z axes of the joint between them. - class BallJoint : public Joint - { - public: - AZ_CLASS_ALLOCATOR(BallJoint, AZ::SystemAllocator, 0); - AZ_TYPE_INFO(BallJoint, "{9FADA1C2-0E2F-4E1B-9E83-6292A1606372}"); - - BallJoint(physx::PxJoint* pxJoint, AzPhysics::SimulatedBody* parentBody, - AzPhysics::SimulatedBody* childBody) - : Joint(pxJoint, parentBody, childBody) - { - } - - const AZ::Crc32 GetNativeType() const override; - void GenerateJointLimitVisualizationData( - float /*scale*/, - AZ::u32 /*angularSubdivisions*/, - AZ::u32 /*radialSubdivisions*/, - AZStd::vector& /*vertexBufferOut*/, - AZStd::vector& /*indexBufferOut*/, - AZStd::vector& /*lineBufferOut*/, - AZStd::vector& /*lineValidityBufferOut*/) override {} - }; - - /// Common parameters for all physics joint types. - class GenericJointConfiguration - { - public: - enum class GenericJointFlag : AZ::u16 - { - None = 0, - Breakable = 1, - SelfCollide = 1 << 1 - }; - - AZ_CLASS_ALLOCATOR(GenericJointConfiguration, AZ::SystemAllocator, 0); - AZ_TYPE_INFO(GenericJointConfiguration, "{AB2E2F92-0248-48A8-9DDD-21284AF0C1DF}"); - static void Reflect(AZ::ReflectContext* context); - static bool VersionConverter( - AZ::SerializeContext& context, - AZ::SerializeContext::DataElementNode& classElement); - - GenericJointConfiguration() = default; - GenericJointConfiguration(float forceMax, - float torqueMax, - AZ::Transform localTransformFromFollower, - AZ::EntityId leadEntity, - AZ::EntityId followerEntity, - GenericJointFlag flags); - - bool GetFlag(GenericJointFlag flag); ///< Returns if a particular flag is set as a bool. - - GenericJointFlag m_flags = GenericJointFlag::None; ///< Flags that indicates if joint is breakable, self-colliding, etc.. Converting joint between breakable/non-breakable at game time is allowed. - float m_forceMax = 1.0f; ///< Max force joint can tolerate before breaking. - float m_torqueMax = 1.0f; ///< Max torque joint can tolerate before breaking. - AZ::EntityId m_leadEntity; ///< EntityID for entity containing body that is lead to this joint constraint. - AZ::EntityId m_followerEntity; ///< EntityID for entity containing body that is follower to this joint constraint. - AZ::Transform m_localTransformFromFollower; ///< Joint's location and orientation in the frame (coordinate system) of the follower entity. - }; - AZ_DEFINE_ENUM_BITWISE_OPERATORS(PhysX::GenericJointConfiguration::GenericJointFlag) - - /// Generic pair of limit values for joint types, e.g. a pair of angular values. - /// This is different from JointLimitConfiguration used in non-generic joints for character/ragdoll/animation. - class GenericJointLimitsConfiguration - { - public: - AZ_CLASS_ALLOCATOR(GenericJointLimitsConfiguration, AZ::SystemAllocator, 0); - AZ_TYPE_INFO(GenericJointLimitsConfiguration, "{9D129B49-F4E6-4F2A-B94D-AC2D6AC6CE02}"); - static void Reflect(AZ::ReflectContext* context); - - GenericJointLimitsConfiguration() = default; - GenericJointLimitsConfiguration(float damping - , bool isLimited - , bool isSoftLimit - , float limitFirst - , float limitSecond - , float stiffness - , float tolerance); - - bool m_isLimited = true; ///< Specifies if limits are applied to the joint constraints. E.g. if the swing angles are limited. - bool m_isSoftLimit = false; ///< If limit is soft, spring and damping are used, otherwise tolerance is used. Converting between soft/hard limit at game time is allowed. - float m_damping = 20.0f; ///< The damping strength of the drive, the force proportional to the velocity error. Used if limit is soft. - float m_limitFirst = 45.0f; ///< Positive angle limit in the case of twist angle limits, Y-axis swing limit in the case of cone limits. - float m_limitSecond = 45.0f; ///< Negative angle limit in the case of twist angle limits, Z-axis swing limit in the case of cone limits. - float m_stiffness = 100.0f; ///< The spring strength of the drive, the force proportional to the position error. Used if limit is soft. - float m_tolerance = 0.1f; ///< Distance from the joint at which limits becomes enforced. Used if limit is hard. - }; - - class JointUtils - { - public: - static AZStd::vector GetSupportedJointTypes(); - - static AZStd::shared_ptr CreateJointLimitConfiguration(AZ::TypeId jointType); - - static AZStd::shared_ptr CreateJoint(const AZStd::shared_ptr& configuration, - AzPhysics::SimulatedBody* parentBody, AzPhysics::SimulatedBody* childBody); - - static D6JointState CalculateD6JointState( - const AZ::Quaternion& parentWorldRotation, - const AZ::Quaternion& parentLocalRotation, - const AZ::Quaternion& childWorldRotation, - const AZ::Quaternion& childLocalRotation); - - static bool IsD6SwingValid( - float swingAngleY, - float swingAngleZ, - float swingLimitY, - float swingLimitZ); - - static void AppendD6SwingConeToLineBuffer( - const AZ::Quaternion& parentLocalRotation, - float swingAngleY, - float swingAngleZ, - float swingLimitY, - float swingLimitZ, - float scale, - AZ::u32 angularSubdivisions, - AZ::u32 radialSubdivisions, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut); - - static void AppendD6TwistArcToLineBuffer( - const AZ::Quaternion& parentLocalRotation, - float twistAngle, - float twistLimitLower, - float twistLimitUpper, - float scale, - AZ::u32 angularSubdivisions, - AZ::u32 radialSubdivisions, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut); - - static void AppendD6CurrentTwistToLineBuffer( - const AZ::Quaternion& parentLocalRotation, - float twistAngle, - float twistLimitLower, - float twistLimitUpper, - float scale, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut); - - static void GenerateJointLimitVisualizationData( - const Physics::JointLimitConfiguration& configuration, - const AZ::Quaternion& parentRotation, - const AZ::Quaternion& childRotation, - float scale, - AZ::u32 angularSubdivisions, - AZ::u32 radialSubdivisions, - AZStd::vector& vertexBufferOut, - AZStd::vector& indexBufferOut, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut); - - static AZStd::unique_ptr ComputeInitialJointLimitConfiguration( - const AZ::TypeId& jointLimitTypeId, - const AZ::Quaternion& parentWorldRotation, - const AZ::Quaternion& childWorldRotation, - const AZ::Vector3& axis, - const AZStd::vector& exampleLocalRotations); - }; -} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/Joint/Configuration/PhysXJointConfiguration.cpp b/Gems/PhysX/Code/Source/Joint/Configuration/PhysXJointConfiguration.cpp new file mode 100644 index 0000000000..0d1fe9c988 --- /dev/null +++ b/Gems/PhysX/Code/Source/Joint/Configuration/PhysXJointConfiguration.cpp @@ -0,0 +1,155 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include + +namespace PhysX +{ + JointGenericProperties::JointGenericProperties(GenericJointFlag flags, float forceMax, float torqueMax) + : m_flags(flags) + , m_forceMax(forceMax) + , m_torqueMax(torqueMax) + { + + } + + JointLimitProperties::JointLimitProperties( + bool isLimited, bool isSoftLimit, + float damping, float limitFirst, float limitSecond, float stiffness, float tolerance) + : m_isLimited(isLimited) + , m_isSoftLimit(isSoftLimit) + , m_damping(damping) + , m_limitFirst(limitFirst) + , m_limitSecond(limitSecond) + , m_stiffness(stiffness) + , m_tolerance(tolerance) + { + + } + + bool JointGenericProperties::IsFlagSet(GenericJointFlag flag) const + { + return static_cast(m_flags & flag); + } + + void D6JointLimitConfiguration::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("SwingLimitY", &D6JointLimitConfiguration::m_swingLimitY) + ->Field("SwingLimitZ", &D6JointLimitConfiguration::m_swingLimitZ) + ->Field("TwistLowerLimit", &D6JointLimitConfiguration::m_twistLimitLower) + ->Field("TwistUpperLimit", &D6JointLimitConfiguration::m_twistLimitUpper) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "PhysX D6 Joint Configuration", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_swingLimitY, "Swing limit Y", + "Maximum angle from the Y axis of the joint frame") + ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") + ->Attribute(AZ::Edit::Attributes::Min, JointConstants::MinSwingLimitDegrees) + ->Attribute(AZ::Edit::Attributes::Max, 180.0f) + ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_swingLimitZ, "Swing limit Z", + "Maximum angle from the Z axis of the joint frame") + ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") + ->Attribute(AZ::Edit::Attributes::Min, JointConstants::MinSwingLimitDegrees) + ->Attribute(AZ::Edit::Attributes::Max, 180.0f) + ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_twistLimitLower, "Twist lower limit", + "Lower limit for rotation about the X axis of the joint frame") + ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") + ->Attribute(AZ::Edit::Attributes::Min, -180.0f) + ->Attribute(AZ::Edit::Attributes::Max, 180.0f) + ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_twistLimitUpper, "Twist upper limit", + "Upper limit for rotation about the X axis of the joint frame") + ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") + ->Attribute(AZ::Edit::Attributes::Min, -180.0f) + ->Attribute(AZ::Edit::Attributes::Max, 180.0f) + ; + } + } + } + + void JointGenericProperties::Reflect(AZ::ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("Maximum Force", &JointGenericProperties::m_forceMax) + ->Field("Maximum Torque", &JointGenericProperties::m_torqueMax) + ->Field("Flags", &JointGenericProperties::m_flags) + ; + } + } + + void JointLimitProperties::Reflect(AZ::ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("First Limit", &JointLimitProperties::m_limitFirst) + ->Field("Second Limit", &JointLimitProperties::m_limitSecond) + ->Field("Tolerance", &JointLimitProperties::m_tolerance) + ->Field("Is Limited", &JointLimitProperties::m_isLimited) + ->Field("Is Soft Limit", &JointLimitProperties::m_isSoftLimit) + ->Field("Damping", &JointLimitProperties::m_damping) + ->Field("Spring", &JointLimitProperties::m_stiffness) + ; + } + } + + void FixedJointConfiguration::Reflect(AZ::ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("Generic Properties", &FixedJointConfiguration::m_genericProperties) + ; + } + } + + void BallJointConfiguration::Reflect(AZ::ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("Generic Properties", &BallJointConfiguration::m_genericProperties) + ->Field("Limit Properties", &BallJointConfiguration::m_limitProperties) + ; + } + } + + void HingeJointConfiguration::Reflect(AZ::ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("Generic Properties", &HingeJointConfiguration::m_genericProperties) + ->Field("Limit Properties", &HingeJointConfiguration::m_limitProperties) + ; + } + } +} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/Joint/PhysXJoint.cpp b/Gems/PhysX/Code/Source/Joint/PhysXJoint.cpp new file mode 100644 index 0000000000..53f054c1df --- /dev/null +++ b/Gems/PhysX/Code/Source/Joint/PhysXJoint.cpp @@ -0,0 +1,200 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include +#include + +namespace PhysX +{ + AzPhysics::SimulatedBodyHandle PhysXJoint::GetParentBodyHandle() const + { + return m_parentBodyHandle; + } + + AzPhysics::SimulatedBodyHandle PhysXJoint::GetChildBodyHandle() const + { + return m_childBodyHandle; + } + + PhysXJoint::PhysXJoint( + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle) + : m_sceneHandle(sceneHandle) + , m_parentBodyHandle(parentBodyHandle) + , m_childBodyHandle(childBodyHandle) + { + + } + + bool PhysXJoint::SetPxActors() + { + physx::PxRigidActor* parentActor = Utils::GetPxRigidActor(m_sceneHandle, m_parentBodyHandle); + physx::PxRigidActor* childActor = Utils::GetPxRigidActor(m_sceneHandle, m_childBodyHandle); + if (!parentActor && !childActor) + { + AZ_Error("PhysX Joint", false, "Invalid PhysX actors in joint - at least one must be a PxRigidActor."); + return false; + } + + m_pxJoint->setActors(parentActor, childActor); + return true; + } + + void PhysXJoint::SetParentBody(AzPhysics::SimulatedBodyHandle parentBodyHandle) + { + auto* parentBody = Utils::GetSimulatedBodyFromHandle(m_sceneHandle, parentBodyHandle); + auto* childBody = Utils::GetSimulatedBodyFromHandle(m_sceneHandle, m_childBodyHandle); + + if (Utils::IsAtLeastOneDynamic(parentBody, childBody)) + { + m_parentBodyHandle = parentBodyHandle; + SetPxActors(); + } + else + { + AZ_Warning("PhysX Joint", false, "Call to SetParentBody would result in invalid joint - at least one " + "body in a joint must be dynamic."); + } + } + + void PhysXJoint::SetChildBody(AzPhysics::SimulatedBodyHandle childBodyHandle) + { + auto* parentBody = Utils::GetSimulatedBodyFromHandle(m_sceneHandle, m_parentBodyHandle); + auto* childBody = Utils::GetSimulatedBodyFromHandle(m_sceneHandle, childBodyHandle); + + if (Utils::IsAtLeastOneDynamic(parentBody, childBody)) + { + m_childBodyHandle = childBodyHandle; + SetPxActors(); + } + else + { + AZ_Warning("PhysX Joint", false, "Call to SetChildBody would result in invalid joint - at least one " + "body in a joint must be dynamic."); + } + } + + void* PhysXJoint::GetNativePointer() const + { + return m_pxJoint.get(); + } + + PhysXD6Joint::PhysXD6Joint(const D6JointLimitConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle) + : PhysXJoint(sceneHandle, parentBodyHandle, childBodyHandle) + { + m_pxJoint = Utils::PxJointFactories::CreatePxD6Joint(configuration, sceneHandle, parentBodyHandle, childBodyHandle); + } + + PhysXFixedJoint::PhysXFixedJoint(const FixedJointConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle) + : PhysXJoint(sceneHandle, parentBodyHandle, childBodyHandle) + { + m_pxJoint = Utils::PxJointFactories::CreatePxFixedJoint(configuration, sceneHandle, parentBodyHandle, childBodyHandle); + } + + PhysXBallJoint::PhysXBallJoint(const BallJointConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle) + : PhysXJoint(sceneHandle, parentBodyHandle, childBodyHandle) + { + m_pxJoint = Utils::PxJointFactories::CreatePxBallJoint(configuration, sceneHandle, parentBodyHandle, childBodyHandle); + } + + PhysXHingeJoint::PhysXHingeJoint(const HingeJointConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle) + : PhysXJoint(sceneHandle, parentBodyHandle, childBodyHandle) + { + m_pxJoint = Utils::PxJointFactories::CreatePxHingeJoint(configuration, sceneHandle, parentBodyHandle, childBodyHandle); + } + + AZ::Crc32 PhysXD6Joint::GetNativeType() const + { + return NativeTypeIdentifiers::D6Joint; + } + + AZ::Crc32 PhysXFixedJoint::GetNativeType() const + { + return NativeTypeIdentifiers::FixedJoint; + } + + AZ::Crc32 PhysXBallJoint::GetNativeType() const + { + return NativeTypeIdentifiers::BallJoint; + } + + AZ::Crc32 PhysXHingeJoint::GetNativeType() const + { + return NativeTypeIdentifiers::HingeJoint; + } + + void PhysXD6Joint::GenerateJointLimitVisualizationData( + float scale, + AZ::u32 angularSubdivisions, + AZ::u32 radialSubdivisions, + [[maybe_unused]] AZStd::vector& vertexBufferOut, + [[maybe_unused]] AZStd::vector& indexBufferOut, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut) + { + auto* parentBody = Utils::GetSimulatedBodyFromHandle(m_sceneHandle, m_parentBodyHandle); + auto* childBody = Utils::GetSimulatedBodyFromHandle(m_sceneHandle, m_childBodyHandle); + + const AZ::u32 angularSubdivisionsClamped = AZ::GetClamp(angularSubdivisions, 4u, 32u); + const AZ::u32 radialSubdivisionsClamped = AZ::GetClamp(radialSubdivisions, 1u, 4u); + + const physx::PxD6Joint* joint = static_cast(m_pxJoint.get()); + const AZ::Quaternion parentLocalRotation = PxMathConvert(joint->getLocalPose(physx::PxJointActorIndex::eACTOR0).q); + const AZ::Quaternion parentWorldRotation = parentBody ? parentBody->GetOrientation() : AZ::Quaternion::CreateIdentity(); + const AZ::Quaternion childLocalRotation = PxMathConvert(joint->getLocalPose(physx::PxJointActorIndex::eACTOR1).q); + const AZ::Quaternion childWorldRotation = childBody ? childBody->GetOrientation() : AZ::Quaternion::CreateIdentity(); + + const float swingAngleY = joint->getSwingYAngle(); + const float swingAngleZ = joint->getSwingZAngle(); + const float swingLimitY = joint->getSwingLimit().yAngle; + const float swingLimitZ = joint->getSwingLimit().zAngle; + const float twistAngle = joint->getTwist(); + const float twistLimitLower = joint->getTwistLimit().lower; + const float twistLimitUpper = joint->getTwistLimit().upper; + + Utils::Joints::AppendD6SwingConeToLineBuffer( + parentLocalRotation, swingAngleY, swingAngleZ, swingLimitY, swingLimitZ, + scale, angularSubdivisionsClamped, radialSubdivisionsClamped, lineBufferOut, lineValidityBufferOut); + Utils::Joints::AppendD6TwistArcToLineBuffer( + parentLocalRotation, twistAngle, twistLimitLower, twistLimitUpper, + scale, angularSubdivisionsClamped, radialSubdivisionsClamped, lineBufferOut, lineValidityBufferOut); + Utils::Joints::AppendD6CurrentTwistToLineBuffer( + parentLocalRotation, twistAngle, twistLimitLower, twistLimitUpper, + scale, lineBufferOut, lineValidityBufferOut); + + // draw the X-axis of the child joint frame + // make the axis slightly longer than the radius of the twist arc so that it is easy to see + float axisLength = 1.25f * scale; + AZ::Vector3 childAxis = (parentWorldRotation.GetConjugate() * childWorldRotation * childLocalRotation).TransformVector( + AZ::Vector3::CreateAxisX(axisLength)); + lineBufferOut.push_back(AZ::Vector3::CreateZero()); + lineBufferOut.push_back(childAxis); + } +} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/Joint/PhysXJoint.h b/Gems/PhysX/Code/Source/Joint/PhysXJoint.h new file mode 100644 index 0000000000..549025d951 --- /dev/null +++ b/Gems/PhysX/Code/Source/Joint/PhysXJoint.h @@ -0,0 +1,126 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +namespace PhysX +{ + class PhysXJoint + : public AzPhysics::Joint + { + public: + AZ_CLASS_ALLOCATOR(PhysXJoint, AZ::SystemAllocator, 0); + AZ_RTTI(PhysXJoint, "{DBE1D185-E318-407D-A5A1-AC1DE7F4A62D}", AzPhysics::Joint); + + PhysXJoint( + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle); + + virtual ~PhysXJoint() = default; + + AzPhysics::SimulatedBodyHandle GetParentBodyHandle() const override; + AzPhysics::SimulatedBodyHandle GetChildBodyHandle() const override; + void SetParentBody(AzPhysics::SimulatedBodyHandle parentBody) override; + void SetChildBody(AzPhysics::SimulatedBodyHandle childBody) override; + void* GetNativePointer() const override; + + protected: + bool SetPxActors(); + + Utils::PxJointUniquePtr m_pxJoint; + AzPhysics::SceneHandle m_sceneHandle; + AzPhysics::SimulatedBodyHandle m_parentBodyHandle; + AzPhysics::SimulatedBodyHandle m_childBodyHandle; + AZStd::string m_name; + }; + + class PhysXD6Joint + : public PhysXJoint + { + public: + AZ_CLASS_ALLOCATOR(PhysXD6Joint, AZ::SystemAllocator, 0); + AZ_RTTI(PhysXD6Joint, "{144B2FAF-A3EE-4FE1-9328-2C44FE1E3676}", PhysX::PhysXJoint); + + PhysXD6Joint(const D6JointLimitConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle); + + virtual ~PhysXD6Joint() = default; + + AZ::Crc32 GetNativeType() const override; + void GenerateJointLimitVisualizationData( + float scale, + AZ::u32 angularSubdivisions, + AZ::u32 radialSubdivisions, + AZStd::vector& vertexBufferOut, + AZStd::vector& indexBufferOut, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut) override; + }; + + //! A fixed joint locks 2 bodies relative to one another on all axes of freedom. + class PhysXFixedJoint : public PhysXJoint + { + public: + AZ_CLASS_ALLOCATOR(PhysXFixedJoint, AZ::SystemAllocator, 0); + AZ_RTTI(PhysXFixedJoint, "{B821D6D8-7B41-479D-9325-F9BC9754C5F8}", PhysX::PhysXJoint); + + PhysXFixedJoint(const FixedJointConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle); + + virtual ~PhysXFixedJoint() = default; + + AZ::Crc32 GetNativeType() const override; + }; + + //! A ball joint locks 2 bodies relative to one another except about the y and z axes of the joint between them. + class PhysXBallJoint : public PhysXJoint + { + public: + AZ_CLASS_ALLOCATOR(PhysXBallJoint, AZ::SystemAllocator, 0); + AZ_RTTI(PhysXBallJoint, "{9494CE43-3AE2-40AB-ADF7-FDC5F8B0F15A}", PhysX::PhysXJoint); + + PhysXBallJoint(const BallJointConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle); + + virtual ~PhysXBallJoint() = default; + + AZ::Crc32 GetNativeType() const override; + }; + + //! A hinge joint locks 2 bodies relative to one another except about the x-axis of the joint between them. + class PhysXHingeJoint : public PhysXJoint + { + public: + AZ_CLASS_ALLOCATOR(PhysXHingeJoint, AZ::SystemAllocator, 0); + AZ_RTTI(PhysXHingeJoint, "{9C5B955C-6C80-45FA-855D-DDA449C85313}", PhysX::PhysXJoint); + + PhysXHingeJoint(const HingeJointConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle); + + virtual ~PhysXHingeJoint() = default; + + AZ::Crc32 GetNativeType() const override; + }; +} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.cpp b/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.cpp new file mode 100644 index 0000000000..5ab4564e0e --- /dev/null +++ b/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.cpp @@ -0,0 +1,470 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace PhysX { + namespace Utils + { + struct PxJointActorData + { + static PxJointActorData InvalidPxJointActorData; + + physx::PxRigidActor* parentActor = nullptr; + physx::PxRigidActor* childActor = nullptr; + }; + PxJointActorData PxJointActorData::InvalidPxJointActorData; + + PxJointActorData GetJointPxActors( + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle) + { + auto* parentBody = GetSimulatedBodyFromHandle(sceneHandle, parentBodyHandle); + auto* childBody = GetSimulatedBodyFromHandle(sceneHandle, childBodyHandle); + + if (!IsAtLeastOneDynamic(parentBody, childBody)) + { + AZ_Warning("PhysX Joint", false, "CreateJoint failed - at least one body must be dynamic."); + return PxJointActorData::InvalidPxJointActorData; + } + + physx::PxRigidActor* parentActor = GetPxRigidActor(sceneHandle, parentBodyHandle); + physx::PxRigidActor* childActor = GetPxRigidActor(sceneHandle, childBodyHandle); + + if (!parentActor && !childActor) + { + AZ_Warning("PhysX Joint", false, "CreateJoint failed - at least one body must be a PxRigidActor."); + return PxJointActorData::InvalidPxJointActorData; + } + + return PxJointActorData{ + parentActor, + childActor + }; + } + + bool IsAtLeastOneDynamic(AzPhysics::SimulatedBody* body0, + AzPhysics::SimulatedBody* body1) + { + for (const AzPhysics::SimulatedBody* body : { body0, body1 }) + { + if (body) + { + if (body->GetNativeType() == NativeTypeIdentifiers::RigidBody || + body->GetNativeType() == NativeTypeIdentifiers::ArticulationLink) + { + return true; + } + } + } + return false; + } + + physx::PxRigidActor* GetPxRigidActor(AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle worldBodyHandle) + { + auto* worldBody = GetSimulatedBodyFromHandle(sceneHandle, worldBodyHandle); + if (worldBody != nullptr + && static_cast(worldBody->GetNativePointer())->is()) + { + return static_cast(worldBody->GetNativePointer()); + } + + return nullptr; + } + + void ReleasePxJoint(physx::PxJoint* joint) + { + PHYSX_SCENE_WRITE_LOCK(joint->getScene()); + joint->userData = nullptr; + joint->release(); + } + + AzPhysics::SimulatedBody* GetSimulatedBodyFromHandle(AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle bodyHandle) + { + if (auto* sceneInterface = AZ::Interface::Get()) + { + return sceneInterface->GetSimulatedBodyFromHandle(sceneHandle, bodyHandle); + } + return nullptr; + } + + void InitializeGenericProperties(const JointGenericProperties& properties, physx::PxJoint* nativeJoint) + { + if (!nativeJoint) + { + return; + } + PHYSX_SCENE_WRITE_LOCK(nativeJoint->getScene()); + nativeJoint->setConstraintFlag( + physx::PxConstraintFlag::eCOLLISION_ENABLED, + properties.IsFlagSet(JointGenericProperties::GenericJointFlag::SelfCollide)); + + if (properties.IsFlagSet(JointGenericProperties::GenericJointFlag::Breakable)) + { + nativeJoint->setBreakForce(properties.m_forceMax, properties.m_torqueMax); + } + } + + void InitializeSphericalLimitProperties(const JointLimitProperties& properties, physx::PxSphericalJoint* nativeJoint) + { + if (!nativeJoint) + { + return; + } + + if (!properties.m_isLimited) + { + nativeJoint->setSphericalJointFlag(physx::PxSphericalJointFlag::eLIMIT_ENABLED, false); + return; + } + + // Hard limit uses a tolerance value (distance to limit at which limit becomes active). + // Soft limit allows angle to exceed limit but springs back with configurable spring stiffness and damping. + physx::PxJointLimitCone swingLimit( + AZ::DegToRad(properties.m_limitFirst), + AZ::DegToRad(properties.m_limitSecond), + properties.m_tolerance); + + if (properties.m_isSoftLimit) + { + swingLimit.stiffness = properties.m_stiffness; + swingLimit.damping = properties.m_damping; + } + + nativeJoint->setLimitCone(swingLimit); + nativeJoint->setSphericalJointFlag(physx::PxSphericalJointFlag::eLIMIT_ENABLED, true); + } + + void InitializeRevoluteLimitProperties(const JointLimitProperties& properties, physx::PxRevoluteJoint* nativeJoint) + { + if (!nativeJoint) + { + return; + } + + if (!properties.m_isLimited) + { + nativeJoint->setRevoluteJointFlag(physx::PxRevoluteJointFlag::eLIMIT_ENABLED, false); + return; + } + + physx::PxJointAngularLimitPair limitPair( + AZ::DegToRad(properties.m_limitSecond), + AZ::DegToRad(properties.m_limitFirst), + properties.m_tolerance); + + if (properties.m_isSoftLimit) + { + limitPair.stiffness = properties.m_stiffness; + limitPair.damping = properties.m_damping; + } + + nativeJoint->setLimit(limitPair); + nativeJoint->setRevoluteJointFlag(physx::PxRevoluteJointFlag::eLIMIT_ENABLED, true); + } + + namespace PxJointFactories + { + PxJointUniquePtr CreatePxD6Joint( + const PhysX::D6JointLimitConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle) + { + PxJointActorData actorData = GetJointPxActors(sceneHandle, parentBodyHandle, childBodyHandle); + + if (!actorData.parentActor || !actorData.childActor) + { + return nullptr; + } + + const physx::PxTransform parentWorldTransform = + actorData.parentActor ? actorData.parentActor->getGlobalPose() : physx::PxTransform(physx::PxIdentity); + const physx::PxTransform childWorldTransform = + actorData.childActor ? actorData.childActor->getGlobalPose() : physx::PxTransform(physx::PxIdentity); + const physx::PxVec3 childOffset = childWorldTransform.p - parentWorldTransform.p; + physx::PxTransform parentLocalTransform(PxMathConvert(configuration.m_parentLocalRotation).getNormalized()); + const physx::PxTransform childLocalTransform(PxMathConvert(configuration.m_childLocalRotation).getNormalized()); + parentLocalTransform.p = parentWorldTransform.q.rotateInv(childOffset); + + physx::PxD6Joint* joint = PxD6JointCreate(PxGetPhysics(), + actorData.parentActor, parentLocalTransform, actorData.childActor, childLocalTransform); + + joint->setMotion(physx::PxD6Axis::eTWIST, physx::PxD6Motion::eLIMITED); + joint->setMotion(physx::PxD6Axis::eSWING1, physx::PxD6Motion::eLIMITED); + joint->setMotion(physx::PxD6Axis::eSWING2, physx::PxD6Motion::eLIMITED); + + AZ_Warning("PhysX Joint", + configuration.m_swingLimitY >= JointConstants::MinSwingLimitDegrees && configuration.m_swingLimitZ >= JointConstants::MinSwingLimitDegrees, + "Very small swing limit requested for joint between \"%s\" and \"%s\", increasing to %f degrees to improve stability", + actorData.parentActor ? actorData.parentActor->getName() : "world", + actorData.childActor ? actorData.childActor->getName() : "world", + JointConstants::MinSwingLimitDegrees); + + const float swingLimitY = AZ::DegToRad(AZ::GetMax(JointConstants::MinSwingLimitDegrees, configuration.m_swingLimitY)); + const float swingLimitZ = AZ::DegToRad(AZ::GetMax(JointConstants::MinSwingLimitDegrees, configuration.m_swingLimitZ)); + physx::PxJointLimitCone limitCone(swingLimitY, swingLimitZ); + joint->setSwingLimit(limitCone); + + const float twistLower = AZ::DegToRad(AZStd::GetMin(configuration.m_twistLimitLower, configuration.m_twistLimitUpper)); + const float twistUpper = AZ::DegToRad(AZStd::GetMax(configuration.m_twistLimitLower, configuration.m_twistLimitUpper)); + physx::PxJointAngularLimitPair twistLimitPair(twistLower, twistUpper); + joint->setTwistLimit(twistLimitPair); + + return Utils::PxJointUniquePtr(joint, ReleasePxJoint); + } + + PxJointUniquePtr CreatePxFixedJoint( + const PhysX::FixedJointConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle) + { + PxJointActorData actorData = GetJointPxActors(sceneHandle, parentBodyHandle, childBodyHandle); + + if (!actorData.parentActor || !actorData.childActor) + { + return nullptr; + } + + physx::PxFixedJoint* joint; + const AZ::Transform parentLocalTM = AZ::Transform::CreateFromQuaternionAndTranslation( + configuration.m_parentLocalRotation, configuration.m_parentLocalPosition); + const AZ::Transform childLocalTM = AZ::Transform::CreateFromQuaternionAndTranslation( + configuration.m_childLocalRotation, configuration.m_childLocalPosition); + + { + PHYSX_SCENE_READ_LOCK(actorData.childActor->getScene()); + joint = physx::PxFixedJointCreate(PxGetPhysics(), + actorData.parentActor, PxMathConvert(parentLocalTM), + actorData.childActor, PxMathConvert(childLocalTM)); + } + + InitializeGenericProperties( + configuration.m_genericProperties, + static_cast(joint)); + + return Utils::PxJointUniquePtr(joint, ReleasePxJoint); + } + + PxJointUniquePtr CreatePxBallJoint( + const PhysX::BallJointConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle) + { + PxJointActorData actorData = GetJointPxActors(sceneHandle, parentBodyHandle, childBodyHandle); + + if (!actorData.parentActor || !actorData.childActor) + { + return nullptr; + } + + physx::PxSphericalJoint* joint; + const AZ::Transform parentLocalTM = AZ::Transform::CreateFromQuaternionAndTranslation( + configuration.m_parentLocalRotation, configuration.m_parentLocalPosition); + const AZ::Transform childLocalTM = AZ::Transform::CreateFromQuaternionAndTranslation( + configuration.m_childLocalRotation, configuration.m_childLocalPosition); + + { + PHYSX_SCENE_READ_LOCK(actorData.childActor->getScene()); + joint = physx::PxSphericalJointCreate(PxGetPhysics(), + actorData.parentActor, PxMathConvert(parentLocalTM), + actorData.childActor, PxMathConvert(childLocalTM)); + } + + InitializeSphericalLimitProperties(configuration.m_limitProperties, joint); + InitializeGenericProperties( + configuration.m_genericProperties, + static_cast(joint)); + + return Utils::PxJointUniquePtr(joint, ReleasePxJoint); + } + + PxJointUniquePtr CreatePxHingeJoint( + const PhysX::HingeJointConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle) + { + PxJointActorData actorData = GetJointPxActors(sceneHandle, parentBodyHandle, childBodyHandle); + + if (!actorData.parentActor || !actorData.childActor) + { + return nullptr; + } + + physx::PxRevoluteJoint* joint; + const AZ::Transform parentLocalTM = AZ::Transform::CreateFromQuaternionAndTranslation( + configuration.m_parentLocalRotation, configuration.m_parentLocalPosition); + const AZ::Transform childLocalTM = AZ::Transform::CreateFromQuaternionAndTranslation( + configuration.m_childLocalRotation, configuration.m_childLocalPosition); + + { + PHYSX_SCENE_READ_LOCK(actorData.childActor->getScene()); + joint = physx::PxRevoluteJointCreate(PxGetPhysics(), + actorData.parentActor, PxMathConvert(parentLocalTM), + actorData.childActor, PxMathConvert(childLocalTM)); + } + + InitializeRevoluteLimitProperties(configuration.m_limitProperties, joint); + InitializeGenericProperties( + configuration.m_genericProperties, + static_cast(joint)); + + return Utils::PxJointUniquePtr(joint, ReleasePxJoint); + } + } // namespace PxJointFactories + + namespace Joints + { + bool IsD6SwingValid(float swingAngleY, float swingAngleZ, float swingLimitY, float swingLimitZ) + { + const float epsilon = AZ::Constants::FloatEpsilon; + const float yFactor = AZStd::tan(0.25f * swingAngleY) / AZStd::GetMax(epsilon, AZStd::tan(0.25f * swingLimitY)); + const float zFactor = AZStd::tan(0.25f * swingAngleZ) / AZStd::GetMax(epsilon, AZStd::tan(0.25f * swingLimitZ)); + + return (yFactor * yFactor + zFactor * zFactor <= 1.0f + epsilon); + } + + void AppendD6SwingConeToLineBuffer( + const AZ::Quaternion& parentLocalRotation, + float swingAngleY, + float swingAngleZ, + float swingLimitY, + float swingLimitZ, + float scale, + AZ::u32 angularSubdivisions, + AZ::u32 radialSubdivisions, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut) + { + const AZ::u32 numLinesSwingCone = angularSubdivisions * (1u + radialSubdivisions); + lineBufferOut.reserve(lineBufferOut.size() + 2u * numLinesSwingCone); + lineValidityBufferOut.reserve(lineValidityBufferOut.size() + numLinesSwingCone); + + // the orientation quat for a radial line in the cone can be represented in terms of sin and cos half angles + // these expressions can be efficiently calculated using tan quarter angles as follows: + // writing t = tan(x / 4) + // sin(x / 2) = 2 * t / (1 + t * t) + // cos(x / 2) = (1 - t * t) / (1 + t * t) + const float tanQuarterSwingZ = AZStd::tan(0.25f * swingLimitZ); + const float tanQuarterSwingY = AZStd::tan(0.25f * swingLimitY); + + AZ::Vector3 previousRadialVector = AZ::Vector3::CreateZero(); + for (AZ::u32 angularIndex = 0; angularIndex <= angularSubdivisions; angularIndex++) + { + const float angle = AZ::Constants::TwoPi / angularSubdivisions * angularIndex; + // the axis about which to rotate the x-axis to get the radial vector for this segment of the cone + const AZ::Vector3 rotationAxis(0, -tanQuarterSwingY * sinf(angle), tanQuarterSwingZ * cosf(angle)); + const float normalizationFactor = rotationAxis.GetLengthSq(); + const AZ::Quaternion radialVectorRotation = 1.0f / (1.0f + normalizationFactor) * + AZ::Quaternion::CreateFromVector3AndValue(2.0f * rotationAxis, 1.0f - normalizationFactor); + const AZ::Vector3 radialVector = + (parentLocalRotation * radialVectorRotation).TransformVector(AZ::Vector3::CreateAxisX(scale)); + + if (angularIndex > 0) + { + for (AZ::u32 radialIndex = 1; radialIndex <= radialSubdivisions; radialIndex++) + { + float radiusFraction = 1.0f / radialSubdivisions * radialIndex; + lineBufferOut.push_back(radiusFraction * radialVector); + lineBufferOut.push_back(radiusFraction * previousRadialVector); + } + } + + if (angularIndex < angularSubdivisions) + { + lineBufferOut.push_back(AZ::Vector3::CreateZero()); + lineBufferOut.push_back(radialVector); + } + + previousRadialVector = radialVector; + } + + const bool swingValid = IsD6SwingValid(swingAngleY, swingAngleZ, swingLimitY, swingLimitZ); + lineValidityBufferOut.insert(lineValidityBufferOut.end(), numLinesSwingCone, swingValid); + } + + void AppendD6TwistArcToLineBuffer( + const AZ::Quaternion& parentLocalRotation, + float twistAngle, + float twistLimitLower, + float twistLimitUpper, + float scale, + AZ::u32 angularSubdivisions, + AZ::u32 radialSubdivisions, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut) + { + const AZ::u32 numLinesTwistArc = angularSubdivisions * (1u + radialSubdivisions) + 1u; + lineBufferOut.reserve(lineBufferOut.size() + 2u * numLinesTwistArc); + + AZ::Vector3 previousRadialVector = AZ::Vector3::CreateZero(); + const float twistRange = twistLimitUpper - twistLimitLower; + + for (AZ::u32 angularIndex = 0; angularIndex <= angularSubdivisions; angularIndex++) + { + const float angle = twistLimitLower + twistRange / angularSubdivisions * angularIndex; + const AZ::Vector3 radialVector = + parentLocalRotation.TransformVector(scale * AZ::Vector3(0.0f, cosf(angle), sinf(angle))); + + if (angularIndex > 0) + { + for (AZ::u32 radialIndex = 1; radialIndex <= radialSubdivisions; radialIndex++) + { + const float radiusFraction = 1.0f / radialSubdivisions * radialIndex; + lineBufferOut.push_back(radiusFraction * radialVector); + lineBufferOut.push_back(radiusFraction * previousRadialVector); + } + } + + lineBufferOut.push_back(AZ::Vector3::CreateZero()); + lineBufferOut.push_back(radialVector); + + previousRadialVector = radialVector; + } + + const bool twistValid = (twistAngle >= twistLimitLower && twistAngle <= twistLimitUpper); + lineValidityBufferOut.insert(lineValidityBufferOut.end(), numLinesTwistArc, twistValid); + } + + void AppendD6CurrentTwistToLineBuffer( + const AZ::Quaternion& parentLocalRotation, + float twistAngle, + [[maybe_unused]] float twistLimitLower, + [[maybe_unused]] float twistLimitUpper, + float scale, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut) + { + const AZ::Vector3 twistVector = + parentLocalRotation.TransformVector(1.25f * scale * AZ::Vector3(0.0f, cosf(twistAngle), sinf(twistAngle))); + lineBufferOut.push_back(AZ::Vector3::CreateZero()); + lineBufferOut.push_back(twistVector); + lineValidityBufferOut.push_back(true); + } + } // namespace Joints + } // namespace Utils +} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.h b/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.h new file mode 100644 index 0000000000..a129c6862a --- /dev/null +++ b/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.h @@ -0,0 +1,101 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +#include + +namespace PhysX +{ + namespace JointConstants + { + // Setting swing limits to very small values can cause extreme stability problems, so clamp above a small + // threshold. + static const float MinSwingLimitDegrees = 1.0f; + } // namespace JointConstants + + namespace Utils + { + using PxJointUniquePtr = AZStd::unique_ptr>; + + bool IsAtLeastOneDynamic(AzPhysics::SimulatedBody* body0, AzPhysics::SimulatedBody* body1); + + physx::PxRigidActor* GetPxRigidActor(AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle worldBodyHandle); + AzPhysics::SimulatedBody* GetSimulatedBodyFromHandle( + AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle bodyHandle); + + namespace PxJointFactories + { + PxJointUniquePtr CreatePxD6Joint(const PhysX::D6JointLimitConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle); + + PxJointUniquePtr CreatePxFixedJoint(const PhysX::FixedJointConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle); + + PxJointUniquePtr CreatePxBallJoint(const PhysX::BallJointConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle); + + PxJointUniquePtr CreatePxHingeJoint(const PhysX::HingeJointConfiguration& configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle); + } // namespace PxActorFactories + + namespace Joints + { + bool IsD6SwingValid(float swingAngleY, float swingAngleZ, float swingLimitY, float swingLimitZ); + + void AppendD6SwingConeToLineBuffer( + const AZ::Quaternion& parentLocalRotation, + float swingAngleY, + float swingAngleZ, + float swingLimitY, + float swingLimitZ, + float scale, + AZ::u32 angularSubdivisions, + AZ::u32 radialSubdivisions, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut); + + void AppendD6TwistArcToLineBuffer( + const AZ::Quaternion& parentLocalRotation, + float twistAngle, + float twistLimitLower, + float twistLimitUpper, + float scale, + AZ::u32 angularSubdivisions, + AZ::u32 radialSubdivisions, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut); + + void AppendD6CurrentTwistToLineBuffer( + const AZ::Quaternion& parentLocalRotation, + float twistAngle, + float twistLimitLower, + float twistLimitUpper, + float scale, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut); + } // namespace Joints + } // namespace Utils +} // namespace PhysX + diff --git a/Gems/PhysX/Code/Source/JointComponent.cpp b/Gems/PhysX/Code/Source/JointComponent.cpp index 39fbe4a3fa..0d62cafa62 100644 --- a/Gems/PhysX/Code/Source/JointComponent.cpp +++ b/Gems/PhysX/Code/Source/JointComponent.cpp @@ -18,33 +18,65 @@ #include #include #include -#include #include #include +#include namespace PhysX { + JointComponentConfiguration::JointComponentConfiguration( + AZ::Transform localTransformFromFollower, + AZ::EntityId leadEntity, + AZ::EntityId followerEntity) + : m_localTransformFromFollower(localTransformFromFollower) + , m_leadEntity(leadEntity) + , m_followerEntity(followerEntity) + { + } + + void JointComponentConfiguration::Reflect(AZ::ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(2) + ->Field("Follower Local Transform", &JointComponentConfiguration::m_localTransformFromFollower) + ->Field("Lead Entity", &JointComponentConfiguration::m_leadEntity) + ->Field("Follower Entity", &JointComponentConfiguration::m_followerEntity) + ; + } + } + void JointComponent::Reflect(AZ::ReflectContext* context) { + JointComponentConfiguration::Reflect(context); + if (auto* serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(1) + ->Version(2) ->Field("Joint Configuration", &JointComponent::m_configuration) + ->Field("Joint Generic Properties", &JointComponent::m_genericProperties) ->Field("Joint Limits", &JointComponent::m_limits) ; } } - JointComponent::JointComponent(const GenericJointConfiguration& config) - : m_configuration(config) + JointComponent::JointComponent( + const JointComponentConfiguration& configuration, + const JointGenericProperties& genericProperties) + : m_configuration(configuration) + , m_genericProperties(genericProperties) { } - JointComponent::JointComponent(const GenericJointConfiguration& config - , const GenericJointLimitsConfiguration& limits) - : m_configuration(AZStd::move(config)) - , m_limits(limits) + JointComponent::JointComponent( + const JointComponentConfiguration& configuration, + const JointGenericProperties& genericProperties, + const JointLimitProperties& limitProperties) + : m_configuration(configuration) + , m_genericProperties(genericProperties) + , m_limits(limitProperties) { } @@ -67,16 +99,22 @@ namespace PhysX void JointComponent::Deactivate() { AZ::EntityBus::Handler::BusDisconnect(); - m_joint.reset(); + if (auto* physicsSystem = AZ::Interface::Get()) + { + if (auto* scene = physicsSystem->GetScene(m_jointSceneOwner)) + { + scene->RemoveJoint(m_jointHandle); + m_jointSceneOwner = AzPhysics::InvalidSceneHandle; + } + } } - physx::PxTransform JointComponent::GetJointLocalPose(const physx::PxRigidActor* actor - , const physx::PxTransform& jointPose) + AZ::Transform JointComponent::GetJointLocalPose(const physx::PxRigidActor* actor, const AZ::Transform& jointPose) { if (!actor) { AZ_Error("JointComponent::GetJointLocalPose", false, "Can't get pose for invalid actor pointer."); - return physx::PxTransform(); + return AZ::Transform::CreateIdentity(); } PHYSX_SCENE_READ_LOCK(actor->getScene()); @@ -84,41 +122,17 @@ namespace PhysX physx::PxTransform actorTranslateInv(-actorPose.p); physx::PxTransform actorRotateInv(actorPose.q); actorRotateInv = actorRotateInv.getInverse(); - return actorRotateInv * actorTranslateInv * jointPose; + return PxMathConvert(actorRotateInv * actorTranslateInv) * jointPose; } AZ::Transform JointComponent::GetJointTransform(AZ::EntityId entityId - , const GenericJointConfiguration& jointConfig) + , const JointComponentConfiguration& jointConfig) { AZ::Transform jointTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(entityId); jointTransform = jointTransform * jointConfig.m_localTransformFromFollower; return jointTransform; } - void JointComponent::InitGenericProperties() - { - if (!m_joint) - { - return; - } - - physx::PxJoint* jointNative = static_cast(m_joint->GetNativePointer()); - if (!jointNative) - { - return; - } - PHYSX_SCENE_WRITE_LOCK(jointNative->getScene()); - jointNative->setConstraintFlag( - physx::PxConstraintFlag::eCOLLISION_ENABLED, - m_configuration.GetFlag(GenericJointConfiguration::GenericJointFlag::SelfCollide)); - - if (m_configuration.GetFlag(GenericJointConfiguration::GenericJointFlag::Breakable)) - { - jointNative->setBreakForce(m_configuration.m_forceMax - , m_configuration.m_torqueMax); - } - } - void JointComponent::ObtainLeadFollowerInfo(JointComponent::LeadFollowerInfo& info) { info = LeadFollowerInfo(); @@ -160,16 +174,15 @@ namespace PhysX const AZ::Transform jointTransform = GetJointTransform(GetEntityId(), m_configuration); - physx::PxTransform jointPose = PxMathConvert(jointTransform); if (info.m_leadActor) { - info.m_leadLocal = GetJointLocalPose(info.m_leadActor, jointPose); // joint position & orientation in lead actor's frame. + info.m_leadLocal = GetJointLocalPose(info.m_leadActor, jointTransform); // joint position & orientation in lead actor's frame. } else { - info.m_leadLocal = jointPose; // lead is null, attaching follower to global position of joint. + info.m_leadLocal = jointTransform; // lead is null, attaching follower to global position of joint. } - info.m_followerLocal = PxMathConvert(m_configuration.m_localTransformFromFollower);// joint position & orientation in follower actor's frame. + info.m_followerLocal = m_configuration.m_localTransformFromFollower;// joint position & orientation in follower actor's frame. } void JointComponent::WarnInvalidJointSetup(AZ::EntityId entityId, const AZStd::string& message) @@ -189,7 +202,6 @@ namespace PhysX if (!m_configuration.m_leadEntity.IsValid() || entityId == m_configuration.m_leadEntity) { InitNativeJoint(); // Invoke overriden specific joint type instantiation - InitGenericProperties(); } // Else, follower entity is activated, subscribe to be notified that lead entity is activated. else diff --git a/Gems/PhysX/Code/Source/JointComponent.h b/Gems/PhysX/Code/Source/JointComponent.h index 468470dd37..6c63cc8ac3 100644 --- a/Gems/PhysX/Code/Source/JointComponent.h +++ b/Gems/PhysX/Code/Source/JointComponent.h @@ -18,7 +18,7 @@ #include #include -#include +#include namespace AzPhysics { @@ -27,6 +27,24 @@ namespace AzPhysics namespace PhysX { + class JointComponentConfiguration + { + public: + AZ_CLASS_ALLOCATOR(JointComponentConfiguration, AZ::SystemAllocator, 0); + AZ_TYPE_INFO(JointComponentConfiguration, "{1454F33F-AA6E-424B-A70C-9E463FBDEA19}"); + static void Reflect(AZ::ReflectContext* context); + + JointComponentConfiguration() = default; + JointComponentConfiguration( + AZ::Transform localTransformFromFollower, + AZ::EntityId leadEntity, + AZ::EntityId followerEntity); + + AZ::EntityId m_leadEntity; ///< EntityID for entity containing body that is lead to this joint constraint. + AZ::EntityId m_followerEntity; ///< EntityID for entity containing body that is follower to this joint constraint. + AZ::Transform m_localTransformFromFollower; ///< Joint's location and orientation in the frame (coordinate system) of the follower entity. + }; + /// Base class for game-time generic joint components. class JointComponent: public AZ::Component , protected AZ::EntityBus::Handler @@ -36,9 +54,13 @@ namespace PhysX static void Reflect(AZ::ReflectContext* context); JointComponent() = default; - explicit JointComponent(const GenericJointConfiguration& config); - JointComponent(const GenericJointConfiguration& config - , const GenericJointLimitsConfiguration& limits); + JointComponent( + const JointComponentConfiguration& configuration, + const JointGenericProperties& genericProperties); + JointComponent( + const JointComponentConfiguration& configuration, + const JointGenericProperties& genericProperties, + const JointLimitProperties& limitProperties); protected: /// Struct to provide subclasses with native pointers during joint initialization. @@ -47,8 +69,8 @@ namespace PhysX { physx::PxRigidActor* m_leadActor = nullptr; physx::PxRigidActor* m_followerActor = nullptr; - physx::PxTransform m_leadLocal = physx::PxTransform(physx::PxIdentity); - physx::PxTransform m_followerLocal = physx::PxTransform(physx::PxIdentity); + AZ::Transform m_leadLocal = AZ::Transform::CreateIdentity(); + AZ::Transform m_followerLocal = AZ::Transform::CreateIdentity(); AzPhysics::SimulatedBody* m_leadBody = nullptr; AzPhysics::SimulatedBody* m_followerBody = nullptr; }; @@ -63,14 +85,10 @@ namespace PhysX /// Invoked in JointComponent::OnEntityActivated for specific joint types to instantiate native joint pointer. virtual void InitNativeJoint() {}; - physx::PxTransform GetJointLocalPose(const physx::PxRigidActor* actor, - const physx::PxTransform& jointPose); + AZ::Transform GetJointLocalPose(const physx::PxRigidActor* actor, const AZ::Transform& jointPose); AZ::Transform GetJointTransform(AZ::EntityId entityId, - const GenericJointConfiguration& jointConfig); - - /// Initializes joint properties common to all native joint types after native joint creation. - void InitGenericProperties(); + const JointComponentConfiguration& jointConfig); /// Used on initialization by sub-classes to get native pointers from entity IDs. /// This allows sub-classes to instantiate specific native types. This base class does not need knowledge of any specific joint type. @@ -79,8 +97,11 @@ namespace PhysX /// Issues warnings for invalid scenarios when initializing a joint from entity IDs. void WarnInvalidJointSetup(AZ::EntityId entityId, const AZStd::string& message); - GenericJointConfiguration m_configuration; - GenericJointLimitsConfiguration m_limits; - AZStd::shared_ptr m_joint = nullptr; + + JointComponentConfiguration m_configuration; + JointGenericProperties m_genericProperties; + JointLimitProperties m_limits; + AzPhysics::JointHandle m_jointHandle = AzPhysics::InvalidJointHandle; + AzPhysics::SceneHandle m_jointSceneOwner = AzPhysics::InvalidSceneHandle; }; } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp index 87e3304a90..90c8d1ad77 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp @@ -18,10 +18,10 @@ #include #include #include +#include #include #include #include -#include namespace PhysX { @@ -262,21 +262,24 @@ namespace PhysX physx::PxTransform parentTM(parentOffset); physx::PxTransform childTM(physx::PxIdentity); - AZStd::shared_ptr jointLimitConfig = configuration.m_nodes[nodeIndex].m_jointLimit; - if (!jointLimitConfig) + AZStd::shared_ptr jointConfig = configuration.m_nodes[nodeIndex].m_jointConfig; + if (!jointConfig) { - AZStd::vector supportedJointLimitTypes = JointUtils::GetSupportedJointTypes(); - - if (!supportedJointLimitTypes.empty()) - { - jointLimitConfig = JointUtils::CreateJointLimitConfiguration(supportedJointLimitTypes[0]); - } + jointConfig = AZStd::make_shared(); } + + AzPhysics::JointHandle jointHandle = sceneInterface->AddJoint( + sceneHandle, jointConfig.get(), + ragdoll->GetNode(parentIndex)->GetRigidBody().m_bodyHandle, + ragdoll->GetNode(nodeIndex)->GetRigidBody().m_bodyHandle); + + AzPhysics::Joint* joint = sceneInterface->GetJointFromHandle(sceneHandle, jointHandle); - AZStd::shared_ptr joint = JointUtils::CreateJoint( - jointLimitConfig, - &ragdoll->GetNode(parentIndex)->GetRigidBody(), - &ragdoll->GetNode(nodeIndex)->GetRigidBody()); + if (!joint) + { + AZ_Error("PhysX Ragdoll", false, "Failed to create joint for node index %i.", nodeIndex); + return nullptr; + } // Moving from PhysX 3.4 to 4.1, the allowed range of the twist angle was expanded from -pi..pi // to -2*pi..2*pi. diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.cpp index 4725212a9d..a489135d34 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.cpp @@ -378,8 +378,8 @@ namespace PhysX else { actor->setRigidBodyFlag(physx::PxRigidBodyFlag::eKINEMATIC, false); - const AZStd::shared_ptr& joint = m_nodes[nodeIndex]->GetJoint(); - if (joint) + + if (AzPhysics::Joint* joint = m_nodes[nodeIndex]->GetJoint()) { if (physx::PxD6Joint* pxJoint = static_cast(joint->GetNativePointer())) { diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.cpp index 0f9c7644cd..a76fecb613 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.cpp @@ -38,11 +38,17 @@ namespace PhysX RagdollNode::~RagdollNode() { + DestroyJoint(); DestroyPhysicsBody(); } - void RagdollNode::SetJoint(const AZStd::shared_ptr& joint) + void RagdollNode::SetJoint(AzPhysics::Joint* joint) { + if (m_joint) + { + return; + } + m_joint = joint; } @@ -52,7 +58,7 @@ namespace PhysX return *m_rigidBody; } - const AZStd::shared_ptr& RagdollNode::GetJoint() const + AzPhysics::Joint* RagdollNode::GetJoint() { return m_joint; } @@ -167,4 +173,16 @@ namespace PhysX } } + void RagdollNode::DestroyJoint() + { + if (m_joint != nullptr && m_sceneOwner != AzPhysics::InvalidSceneHandle) + { + if (auto* sceneInterface = AZ::Interface::Get()) + { + sceneInterface->RemoveJoint(m_sceneOwner, m_joint->m_jointHandle); + } + m_joint = nullptr; + } + } + } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.h b/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.h index 0567723c01..2f1e508dd4 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.h +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.h @@ -32,11 +32,11 @@ namespace PhysX explicit RagdollNode(AzPhysics::SceneHandle sceneHandle, Physics::RagdollNodeConfiguration& nodeConfig); ~RagdollNode(); - void SetJoint(const AZStd::shared_ptr& joint); + void SetJoint(AzPhysics::Joint* joint); // Physics::RagdollNode AzPhysics::RigidBody& GetRigidBody() override; - const AZStd::shared_ptr& GetJoint() const override; + AzPhysics::Joint* GetJoint() override; bool IsSimulating() const override; // AzPhysics::SimulatedBody @@ -60,9 +60,10 @@ namespace PhysX private: void CreatePhysicsBody(AzPhysics::SceneHandle sceneHandle, Physics::RagdollNodeConfiguration& nodeConfig); void DestroyPhysicsBody(); + void DestroyJoint(); - AZStd::shared_ptr m_joint; - AzPhysics::RigidBody* m_rigidBody; + AzPhysics::Joint* m_joint = nullptr; + AzPhysics::RigidBody* m_rigidBody = nullptr; AzPhysics::SimulatedBodyHandle m_rigidBodyHandle = AzPhysics::InvalidSimulatedBodyHandle; AzPhysics::SceneHandle m_sceneOwner = AzPhysics::InvalidSceneHandle; PhysX::ActorData m_actorUserData; diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp index 8da512647f..c341fbf348 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp @@ -379,7 +379,7 @@ namespace PhysX for (size_t nodeIndex = 0; nodeIndex < numNodes; nodeIndex++) { - if (const AZStd::shared_ptr& joint = ragdoll->GetNode(nodeIndex)->GetJoint()) + if (const AzPhysics::Joint* joint = ragdoll->GetNode(nodeIndex)->GetJoint()) { if (auto* pxJoint = static_cast(joint->GetNativePointer())) { diff --git a/Gems/PhysX/Code/Source/Platform/Android/PAL_android.cmake b/Gems/PhysX/Code/Source/Platform/Android/PAL_android.cmake index 975225b8a4..4002fe5484 100644 --- a/Gems/PhysX/Code/Source/Platform/Android/PAL_android.cmake +++ b/Gems/PhysX/Code/Source/Platform/Android/PAL_android.cmake @@ -10,3 +10,5 @@ # set(PAL_TRAIT_PHYSX_SUPPORTED TRUE) +set(PAL_TRAIT_JOINTS_TYPED_TEST_CASE FALSE) + diff --git a/Gems/PhysX/Code/Source/Platform/Linux/PAL_linux.cmake b/Gems/PhysX/Code/Source/Platform/Linux/PAL_linux.cmake index 975225b8a4..d57b460d13 100644 --- a/Gems/PhysX/Code/Source/Platform/Linux/PAL_linux.cmake +++ b/Gems/PhysX/Code/Source/Platform/Linux/PAL_linux.cmake @@ -10,3 +10,4 @@ # set(PAL_TRAIT_PHYSX_SUPPORTED TRUE) +set(PAL_TRAIT_JOINTS_TYPED_TEST_CASE FALSE) diff --git a/Gems/PhysX/Code/Source/Platform/Mac/PAL_mac.cmake b/Gems/PhysX/Code/Source/Platform/Mac/PAL_mac.cmake index 975225b8a4..052ad091e4 100644 --- a/Gems/PhysX/Code/Source/Platform/Mac/PAL_mac.cmake +++ b/Gems/PhysX/Code/Source/Platform/Mac/PAL_mac.cmake @@ -10,3 +10,4 @@ # set(PAL_TRAIT_PHYSX_SUPPORTED TRUE) +set(PAL_TRAIT_JOINTS_TYPED_TEST_CASE TRUE) \ No newline at end of file diff --git a/Gems/PhysX/Code/Source/Platform/Windows/PAL_windows.cmake b/Gems/PhysX/Code/Source/Platform/Windows/PAL_windows.cmake index 975225b8a4..052ad091e4 100644 --- a/Gems/PhysX/Code/Source/Platform/Windows/PAL_windows.cmake +++ b/Gems/PhysX/Code/Source/Platform/Windows/PAL_windows.cmake @@ -10,3 +10,4 @@ # set(PAL_TRAIT_PHYSX_SUPPORTED TRUE) +set(PAL_TRAIT_JOINTS_TYPED_TEST_CASE TRUE) \ No newline at end of file diff --git a/Gems/PhysX/Code/Source/Platform/iOS/PAL_ios.cmake b/Gems/PhysX/Code/Source/Platform/iOS/PAL_ios.cmake index 975225b8a4..b7794acee0 100644 --- a/Gems/PhysX/Code/Source/Platform/iOS/PAL_ios.cmake +++ b/Gems/PhysX/Code/Source/Platform/iOS/PAL_ios.cmake @@ -10,3 +10,4 @@ # set(PAL_TRAIT_PHYSX_SUPPORTED TRUE) +set(PAL_TRAIT_JOINTS_TYPED_TEST_CASE TRUE) diff --git a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp index 689ea47be7..a47e2a99ec 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp +++ b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include namespace PhysX { @@ -232,6 +234,18 @@ namespace PhysX scene->GetSceneHandle()); } + template + AzPhysics::Joint* CreateJoint(const ConfigurationType* configuration, + AzPhysics::SceneHandle sceneHandle, + AzPhysics::SimulatedBodyHandle parentBodyHandle, + AzPhysics::SimulatedBodyHandle childBodyHandle, + AZ::Crc32& crc) + { + JointType* newBody = aznew JointType(*configuration, sceneHandle, parentBodyHandle, childBodyHandle); + crc = AZ::Crc32(newBody, sizeof(*newBody)); + return newBody; + } + //helper to perform a ray cast AzPhysics::SceneQueryHits RayCast(const AzPhysics::RayCastRequest* raycastRequest, AZStd::vector& raycastBuffer, @@ -813,6 +827,90 @@ namespace PhysX } } + AzPhysics::JointHandle PhysXScene::AddJoint(const AzPhysics::JointConfiguration* jointConfig, + AzPhysics::SimulatedBodyHandle parentBody, AzPhysics::SimulatedBodyHandle childBody) + { + AzPhysics::Joint* newJoint = nullptr; + AZ::Crc32 newJointCrc; + if (azrtti_istypeof(jointConfig)) + { + newJoint = Internal::CreateJoint( + azdynamic_cast(jointConfig), + m_sceneHandle, parentBody, childBody, newJointCrc); + } + else if (azrtti_istypeof(jointConfig)) + { + newJoint = Internal::CreateJoint( + azdynamic_cast(jointConfig), + m_sceneHandle, parentBody, childBody, newJointCrc); + } + else if (azrtti_istypeof(jointConfig)) + { + newJoint = Internal::CreateJoint( + azdynamic_cast(jointConfig), + m_sceneHandle, parentBody, childBody, newJointCrc); + } + else if (azrtti_istypeof(jointConfig)) + { + newJoint = Internal::CreateJoint( + azdynamic_cast(jointConfig), + m_sceneHandle, parentBody, childBody, newJointCrc); + } + else + { + AZ_Warning("PhysXScene", false, "Unknown JointConfiguration."); + return AzPhysics::InvalidJointHandle; + } + + if (newJoint != nullptr) + { + AzPhysics::JointIndex index = index = m_joints.size(); + m_joints.emplace_back(newJointCrc, newJoint); + + const AzPhysics::JointHandle newJointHandle(newJointCrc, index); + newJoint->m_sceneOwner = m_sceneHandle; + newJoint->m_jointHandle = newJointHandle; + + return newJointHandle; + } + + return AzPhysics::InvalidJointHandle; + } + + AzPhysics::Joint* PhysXScene::GetJointFromHandle(AzPhysics::JointHandle jointHandle) + { + if (jointHandle == AzPhysics::InvalidJointHandle) + { + return nullptr; + } + + AzPhysics::JointIndex index = AZStd::get(jointHandle); + if (index < m_joints.size() + && m_joints[index].first == AZStd::get(jointHandle)) + { + return m_joints[index].second; + } + return nullptr; + } + + void PhysXScene::RemoveJoint(AzPhysics::JointHandle jointHandle) + { + if (jointHandle == AzPhysics::InvalidJointHandle) + { + return; + } + + AzPhysics::JointIndex index = AZStd::get(jointHandle); + if (index < m_joints.size() + && m_joints[index].first == AZStd::get(jointHandle)) + { + m_deferredDeletionsJoints.push_back(m_joints[index].second); + m_joints[index] = AZStd::make_pair(AZ::Crc32(), nullptr); + m_freeJointSlots.push(index); + jointHandle = AzPhysics::InvalidJointHandle; + } + } + AzPhysics::SceneQueryHits PhysXScene::QueryScene(const AzPhysics::SceneQueryRequest* request) { if (request == nullptr) @@ -996,6 +1094,13 @@ namespace PhysX { delete simulatedBody; } + + AZStd::vector jointDeletions; + jointDeletions.swap(m_deferredDeletionsJoints); + for (auto* joint : jointDeletions) + { + delete joint; + } } void PhysXScene::ProcessTriggerEvents() diff --git a/Gems/PhysX/Code/Source/Scene/PhysXScene.h b/Gems/PhysX/Code/Source/Scene/PhysXScene.h index 2e257283f0..c6bb045e65 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXScene.h +++ b/Gems/PhysX/Code/Source/Scene/PhysXScene.h @@ -12,6 +12,7 @@ #pragma once #include +#include #include #include #include @@ -52,6 +53,10 @@ namespace PhysX void RemoveSimulatedBodies(AzPhysics::SimulatedBodyHandleList& bodyHandles) override; void EnableSimulationOfBody(AzPhysics::SimulatedBodyHandle bodyHandle) override; void DisableSimulationOfBody(AzPhysics::SimulatedBodyHandle bodyHandle) override; + AzPhysics::JointHandle AddJoint(const AzPhysics::JointConfiguration* jointConfig, + AzPhysics::SimulatedBodyHandle parentBody, AzPhysics::SimulatedBodyHandle childBody) override; + AzPhysics::Joint* GetJointFromHandle(AzPhysics::JointHandle jointHandle) override; + void RemoveJoint(AzPhysics::JointHandle jointHandle) override; AzPhysics::SceneQueryHits QueryScene(const AzPhysics::SceneQueryRequest* request) override; AzPhysics::SceneQueryHitsList QuerySceneBatch(const AzPhysics::SceneQueryRequests& requests) override; [[nodiscard]] bool QuerySceneAsync(AzPhysics::SceneQuery::AsyncRequestId requestId, @@ -94,6 +99,10 @@ namespace PhysX AZStd::vector m_deferredDeletions; AZStd::queue m_freeSceneSlots; + AZStd::vector> m_joints; + AZStd::vector m_deferredDeletionsJoints; + AZStd::queue m_freeJointSlots; + AzPhysics::SystemEvents::OnConfigurationChangedEvent::Handler m_physicsSystemConfigChanged; static thread_local AZStd::vector s_rayCastBuffer; //!< thread local structure to hold hits for a single raycast or shapecast. diff --git a/Gems/PhysX/Code/Source/Scene/PhysXSceneInterface.cpp b/Gems/PhysX/Code/Source/Scene/PhysXSceneInterface.cpp index 3b3ab2f0f8..9aeafb1931 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXSceneInterface.cpp +++ b/Gems/PhysX/Code/Source/Scene/PhysXSceneInterface.cpp @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -144,6 +145,36 @@ namespace PhysX } } + AzPhysics::JointHandle PhysXSceneInterface::AddJoint( + AzPhysics::SceneHandle sceneHandle, const AzPhysics::JointConfiguration* jointConfig, + AzPhysics::SimulatedBodyHandle parentBody, AzPhysics::SimulatedBodyHandle childBody) + { + if (AzPhysics::Scene* scene = m_physxSystem->GetScene(sceneHandle)) + { + return scene->AddJoint(jointConfig, parentBody, childBody); + } + + return AzPhysics::InvalidJointHandle; + } + + AzPhysics::Joint* PhysXSceneInterface::GetJointFromHandle(AzPhysics::SceneHandle sceneHandle, AzPhysics::JointHandle jointHandle) + { + if (AzPhysics::Scene* scene = m_physxSystem->GetScene(sceneHandle)) + { + return scene->GetJointFromHandle(jointHandle); + } + + return nullptr; + } + + void PhysXSceneInterface::RemoveJoint(AzPhysics::SceneHandle sceneHandle, AzPhysics::JointHandle jointHandle) + { + if (AzPhysics::Scene* scene = m_physxSystem->GetScene(sceneHandle)) + { + scene->RemoveJoint(jointHandle); + } + } + AzPhysics::SceneQueryHits PhysXSceneInterface::QueryScene( AzPhysics::SceneHandle sceneHandle, const AzPhysics::SceneQueryRequest* request) { diff --git a/Gems/PhysX/Code/Source/Scene/PhysXSceneInterface.h b/Gems/PhysX/Code/Source/Scene/PhysXSceneInterface.h index 2edfbd8457..b96f74a3f7 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXSceneInterface.h +++ b/Gems/PhysX/Code/Source/Scene/PhysXSceneInterface.h @@ -44,6 +44,10 @@ namespace PhysX void RemoveSimulatedBodies(AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandleList& bodyHandles) override; void EnableSimulationOfBody(AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle bodyHandle) override; void DisableSimulationOfBody(AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle bodyHandle) override; + AzPhysics::JointHandle AddJoint(AzPhysics::SceneHandle sceneHandle, const AzPhysics::JointConfiguration* jointConfig, + AzPhysics::SimulatedBodyHandle parentBody, AzPhysics::SimulatedBodyHandle childBody) override; + AzPhysics::Joint* GetJointFromHandle(AzPhysics::SceneHandle sceneHandle, AzPhysics::JointHandle jointHandle) override; + void RemoveJoint(AzPhysics::SceneHandle sceneHandle, AzPhysics::JointHandle jointHandle) override; AzPhysics::SceneQueryHits QueryScene(AzPhysics::SceneHandle sceneHandle, const AzPhysics::SceneQueryRequest* request) override; AzPhysics::SceneQueryHitsList QuerySceneBatch(AzPhysics::SceneHandle sceneHandle, const AzPhysics::SceneQueryRequests& requests) override; [[nodiscard]] bool QuerySceneAsync(AzPhysics::SceneHandle sceneHandle, AzPhysics::SceneQuery::AsyncRequestId requestId, diff --git a/Gems/PhysX/Code/Source/System/PhysXJointInterface.cpp b/Gems/PhysX/Code/Source/System/PhysXJointInterface.cpp new file mode 100644 index 0000000000..1fa13cffb4 --- /dev/null +++ b/Gems/PhysX/Code/Source/System/PhysXJointInterface.cpp @@ -0,0 +1,299 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or + * a third party where indicated. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include + +namespace PhysX +{ + namespace + { + struct D6JointState + { + float m_swingAngleY; + float m_swingAngleZ; + float m_twistAngle; + }; + + D6JointState CalculateD6JointState( + const AZ::Quaternion& parentWorldRotation, + const AZ::Quaternion& parentLocalRotation, + const AZ::Quaternion& childWorldRotation, + const AZ::Quaternion& childLocalRotation) + { + D6JointState result; + + const AZ::Quaternion parentRotation = parentWorldRotation * parentLocalRotation; + const AZ::Quaternion childRotation = childWorldRotation * childLocalRotation; + const AZ::Quaternion relativeRotation = parentRotation.GetConjugate() * childRotation; + AZ::Quaternion twistQuat = AZ::IsClose(relativeRotation.GetX(), 0.0f, AZ::Constants::FloatEpsilon) + ? AZ::Quaternion::CreateIdentity() + : AZ::Quaternion(relativeRotation.GetX(), 0.0f, 0.0f, relativeRotation.GetW()).GetNormalized(); + AZ::Quaternion swingQuat = relativeRotation * twistQuat.GetConjugate(); + + // make sure the twist angle has the correct sign for the rotation + twistQuat *= AZ::GetSign(twistQuat.GetX()); + // make sure we get the shortest arcs for the swing degrees of freedom + swingQuat *= AZ::GetSign(swingQuat.GetW()); + // the PhysX swing limits work in terms of tan quarter angles + result.m_swingAngleY = 4.0f * atan2f(swingQuat.GetY(), 1.0f + swingQuat.GetW()); + result.m_swingAngleZ = 4.0f * atan2f(swingQuat.GetZ(), 1.0f + swingQuat.GetW()); + const float twistAngle = twistQuat.GetAngle(); + // GetAngle returns an angle in the range 0..2 pi, but the twist limits work in the range -pi..pi + const float wrappedTwistAngle = twistAngle > AZ::Constants::Pi ? twistAngle - AZ::Constants::TwoPi : twistAngle; + result.m_twistAngle = wrappedTwistAngle; + + return result; + } + + bool IsD6SwingValid(float swingAngleY, float swingAngleZ, float swingLimitY, float swingLimitZ) + { + const float epsilon = AZ::Constants::FloatEpsilon; + const float yFactor = tanf(0.25f * swingAngleY) / AZStd::GetMax(epsilon, tanf(0.25f * swingLimitY)); + const float zFactor = tanf(0.25f * swingAngleZ) / AZStd::GetMax(epsilon, tanf(0.25f * swingLimitZ)); + + return (yFactor * yFactor + zFactor * zFactor <= 1.0f + epsilon); + } + + void AppendD6SwingConeToLineBuffer( + const AZ::Quaternion& parentLocalRotation, + float swingAngleY, + float swingAngleZ, + float swingLimitY, + float swingLimitZ, + float scale, + AZ::u32 angularSubdivisions, + AZ::u32 radialSubdivisions, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut) + { + const AZ::u32 numLinesSwingCone = angularSubdivisions * (1u + radialSubdivisions); + lineBufferOut.reserve(lineBufferOut.size() + 2u * numLinesSwingCone); + lineValidityBufferOut.reserve(lineValidityBufferOut.size() + numLinesSwingCone); + + // the orientation quat for a radial line in the cone can be represented in terms of sin and cos half angles + // these expressions can be efficiently calculated using tan quarter angles as follows: + // writing t = tan(x / 4) + // sin(x / 2) = 2 * t / (1 + t * t) + // cos(x / 2) = (1 - t * t) / (1 + t * t) + const float tanQuarterSwingZ = tanf(0.25f * swingLimitZ); + const float tanQuarterSwingY = tanf(0.25f * swingLimitY); + + AZ::Vector3 previousRadialVector = AZ::Vector3::CreateZero(); + for (AZ::u32 angularIndex = 0; angularIndex <= angularSubdivisions; angularIndex++) + { + const float angle = AZ::Constants::TwoPi / angularSubdivisions * angularIndex; + // the axis about which to rotate the x-axis to get the radial vector for this segment of the cone + const AZ::Vector3 rotationAxis(0, -tanQuarterSwingY * sinf(angle), tanQuarterSwingZ * cosf(angle)); + const float normalizationFactor = rotationAxis.GetLengthSq(); + const AZ::Quaternion radialVectorRotation = 1.0f / (1.0f + normalizationFactor) * + AZ::Quaternion::CreateFromVector3AndValue(2.0f * rotationAxis, 1.0f - normalizationFactor); + const AZ::Vector3 radialVector = + (parentLocalRotation * radialVectorRotation).TransformVector(AZ::Vector3::CreateAxisX(scale)); + + if (angularIndex > 0) + { + for (AZ::u32 radialIndex = 1; radialIndex <= radialSubdivisions; radialIndex++) + { + float radiusFraction = 1.0f / radialSubdivisions * radialIndex; + lineBufferOut.push_back(radiusFraction * radialVector); + lineBufferOut.push_back(radiusFraction * previousRadialVector); + } + } + + if (angularIndex < angularSubdivisions) + { + lineBufferOut.push_back(AZ::Vector3::CreateZero()); + lineBufferOut.push_back(radialVector); + } + + previousRadialVector = radialVector; + } + + const bool swingValid = IsD6SwingValid(swingAngleY, swingAngleZ, swingLimitY, swingLimitZ); + lineValidityBufferOut.insert(lineValidityBufferOut.end(), numLinesSwingCone, swingValid); + } + + void AppendD6TwistArcToLineBuffer( + const AZ::Quaternion& parentLocalRotation, + float twistAngle, + float twistLimitLower, + float twistLimitUpper, + float scale, + AZ::u32 angularSubdivisions, + AZ::u32 radialSubdivisions, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut) + { + const AZ::u32 numLinesTwistArc = angularSubdivisions * (1u + radialSubdivisions) + 1u; + lineBufferOut.reserve(lineBufferOut.size() + 2u * numLinesTwistArc); + + AZ::Vector3 previousRadialVector = AZ::Vector3::CreateZero(); + const float twistRange = twistLimitUpper - twistLimitLower; + + for (AZ::u32 angularIndex = 0; angularIndex <= angularSubdivisions; angularIndex++) + { + const float angle = twistLimitLower + twistRange / angularSubdivisions * angularIndex; + const AZ::Vector3 radialVector = parentLocalRotation.TransformVector(scale * AZ::Vector3(0.0f, cosf(angle), sinf(angle))); + + if (angularIndex > 0) + { + for (AZ::u32 radialIndex = 1; radialIndex <= radialSubdivisions; radialIndex++) + { + const float radiusFraction = 1.0f / radialSubdivisions * radialIndex; + lineBufferOut.push_back(radiusFraction * radialVector); + lineBufferOut.push_back(radiusFraction * previousRadialVector); + } + } + + lineBufferOut.push_back(AZ::Vector3::CreateZero()); + lineBufferOut.push_back(radialVector); + + previousRadialVector = radialVector; + } + + const bool twistValid = (twistAngle >= twistLimitLower && twistAngle <= twistLimitUpper); + lineValidityBufferOut.insert(lineValidityBufferOut.end(), numLinesTwistArc, twistValid); + } + + void AppendD6CurrentTwistToLineBuffer( + const AZ::Quaternion& parentLocalRotation, + float twistAngle, + [[maybe_unused]] float twistLimitLower, + [[maybe_unused]] float twistLimitUpper, + float scale, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut) + { + const AZ::Vector3 twistVector = + parentLocalRotation.TransformVector(1.25f * scale * AZ::Vector3(0.0f, cosf(twistAngle), sinf(twistAngle))); + lineBufferOut.push_back(AZ::Vector3::CreateZero()); + lineBufferOut.push_back(twistVector); + lineValidityBufferOut.push_back(true); + } + + template + AZStd::unique_ptr ConfigurationFactory( + const AZ::Quaternion& parentLocalRotation, const AZ::Quaternion& childLocalRotation) + { + auto jointConfig = AZStd::make_unique(); + jointConfig->m_childLocalRotation = childLocalRotation; + jointConfig->m_parentLocalRotation = parentLocalRotation; + + return jointConfig; + } + + } // namespace + + const AZStd::vector PhysXJointHelpersInterface::GetSupportedJointTypeIds() const + { + static AZStd::vector jointTypes = { + D6JointLimitConfiguration::RTTI_Type(), + FixedJointConfiguration::RTTI_Type(), + BallJointConfiguration::RTTI_Type(), + HingeJointConfiguration::RTTI_Type() + }; + return jointTypes; + } + + AZStd::optional PhysXJointHelpersInterface::GetSupportedJointTypeId(AzPhysics::JointType typeEnum) const + { + switch (typeEnum) + { + case AzPhysics::JointType::D6Joint: + return azrtti_typeid(); + case AzPhysics::JointType::FixedJoint: + return azrtti_typeid(); + case AzPhysics::JointType::BallJoint: + return azrtti_typeid(); + case AzPhysics::JointType::HingeJoint: + return azrtti_typeid(); + default: + AZ_Warning("PhysX Joint Utils", false, "Unsupported joint type in GetSupportedJointTypeId"); + } + return AZStd::nullopt; + } + + AZStd::unique_ptr PhysXJointHelpersInterface::ComputeInitialJointLimitConfiguration( + const AZ::TypeId& jointLimitTypeId, + const AZ::Quaternion& parentWorldRotation, + const AZ::Quaternion& childWorldRotation, + const AZ::Vector3& axis, + [[maybe_unused]] const AZStd::vector& exampleLocalRotations) + { + const AZ::Vector3& normalizedAxis = axis.IsZero() ? AZ::Vector3::CreateAxisX() : axis.GetNormalized(); + const AZ::Quaternion childLocalRotation = AZ::Quaternion::CreateShortestArc( + AZ::Vector3::CreateAxisX(), childWorldRotation.GetConjugate().TransformVector(normalizedAxis)); + const AZ::Quaternion parentLocalRotation = parentWorldRotation.GetConjugate() * childWorldRotation * childLocalRotation; + + if (jointLimitTypeId == azrtti_typeid()) + { + return ConfigurationFactory(parentLocalRotation, childLocalRotation); + } + else if (jointLimitTypeId == azrtti_typeid()) + { + return ConfigurationFactory(parentLocalRotation, childLocalRotation); + } + else if (jointLimitTypeId == azrtti_typeid()) + { + return ConfigurationFactory(parentLocalRotation, childLocalRotation); + } + else if (jointLimitTypeId == azrtti_typeid()) + { + return ConfigurationFactory(parentLocalRotation, childLocalRotation); + } + + AZ_Warning("PhysX Joint Utils", false, "Unsupported joint type in ComputeInitialJointLimitConfiguration"); + return nullptr; + } + + void PhysXJointHelpersInterface::GenerateJointLimitVisualizationData( + const AzPhysics::JointConfiguration& configuration, + const AZ::Quaternion& parentRotation, + const AZ::Quaternion& childRotation, + float scale, + AZ::u32 angularSubdivisions, + AZ::u32 radialSubdivisions, + [[maybe_unused]] AZStd::vector& vertexBufferOut, + [[maybe_unused]] AZStd::vector& indexBufferOut, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut) + { + if (const auto d6JointConfiguration = azrtti_cast(&configuration)) + { + const AZ::u32 angularSubdivisionsClamped = AZ::GetClamp(angularSubdivisions, 4u, 32u); + const AZ::u32 radialSubdivisionsClamped = AZ::GetClamp(radialSubdivisions, 1u, 4u); + + const D6JointState jointState = CalculateD6JointState( + parentRotation, d6JointConfiguration->m_parentLocalRotation, childRotation, d6JointConfiguration->m_childLocalRotation); + const float swingAngleY = jointState.m_swingAngleY; + const float swingAngleZ = jointState.m_swingAngleZ; + const float twistAngle = jointState.m_twistAngle; + const float swingLimitY = AZ::DegToRad(d6JointConfiguration->m_swingLimitY); + const float swingLimitZ = AZ::DegToRad(d6JointConfiguration->m_swingLimitZ); + const float twistLimitLower = AZ::DegToRad(d6JointConfiguration->m_twistLimitLower); + const float twistLimitUpper = AZ::DegToRad(d6JointConfiguration->m_twistLimitUpper); + + AppendD6SwingConeToLineBuffer( + d6JointConfiguration->m_parentLocalRotation, swingAngleY, swingAngleZ, swingLimitY, swingLimitZ, scale, + angularSubdivisionsClamped, radialSubdivisionsClamped, lineBufferOut, lineValidityBufferOut); + AppendD6TwistArcToLineBuffer( + d6JointConfiguration->m_parentLocalRotation, twistAngle, twistLimitLower, twistLimitUpper, scale, + angularSubdivisionsClamped, radialSubdivisionsClamped, lineBufferOut, lineValidityBufferOut); + AppendD6CurrentTwistToLineBuffer( + d6JointConfiguration->m_parentLocalRotation, twistAngle, twistLimitLower, twistLimitUpper, scale, lineBufferOut, + lineValidityBufferOut); + } + } +} diff --git a/Gems/PhysX/Code/Source/System/PhysXJointInterface.h b/Gems/PhysX/Code/Source/System/PhysXJointInterface.h new file mode 100644 index 0000000000..1cee865e8b --- /dev/null +++ b/Gems/PhysX/Code/Source/System/PhysXJointInterface.h @@ -0,0 +1,52 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or + * a third party where indicated. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once + +#include +#include + +namespace AzPhysics +{ + struct JointConfiguration; +} + +namespace PhysX +{ + class PhysXJointHelpersInterface + : public AZ::Interface::Registrar + { + public: + AZ_RTTI(PhysX::PhysXJointHelpersInterface, "{48AC5137-2226-4C57-8E4C-FCF3C1965252}", AzPhysics::JointHelpersInterface); + + const AZStd::vector GetSupportedJointTypeIds() const override; + AZStd::optional GetSupportedJointTypeId(AzPhysics::JointType typeEnum) const override; + + AZStd::unique_ptr ComputeInitialJointLimitConfiguration( + const AZ::TypeId& jointLimitTypeId, + const AZ::Quaternion& parentWorldRotation, + const AZ::Quaternion& childWorldRotation, + const AZ::Vector3& axis, + const AZStd::vector& exampleLocalRotations) override; + + void GenerateJointLimitVisualizationData( + const AzPhysics::JointConfiguration& configuration, + const AZ::Quaternion& parentRotation, + const AZ::Quaternion& childRotation, + float scale, + AZ::u32 angularSubdivisions, + AZ::u32 radialSubdivisions, + AZStd::vector& vertexBufferOut, + AZStd::vector& indexBufferOut, + AZStd::vector& lineBufferOut, + AZStd::vector& lineValidityBufferOut) override; + }; +} diff --git a/Gems/PhysX/Code/Source/System/PhysXSystem.h b/Gems/PhysX/Code/Source/System/PhysXSystem.h index 533685bbd1..60f72c6145 100644 --- a/Gems/PhysX/Code/Source/System/PhysXSystem.h +++ b/Gems/PhysX/Code/Source/System/PhysXSystem.h @@ -25,6 +25,7 @@ #include #include +#include namespace physx { @@ -128,6 +129,7 @@ namespace PhysX Debug::PhysXDebug m_physXDebug; //! Handler for the PhysXDebug Interface. PhysXSettingsRegistryManager& m_registryManager; //! Handles all settings registry interactions. PhysXSceneInterface m_sceneInterface; //! Implemented the Scene Az::Interface. + PhysXJointHelpersInterface m_jointHelperInterface; //! Implementation of the JointHelpersInterface. class MaterialLibraryAssetHelper : private AZ::Data::AssetBus::Handler diff --git a/Gems/PhysX/Code/Source/SystemComponent.cpp b/Gems/PhysX/Code/Source/SystemComponent.cpp index eae56967b2..6dcf3951f4 100644 --- a/Gems/PhysX/Code/Source/SystemComponent.cpp +++ b/Gems/PhysX/Code/Source/SystemComponent.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -93,7 +92,6 @@ namespace PhysX void SystemComponent::Reflect(AZ::ReflectContext* context) { - D6JointLimitConfiguration::Reflect(context); Pipeline::MeshAsset::Reflect(context); PhysX::ReflectionUtils::ReflectPhysXOnlyApi(context); @@ -347,49 +345,6 @@ namespace PhysX return AZStd::make_shared(materialConfiguration); } - AZStd::vector SystemComponent::GetSupportedJointTypes() - { - return JointUtils::GetSupportedJointTypes(); - } - - AZStd::shared_ptr SystemComponent::CreateJointLimitConfiguration(AZ::TypeId jointType) - { - return JointUtils::CreateJointLimitConfiguration(jointType); - } - - AZStd::shared_ptr SystemComponent::CreateJoint(const AZStd::shared_ptr& configuration, - AzPhysics::SimulatedBody* parentBody, AzPhysics::SimulatedBody* childBody) - { - return JointUtils::CreateJoint(configuration, parentBody, childBody); - } - - void SystemComponent::GenerateJointLimitVisualizationData( - const Physics::JointLimitConfiguration& configuration, - const AZ::Quaternion& parentRotation, - const AZ::Quaternion& childRotation, - float scale, - AZ::u32 angularSubdivisions, - AZ::u32 radialSubdivisions, - AZStd::vector& vertexBufferOut, - AZStd::vector& indexBufferOut, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut) - { - JointUtils::GenerateJointLimitVisualizationData(configuration, parentRotation, childRotation, scale, - angularSubdivisions, radialSubdivisions, vertexBufferOut, indexBufferOut, lineBufferOut, lineValidityBufferOut); - } - - AZStd::unique_ptr SystemComponent::ComputeInitialJointLimitConfiguration( - const AZ::TypeId& jointLimitTypeId, - const AZ::Quaternion& parentWorldRotation, - const AZ::Quaternion& childWorldRotation, - const AZ::Vector3& axis, - const AZStd::vector& exampleLocalRotations) - { - return JointUtils::ComputeInitialJointLimitConfiguration(jointLimitTypeId, parentWorldRotation, - childWorldRotation, axis, exampleLocalRotations); - } - void SystemComponent::ReleaseNativeMeshObject(void* nativeMeshObject) { if (nativeMeshObject) diff --git a/Gems/PhysX/Code/Source/SystemComponent.h b/Gems/PhysX/Code/Source/SystemComponent.h index 4609fde0ce..aab5c5a8e9 100644 --- a/Gems/PhysX/Code/Source/SystemComponent.h +++ b/Gems/PhysX/Code/Source/SystemComponent.h @@ -115,28 +115,6 @@ namespace PhysX AZStd::shared_ptr CreateShape(const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& configuration) override; AZStd::shared_ptr CreateMaterial(const Physics::MaterialConfiguration& materialConfiguration) override; - AZStd::vector GetSupportedJointTypes() override; - AZStd::shared_ptr CreateJointLimitConfiguration(AZ::TypeId jointType) override; - AZStd::shared_ptr CreateJoint(const AZStd::shared_ptr& configuration, - AzPhysics::SimulatedBody* parentBody, AzPhysics::SimulatedBody* childBody) override; - void GenerateJointLimitVisualizationData( - const Physics::JointLimitConfiguration& configuration, - const AZ::Quaternion& parentRotation, - const AZ::Quaternion& childRotation, - float scale, - AZ::u32 angularSubdivisions, - AZ::u32 radialSubdivisions, - AZStd::vector& vertexBufferOut, - AZStd::vector& indexBufferOut, - AZStd::vector& lineBufferOut, - AZStd::vector& lineValidityBufferOut) override; - AZStd::unique_ptr ComputeInitialJointLimitConfiguration( - const AZ::TypeId& jointLimitTypeId, - const AZ::Quaternion& parentWorldRotation, - const AZ::Quaternion& childWorldRotation, - const AZ::Vector3& axis, - const AZStd::vector& exampleLocalRotations) override; - void ReleaseNativeMeshObject(void* nativeMeshObject) override; // Assets related data diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index 270a352fc0..cb28358213 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -40,9 +40,9 @@ #include #include #include -#include #include #include +#include namespace PhysX { @@ -1394,8 +1394,12 @@ namespace PhysX ForceRegionBusBehaviorHandler::Reflect(context); - GenericJointConfiguration::Reflect(context); - GenericJointLimitsConfiguration::Reflect(context); + D6JointLimitConfiguration::Reflect(context); + JointGenericProperties::Reflect(context); + JointLimitProperties::Reflect(context); + FixedJointConfiguration::Reflect(context); + BallJointConfiguration::Reflect(context); + HingeJointConfiguration::Reflect(context); } void ForceRegionBusBehaviorHandler::Reflect(AZ::ReflectContext* context) diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksUtilities.h b/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksUtilities.h index 0971226644..5a54ab5833 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksUtilities.h +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksUtilities.h @@ -24,6 +24,7 @@ namespace AzPhysics { class Scene; + struct RigidBody; } namespace PhysX::Benchmarks diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXJointBenchmarks.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXJointBenchmarks.cpp index 24b51f0085..e1ebed18ee 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXJointBenchmarks.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXJointBenchmarks.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include namespace PhysX::Benchmarks { @@ -102,7 +102,7 @@ namespace PhysX::Benchmarks { AzPhysics::RigidBody* m_parent; AzPhysics::SimulatedBody* m_child; - AZStd::shared_ptr m_joint; + AzPhysics::JointHandle m_jointHandle; }; //! Structure to hold the upper and lower twist limits //! used with Utils::CreateJoints GenerateTwistLimitsFuncPtr @@ -122,13 +122,11 @@ namespace PhysX::Benchmarks //! Helper function to add the required number of joints to the provided world //! @param numJoints, the requested number of joints to spawn - //! @param system, current active physics system //! @param scene, the physics scene to spawn the joints and their world bodies into //! @param parentPositionGenerator, [optional] function pointer to allow caller to pick the spawn position of the parent body //! @param childPositionGenerator, [optional] function pointer to allow caller to pick the spawn position of the child body //! @param GenerateTwistLimitsFuncPtr, [optional] function pointer to allow caller to pick the twist limits of the joint AZStd::vector CreateJoints(int numJoints, - Physics::System* system, AzPhysics::Scene* scene, GenerateSpawnPositionFuncPtr* parentPositionGenerator = nullptr, GenerateSpawnPositionFuncPtr* childPositionGenerator = nullptr, @@ -173,18 +171,18 @@ namespace PhysX::Benchmarks AzPhysics::SimulatedBodyHandle staticRigidBodyHandle = scene->AddSimulatedBody(&staticRigidBodyConfig); newJoint.m_child = scene->GetSimulatedBodyFromHandle(staticRigidBodyHandle); - AZStd::shared_ptr config = AZStd::make_shared(); + PhysX::D6JointLimitConfiguration config; TwistLimits limits(JointConstants::CreateJointDefaults::UpperLimit, JointConstants::CreateJointDefaults::LowerLimit); if (twistLimitsGenerator) { limits = (*twistLimitsGenerator)(i); } - config->m_twistLimitUpper = limits.m_upperLimit; - config->m_twistLimitLower = limits.m_lowerLimit; - config->m_swingLimitY = 1.0f; - config->m_swingLimitZ = 1.0f; - newJoint.m_joint = system->CreateJoint(config, newJoint.m_parent, newJoint.m_child); + config.m_twistLimitUpper = limits.m_upperLimit; + config.m_twistLimitLower = limits.m_lowerLimit; + config.m_swingLimitY = 1.0f; + config.m_swingLimitZ = 1.0f; + newJoint.m_jointHandle = scene->AddJoint(&config, newJoint.m_parent->m_bodyHandle, newJoint.m_child->m_bodyHandle); joints.emplace_back(AZStd::move(newJoint)); } @@ -201,8 +199,6 @@ namespace PhysX::Benchmarks virtual void SetUp([[maybe_unused]] const ::benchmark::State &state) override { PhysXBaseBenchmarkFixture::SetUpInternal(); - //need to get the Physics::System to be able to spawn the rigid bodies - m_system = AZ::Interface::Get(); } virtual void TearDown([[maybe_unused]] const ::benchmark::State &state) override @@ -218,8 +214,6 @@ namespace PhysX::Benchmarks return sceneConfig; } // PhysXBaseBenchmarkFixture Interface --------- - - Physics::System *m_system; }; //! BM_Joints_AtRest - This test will spawn the requested number of joints @@ -237,7 +231,7 @@ namespace PhysX::Benchmarks return position; }; - AZStd::vector joinGroups = Utils::CreateJoints(aznumeric_cast(state.range(0)), m_system, m_defaultScene, + AZStd::vector joinGroups = Utils::CreateJoints(aznumeric_cast(state.range(0)), m_defaultScene, &parentPosGenerator, &childPosGenerator); //setup the sub tick tracker @@ -260,6 +254,11 @@ namespace PhysX::Benchmarks } subTickTracker.Stop(); + for (const auto& jointGroup : joinGroups) + { + m_defaultScene->RemoveJoint(jointGroup.m_jointHandle); + } + //sort the frame times and get the P50, P90, P99 percentiles Utils::ReportFramePercentileCounters(state, tickTimes, subTickTracker.GetSubTickTimes()); Utils::ReportFrameStandardDeviationAndMeanCounters(state, tickTimes, subTickTracker.GetSubTickTimes()); @@ -287,7 +286,7 @@ namespace PhysX::Benchmarks return Utils::TwistLimits(JointConstants::JointSettings::SwingingJointUpperLimit, JointConstants::JointSettings::SwingingJointLowerLimit); }; - AZStd::vector joinGroups = Utils::CreateJoints(aznumeric_cast(state.range(0)), m_system, m_defaultScene, + AZStd::vector joinGroups = Utils::CreateJoints(aznumeric_cast(state.range(0)), m_defaultScene, &parentPosGenerator, &childPosGenerator, &twistGenerator); //setup the sub tick tracker @@ -326,6 +325,11 @@ namespace PhysX::Benchmarks } subTickTracker.Stop(); + for (const auto& jointGroup : joinGroups) + { + m_defaultScene->RemoveJoint(jointGroup.m_jointHandle); + } + //sort the frame times and get the P50, P90, P99 percentiles Utils::ReportFramePercentileCounters(state, tickTimes, subTickTracker.GetSubTickTimes()); Utils::ReportFrameStandardDeviationAndMeanCounters(state, tickTimes, subTickTracker.GetSubTickTimes()); @@ -370,17 +374,18 @@ namespace PhysX::Benchmarks snakeRigidBodies = Utils::GetRigidBodiesFromHandles(m_defaultScene, snakeRigidBodyHandles); //build the snake - AZStd::vector> joints; - AZStd::shared_ptr config = AZStd::make_shared(); - config->m_twistLimitUpper = JointConstants::CreateJointDefaults::UpperLimit; - config->m_twistLimitLower = JointConstants::CreateJointDefaults::LowerLimit; - config->m_swingLimitY = 1.0f; - config->m_swingLimitZ = 1.0f; + AZStd::vector jointHandles; + PhysX::D6JointLimitConfiguration config; + config.m_twistLimitUpper = JointConstants::CreateJointDefaults::UpperLimit; + config.m_twistLimitLower = JointConstants::CreateJointDefaults::LowerLimit; + config.m_swingLimitY = 1.0f; + config.m_swingLimitZ = 1.0f; //build the head - joints.emplace_back(m_system->CreateJoint(config, snakeRigidBodies[0], snakeHead)); + jointHandles.emplace_back(m_defaultScene->AddJoint(&config, snakeRigidBodies[0]->m_bodyHandle, snakeHead->m_bodyHandle)); for (size_t i = 0; (i+1) < snakeRigidBodies.size(); i++) { - joints.emplace_back(m_system->CreateJoint(config, snakeRigidBodies[i + 1], snakeRigidBodies[i])); + jointHandles.emplace_back( + m_defaultScene->AddJoint(&config, snakeRigidBodies[i + 1]->m_bodyHandle, snakeRigidBodies[i]->m_bodyHandle)); } //setup the sub tick tracker @@ -404,6 +409,10 @@ namespace PhysX::Benchmarks subTickTracker.Stop(); m_defaultScene->RemoveSimulatedBodies(snakeRigidBodyHandles); + for (const auto& jointHandle : jointHandles) + { + m_defaultScene->RemoveJoint(jointHandle); + } snakeRigidBodyHandles.clear(); //sort the frame times and get the P50, P90, P99 percentiles diff --git a/Gems/PhysX/Code/Tests/PhysXGenericTestFixture.h b/Gems/PhysX/Code/Tests/PhysXGenericTestFixture.h index 80b338150f..1a1eefab46 100644 --- a/Gems/PhysX/Code/Tests/PhysXGenericTestFixture.h +++ b/Gems/PhysX/Code/Tests/PhysXGenericTestFixture.h @@ -111,7 +111,7 @@ namespace PhysX }; class GenericPhysicsInterfaceTest - : protected GenericPhysicsFixture + : public GenericPhysicsFixture , public testing::Test { public: diff --git a/Gems/PhysX/Code/Tests/PhysXJointsTest.cpp b/Gems/PhysX/Code/Tests/PhysXJointsTest.cpp index b976bbe77a..7d691d02a6 100644 --- a/Gems/PhysX/Code/Tests/PhysXJointsTest.cpp +++ b/Gems/PhysX/Code/Tests/PhysXJointsTest.cpp @@ -18,17 +18,18 @@ #include #include -#include #include #include #include #include +#include #include #include #include #include #include +#include namespace PhysX { @@ -38,8 +39,9 @@ namespace PhysX AZStd::unique_ptr AddBodyColliderEntity( AzPhysics::SceneHandle sceneHandle, const AZ::Vector3& position, const AZ::Vector3& initialLinearVelocity, - AZStd::shared_ptr jointConfig = nullptr, - AZStd::shared_ptr jointLimitsConfig = nullptr) + AZStd::shared_ptr jointConfig = nullptr, + AZStd::shared_ptr jointGenericProperties = nullptr, + AZStd::shared_ptr jointLimitProperties = nullptr) { const char* entityName = "testEntity"; auto entity = AZStd::make_unique(entityName); @@ -68,10 +70,12 @@ namespace PhysX { jointConfig->m_followerEntity = entity->GetId(); - GenericJointLimitsConfiguration defaultJointLimitsConfig; + JointGenericProperties defaultJointGenericProperties; + JointLimitProperties defaultJointLimitProperties; entity->CreateComponent( *jointConfig, - (jointLimitsConfig)? *jointLimitsConfig : defaultJointLimitsConfig); + (jointGenericProperties)? *jointGenericProperties : defaultJointGenericProperties, + (jointLimitProperties)? *jointLimitProperties : defaultJointLimitProperties); } entity->Init(); @@ -114,7 +118,7 @@ namespace PhysX leadPosition, leadInitialLinearVelocity); - auto jointConfig = AZStd::make_shared(); + auto jointConfig = AZStd::make_shared(); jointConfig->m_leadEntity = leadEntity->GetId(); jointConfig->m_localTransformFromFollower = jointLocalTransform; @@ -150,17 +154,18 @@ namespace PhysX leadPosition, leadInitialLinearVelocity); - auto jointConfig = AZStd::make_shared(); + auto jointConfig = AZStd::make_shared(); jointConfig->m_leadEntity = leadEntity->GetId(); jointConfig->m_localTransformFromFollower = jointLocalTransform; - auto jointLimits = AZStd::make_shared (); + auto jointLimits = AZStd::make_shared (); jointLimits->m_isLimited = false; auto followerEntity = AddBodyColliderEntity(m_testSceneHandle, followerPosition, followerInitialLinearVelocity, jointConfig, + nullptr, jointLimits); const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId()); @@ -191,21 +196,104 @@ namespace PhysX leadPosition, leadInitialLinearVelocity); - auto jointConfig = AZStd::make_shared(); + auto jointConfig = AZStd::make_shared(); jointConfig->m_leadEntity = leadEntity->GetId(); jointConfig->m_localTransformFromFollower = jointLocalTransform; - auto jointLimits = AZStd::make_shared (); + auto jointLimits = AZStd::make_shared (); jointLimits->m_isLimited = false; auto followerEntity = AddBodyColliderEntity(m_testSceneHandle, followerPosition, followerInitialLinearVelocity, jointConfig, + nullptr, jointLimits); const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId()); EXPECT_TRUE(followerEndPosition.GetZ() > followerPosition.GetZ()); } + +// for some reason TYPED_TEST_CASE with the fixture is not working on Android + Linux +#ifdef ENABLE_JOINTS_TYPED_TEST_CASE + template + class PhysXJointsApiTest : public PhysX::GenericPhysicsInterfaceTest + { + public: + + void SetUp() override + { + PhysX::GenericPhysicsInterfaceTest::SetUp(); + + if (auto* sceneInterface = AZ::Interface::Get()) + { + AzPhysics::RigidBodyConfiguration parentConfiguration; + AzPhysics::RigidBodyConfiguration childConfiguration; + + auto colliderConfig = AZStd::make_shared(); + auto shapeConfiguration = AZStd::make_shared(AZ::Vector3(1.0f, 1.0f, 1.0f)); + + parentConfiguration.m_colliderAndShapeData = AzPhysics::ShapeColliderPair(colliderConfig, shapeConfiguration); + childConfiguration.m_colliderAndShapeData = AzPhysics::ShapeColliderPair(colliderConfig, shapeConfiguration); + + // Put the child body a bit to the lower side of X to avoid it colliding with parent + childConfiguration.m_position.SetX(childConfiguration.m_position.GetX() - 2.0f); + m_childInitialPos = childConfiguration.m_position; + parentConfiguration.m_initialLinearVelocity.SetX(10.0f); + + m_parentBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &parentConfiguration); + m_childBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &childConfiguration); + } + } + + void TearDown() override + { + if (auto* sceneInterface = AZ::Interface::Get()) + { + sceneInterface->RemoveSimulatedBody(m_testSceneHandle, m_parentBodyHandle); + sceneInterface->RemoveSimulatedBody(m_testSceneHandle, m_childBodyHandle); + } + + PhysX::GenericPhysicsInterfaceTest::TearDown(); + } + + AzPhysics::SimulatedBodyHandle m_parentBodyHandle = AzPhysics::InvalidJointHandle; + AzPhysics::SimulatedBodyHandle m_childBodyHandle = AzPhysics::InvalidJointHandle; + AZ::Vector3 m_childInitialPos; + }; + + using JointTypes = testing::Types< + D6JointLimitConfiguration, + FixedJointConfiguration, + BallJointConfiguration, + HingeJointConfiguration>; + TYPED_TEST_CASE(PhysXJointsApiTest, JointTypes); + + TYPED_TEST(PhysXJointsApiTest, Joint_ChildFollowsParent) + { + TypeParam jointConfiguration; + AzPhysics::JointHandle jointHandle = AzPhysics::InvalidJointHandle; + + if (auto* sceneInterface = AZ::Interface::Get()) + { + jointHandle = sceneInterface->AddJoint(m_testSceneHandle, &jointConfiguration, m_parentBodyHandle, m_childBodyHandle); + } + + EXPECT_NE(jointHandle, AzPhysics::InvalidJointHandle); + + // run physics to trigger the the move of parent body + TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 1); + + AZ::Vector3 childCurrentPos; + + if (auto* sceneInterface = AZ::Interface::Get()) + { + auto* childBody = sceneInterface->GetSimulatedBodyFromHandle(m_testSceneHandle, m_childBodyHandle); + childCurrentPos = childBody->GetPosition(); + } + + EXPECT_GT(childCurrentPos.GetX(), m_childInitialPos.GetX()); + } +#endif // ENABLE_JOINTS_TYPED_TEST_CASE } diff --git a/Gems/PhysX/Code/Tests/RagdollConfiguration.xml b/Gems/PhysX/Code/Tests/RagdollConfiguration.xml index e1c2c5bed9..b3a268a14b 100644 --- a/Gems/PhysX/Code/Tests/RagdollConfiguration.xml +++ b/Gems/PhysX/Code/Tests/RagdollConfiguration.xml @@ -4,7 +4,7 @@ - + @@ -27,9 +27,9 @@ - - - + + + @@ -42,7 +42,7 @@ - + @@ -65,9 +65,9 @@ - - - + + + @@ -80,7 +80,7 @@ - + @@ -103,9 +103,9 @@ - - - + + + @@ -118,7 +118,7 @@ - + @@ -141,9 +141,9 @@ - - - + + + @@ -156,7 +156,7 @@ - + @@ -179,9 +179,9 @@ - - - + + + @@ -194,7 +194,7 @@ - + @@ -217,9 +217,9 @@ - - - + + + @@ -232,7 +232,7 @@ - + @@ -255,9 +255,9 @@ - - - + + + @@ -270,7 +270,7 @@ - + @@ -293,9 +293,9 @@ - - - + + + @@ -308,7 +308,7 @@ - + @@ -331,9 +331,9 @@ - - - + + + @@ -346,7 +346,7 @@ - + @@ -369,9 +369,9 @@ - - - + + + @@ -384,7 +384,7 @@ - + @@ -407,9 +407,9 @@ - - - + + + @@ -422,7 +422,7 @@ - + @@ -445,9 +445,9 @@ - - - + + + @@ -460,7 +460,7 @@ - + @@ -483,9 +483,9 @@ - - - + + + @@ -498,7 +498,7 @@ - + @@ -521,9 +521,9 @@ - - - + + + @@ -536,7 +536,7 @@ - + @@ -559,9 +559,9 @@ - - - + + + @@ -574,7 +574,7 @@ - + @@ -597,9 +597,9 @@ - - - + + + @@ -612,7 +612,7 @@ - + @@ -635,9 +635,9 @@ - - - + + + @@ -650,7 +650,7 @@ - + @@ -673,9 +673,9 @@ - - - + + + @@ -688,7 +688,7 @@ - + @@ -711,9 +711,9 @@ - - - + + + @@ -726,7 +726,7 @@ - + @@ -749,9 +749,9 @@ - - - + + + @@ -764,7 +764,7 @@ - + @@ -787,9 +787,9 @@ - - - + + + @@ -802,7 +802,7 @@ - + @@ -825,9 +825,9 @@ - - - + + + diff --git a/Gems/PhysX/Code/Tests/RagdollTests.cpp b/Gems/PhysX/Code/Tests/RagdollTests.cpp index 477e754d74..816e7e2884 100644 --- a/Gems/PhysX/Code/Tests/RagdollTests.cpp +++ b/Gems/PhysX/Code/Tests/RagdollTests.cpp @@ -208,8 +208,8 @@ namespace PhysX } else { - EXPECT_EQ(joint->GetChildBody(), &node->GetRigidBody()); - EXPECT_EQ(joint->GetParentBody(), &ragdoll->GetNode(parentIndex)->GetRigidBody()); + EXPECT_EQ(joint->GetChildBodyHandle(), node->GetRigidBody().m_bodyHandle); + EXPECT_EQ(joint->GetParentBodyHandle(), ragdoll->GetNode(parentIndex)->GetRigidBody().m_bodyHandle); } } } diff --git a/Gems/PhysX/Code/physx_files.cmake b/Gems/PhysX/Code/physx_files.cmake index 24aa42d62a..3f5589242a 100644 --- a/Gems/PhysX/Code/physx_files.cmake +++ b/Gems/PhysX/Code/physx_files.cmake @@ -75,8 +75,6 @@ set(FILES Source/Shape.cpp Source/Material.cpp Source/Material.h - Source/Joint.cpp - Source/Joint.h Source/ForceRegionForces.cpp Source/ForceRegionForces.h Source/ForceRegion.cpp @@ -104,6 +102,7 @@ set(FILES Include/PhysX/Debug/PhysXDebugConfiguration.h Include/PhysX/Debug/PhysXDebugInterface.h Include/PhysX/Configuration/PhysXConfiguration.h + Include/PhysX/Joint/Configuration/PhysXJointConfiguration.h Source/Common/PhysXSceneQueryHelpers.h Source/Common/PhysXSceneQueryHelpers.cpp Source/Configuration/PhysXConfiguration.cpp @@ -112,6 +111,11 @@ set(FILES Source/Debug/PhysXDebug.h Source/Debug/PhysXDebug.cpp Source/Debug/Configuration/PhysXDebugConfiguration.cpp + Source/Joint/PhysXJoint.h + Source/Joint/PhysXJoint.cpp + Source/Joint/PhysXJointUtils.h + Source/Joint/PhysXJointUtils.cpp + Source/Joint/Configuration/PhysXJointConfiguration.cpp Source/Scene/PhysXScene.h Source/Scene/PhysXScene.cpp Source/Scene/PhysXSceneInterface.h @@ -128,6 +132,8 @@ set(FILES Source/System/PhysXCpuDispatcher.h Source/System/PhysXJob.cpp Source/System/PhysXJob.h + Source/System/PhysXJointInterface.h + Source/System/PhysXJointInterface.cpp Source/System/PhysXSdkCallbacks.h Source/System/PhysXSdkCallbacks.cpp Source/System/PhysXSystem.h diff --git a/Gems/PhysXDebug/Code/Source/SystemComponent.cpp b/Gems/PhysXDebug/Code/Source/SystemComponent.cpp index c693599a59..3a5499368b 100644 --- a/Gems/PhysXDebug/Code/Source/SystemComponent.cpp +++ b/Gems/PhysXDebug/Code/Source/SystemComponent.cpp @@ -783,7 +783,7 @@ namespace PhysXDebug Physics::RagdollNode* ragdollNode = actorData->GetRagdollNode(); if (ragdollNode) { - const AZStd::shared_ptr& joint = ragdollNode->GetJoint(); + AzPhysics::Joint* joint = ragdollNode->GetJoint(); physx::PxJoint* pxJoint = static_cast(joint->GetNativePointer()); physx::PxTransform jointPose = actor1->getGlobalPose() * pxJoint->getLocalPose(physx::PxJointActorIndex::eACTOR1); if (!m_culling.m_enabled || m_cullingBox.contains(jointPose.p)) diff --git a/Gems/ScriptCanvasPhysics/Code/Tests/ScriptCanvasPhysicsTest.cpp b/Gems/ScriptCanvasPhysics/Code/Tests/ScriptCanvasPhysicsTest.cpp index 07ccdc5011..63b43938e7 100644 --- a/Gems/ScriptCanvasPhysics/Code/Tests/ScriptCanvasPhysicsTest.cpp +++ b/Gems/ScriptCanvasPhysics/Code/Tests/ScriptCanvasPhysicsTest.cpp @@ -100,6 +100,9 @@ namespace ScriptCanvasPhysicsTests void DisableSimulationOfBody( [[maybe_unused]] AzPhysics::SceneHandle sceneHandle, [[maybe_unused]] AzPhysics::SimulatedBodyHandle bodyHandle) override {} + void RemoveJoint( + [[maybe_unused]]AzPhysics::SceneHandle sceneHandle, + [[maybe_unused]] AzPhysics::JointHandle jointHandle) override {} void SuppressCollisionEvents( [[maybe_unused]] AzPhysics::SceneHandle sceneHandle, [[maybe_unused]] const AzPhysics::SimulatedBodyHandle& bodyHandleA, @@ -148,6 +151,10 @@ namespace ScriptCanvasPhysicsTests MOCK_METHOD2(AddSimulatedBodies, AzPhysics::SimulatedBodyHandleList(AzPhysics::SceneHandle sceneHandle, const AzPhysics::SimulatedBodyConfigurationList& simulatedBodyConfigs)); MOCK_METHOD2(GetSimulatedBodyFromHandle, AzPhysics::SimulatedBody* (AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle bodyHandle)); MOCK_METHOD2(GetSimulatedBodiesFromHandle, AzPhysics::SimulatedBodyList(AzPhysics::SceneHandle sceneHandle, const AzPhysics::SimulatedBodyHandleList& bodyHandles)); + MOCK_METHOD4(AddJoint, AzPhysics::JointHandle(AzPhysics::SceneHandle sceneHandle, const AzPhysics::JointConfiguration* jointConfig, + AzPhysics::SimulatedBodyHandle parentBody, AzPhysics::SimulatedBodyHandle childBody)); + MOCK_METHOD2( + GetJointFromHandle, AzPhysics::Joint*(AzPhysics::SceneHandle sceneHandle, AzPhysics::JointHandle jointHandle)); MOCK_CONST_METHOD1(GetGravity, AZ::Vector3(AzPhysics::SceneHandle sceneHandle)); MOCK_METHOD2(RegisterSceneSimulationFinishHandler, void(AzPhysics::SceneHandle sceneHandle, AzPhysics::SceneEvents::OnSceneSimulationFinishHandler& handler)); MOCK_CONST_METHOD2(GetLegacyBody, AzPhysics::SimulatedBody* (AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle handle)); From 8ffd16b0be6ff00dd648518ee09deac28da3d663 Mon Sep 17 00:00:00 2001 From: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> Date: Thu, 17 Jun 2021 18:31:56 +0100 Subject: [PATCH 227/233] Ensure SnapToGrid only appears when GridSnapping is enabled LYN-2302 (#1362) * updates to better support showing/hiding viewport ui when grid snapping is enabled/disabled * connect up editor settings callbacks * minor polish changes * api rename * updates following review feedback --- .../Viewport/ViewportMessages.h | 9 +++++ .../EditorTransformComponentSelection.cpp | 19 ++++++++++- .../EditorTransformComponentSelection.h | 7 ++++ .../Sandbox/Editor/EditorViewportSettings.cpp | 34 +++++++++++++++++++ Code/Sandbox/Editor/EditorViewportSettings.h | 19 +++++++++++ Code/Sandbox/Editor/EditorViewportWidget.cpp | 12 ++++++- Code/Sandbox/Editor/EditorViewportWidget.h | 4 +++ 7 files changed, 102 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h index 85250f2a32..d202735522 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h @@ -210,6 +210,15 @@ namespace AzToolsFramework //! Type to inherit to implement ViewportInteractionRequests. using ViewportInteractionRequestBus = AZ::EBus; + //! An interface to notify when changes to viewport settings have happened. + class ViewportSettingNotifications + { + public: + virtual void OnGridSnappingChanged(bool enabled) = 0; + }; + + using ViewportSettingsNotificationBus = AZ::EBus; + //! Requests to freeze the Viewport Input //! Added to prevent a bug with the legacy CryEngine Viewport code that would //! keep doing raycast tests even when no level is loaded, causing a crash. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index c4f1ae33da..3e16088f0e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -489,6 +489,16 @@ namespace AzToolsFramework return buttonId; } + void SnappingCluster::TrySetVisible(const bool visible) + { + bool snapping = false; + ViewportInteraction::ViewportInteractionRequestBus::EventResult( + snapping, ViewportUi::DefaultViewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::GridSnappingEnabled); + + // show snapping viewport ui only if there are entities selected and snapping is enabled + SetViewportUiClusterVisible(m_clusterId, visible && snapping); + } + // return either center or entity pivot static AZ::Vector3 CalculatePivotTranslation(const AZ::EntityId entityId, const EditorTransformComponentSelectionRequests::Pivot pivot) { @@ -1035,6 +1045,7 @@ namespace AzToolsFramework EditorEntityLockComponentNotificationBus::Router::BusRouterConnect(); EditorManipulatorCommandUndoRedoRequestBus::Handler::BusConnect(entityContextId); EditorContextMenuBus::Handler::BusConnect(); + ViewportInteraction::ViewportSettingsNotificationBus::Handler::BusConnect(ViewportUi::DefaultViewportId); CreateTransformModeSelectionCluster(); CreateSpaceSelectionCluster(); @@ -1058,6 +1069,7 @@ namespace AzToolsFramework m_pivotOverrideFrame.Reset(); + ViewportInteraction::ViewportSettingsNotificationBus::Handler::BusDisconnect(); EditorContextMenuBus::Handler::BusConnect(); EditorManipulatorCommandUndoRedoRequestBus::Handler::BusDisconnect(); EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect(); @@ -3253,7 +3265,7 @@ namespace AzToolsFramework m_didSetSelectedEntities = false; } - SetViewportUiClusterVisible(m_snappingCluster.m_clusterId, m_viewportUiVisible && !m_selectedEntityIds.empty()); + m_snappingCluster.TrySetVisible(m_viewportUiVisible && !m_selectedEntityIds.empty()); RegenerateManipulators(); } @@ -3717,6 +3729,11 @@ namespace AzToolsFramework SetAllViewportUiVisible(true); } + void EditorTransformComponentSelection::OnGridSnappingChanged([[maybe_unused]] const bool enabled) + { + m_snappingCluster.TrySetVisible(m_viewportUiVisible && !m_selectedEntityIds.empty()); + } + namespace ETCS { // little raii wrapper to switch a value from true to false and back diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h index db96d91911..00c2cb4e50 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h @@ -131,6 +131,9 @@ namespace AzToolsFramework SnappingCluster(const SnappingCluster&) = delete; SnappingCluster& operator=(const SnappingCluster&) = delete; + //! Attempt to show the snapping cluster (will only succeed if snapping is enabled). + void TrySetVisible(bool visible); + ViewportUi::ClusterId m_clusterId; //!< The cluster id for all snapping buttons. ViewportUi::ButtonId m_snapToWorldButtonId; //!< The button id for snapping all axes to the world. AZ::Event::Handler m_snappingHandler; //!< Callback for when a snapping cluster button is pressed. @@ -151,6 +154,7 @@ namespace AzToolsFramework , private EditorEntityLockComponentNotificationBus::Router , private EditorManipulatorCommandUndoRedoRequestBus::Handler , private AZ::TransformNotificationBus::MultiHandler + , private ViewportInteraction::ViewportSettingsNotificationBus::Handler { public: AZ_CLASS_ALLOCATOR_DECL @@ -289,6 +293,9 @@ namespace AzToolsFramework void OnStartPlayInEditor() override; void OnStopPlayInEditor() override; + // ViewportSettingsNotificationBus overrides ... + void OnGridSnappingChanged(bool enabled) override; + // Helpers to safely interact with the TransformBus (requests). void SetEntityWorldTranslation(AZ::EntityId entityId, const AZ::Vector3& worldTranslation); void SetEntityLocalTranslation(AZ::EntityId entityId, const AZ::Vector3& localTranslation); diff --git a/Code/Sandbox/Editor/EditorViewportSettings.cpp b/Code/Sandbox/Editor/EditorViewportSettings.cpp index dbfd3ea4ed..02e280b8d1 100644 --- a/Code/Sandbox/Editor/EditorViewportSettings.cpp +++ b/Code/Sandbox/Editor/EditorViewportSettings.cpp @@ -14,6 +14,7 @@ #include #include +#include #include namespace SandboxEditor @@ -56,6 +57,39 @@ namespace SandboxEditor return value; } + struct EditorViewportSettingsCallbacksImpl : public EditorViewportSettingsCallbacks + { + EditorViewportSettingsCallbacksImpl() + { + if (auto* registry = AZ::SettingsRegistry::Get()) + { + using AZ::SettingsRegistryMergeUtils::IsPathAncestorDescendantOrEqual; + + m_notifyEventHandler = registry->RegisterNotifier( + [this](const AZStd::string_view path, [[maybe_unused]] const AZ::SettingsRegistryInterface::Type type) + { + if (IsPathAncestorDescendantOrEqual(GridSnappingSetting, path)) + { + m_gridSnappingChanged.Signal(GridSnappingEnabled()); + } + }); + } + } + + void SetGridSnappingChangedEvent(GridSnappingChangedEvent::Handler& handler) override + { + handler.Connect(m_gridSnappingChanged); + } + + GridSnappingChangedEvent m_gridSnappingChanged; + AZ::SettingsRegistryInterface::NotifyEventHandler m_notifyEventHandler; + }; + + AZStd::unique_ptr CreateEditorViewportSettingsCallbacks() + { + return AZStd::make_unique(); + } + bool GridSnappingEnabled() { return GetRegistry(GridSnappingSetting, false); diff --git a/Code/Sandbox/Editor/EditorViewportSettings.h b/Code/Sandbox/Editor/EditorViewportSettings.h index a2f80d196e..d3a082c095 100644 --- a/Code/Sandbox/Editor/EditorViewportSettings.h +++ b/Code/Sandbox/Editor/EditorViewportSettings.h @@ -14,8 +14,27 @@ #include +#include +#include + namespace SandboxEditor { + using GridSnappingChangedEvent = AZ::Event; + + //! Set callbacks to listen for editor settings change events. + class EditorViewportSettingsCallbacks + { + public: + virtual ~EditorViewportSettingsCallbacks() = default; + + virtual void SetGridSnappingChangedEvent(GridSnappingChangedEvent::Handler& handler) = 0; + }; + + //! Create an instance of EditorViewportSettingsCallbacks + //! Note: EditorViewportSettingsCallbacks is implemented in EditorViewportSettings.cpp - a change + //! event will fire when a value in the settings registry (editorpreferences.setreg) is modified. + SANDBOX_API AZStd::unique_ptr CreateEditorViewportSettingsCallbacks(); + SANDBOX_API bool GridSnappingEnabled(); SANDBOX_API void SetGridSnapping(bool enabled); diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index 1a7c54967b..699cd7acb9 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -76,7 +76,6 @@ #include "EditorPreferencesPageGeneral.h" #include "ViewportManipulatorController.h" #include "LegacyViewportCameraController.h" -#include "EditorViewportSettings.h" #include "ViewPane.h" #include "CustomResolutionDlg.h" @@ -1450,6 +1449,17 @@ void EditorViewportWidget::SetViewportId(int id) { SetAsActiveViewport(); } + + m_editorViewportSettingsCallbacks = SandboxEditor::CreateEditorViewportSettingsCallbacks(); + + m_gridSnappingHandler = SandboxEditor::GridSnappingChangedEvent::Handler( + [id](const bool snapping) + { + AzToolsFramework::ViewportInteraction::ViewportSettingsNotificationBus::Event( + id, &AzToolsFramework::ViewportInteraction::ViewportSettingsNotificationBus::Events::OnGridSnappingChanged, snapping); + }); + + m_editorViewportSettingsCallbacks->SetGridSnappingChangedEvent(m_gridSnappingHandler); } void EditorViewportWidget::ConnectViewportInteractionRequestBus() diff --git a/Code/Sandbox/Editor/EditorViewportWidget.h b/Code/Sandbox/Editor/EditorViewportWidget.h index 511a7910c6..670694f91f 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.h +++ b/Code/Sandbox/Editor/EditorViewportWidget.h @@ -24,6 +24,7 @@ #include "Objects/DisplayContext.h" #include "Undo/Undo.h" #include "Util/PredefinedAspectRatios.h" +#include "EditorViewportSettings.h" #include #include @@ -571,6 +572,9 @@ private: AzFramework::EntityVisibilityQuery m_entityVisibilityQuery; + SandboxEditor::GridSnappingChangedEvent::Handler m_gridSnappingHandler; + AZStd::unique_ptr m_editorViewportSettingsCallbacks; + QSet m_keyDown; bool m_freezeViewportInput = false; From 43b1d0444d93d967f41da003843d7402317740fd Mon Sep 17 00:00:00 2001 From: John Date: Thu, 17 Jun 2021 19:47:01 +0100 Subject: [PATCH 228/233] Fix PythonBindingsExampleTest name --- Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp b/Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp index a463a5216d..a57b35fb7e 100644 --- a/Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp +++ b/Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp @@ -63,7 +63,7 @@ namespace PythonBindingsExample AZStd::unique_ptr PythonBindingsExampleTest::s_application; - TEST_F(PythonBindingsExampleTest, Application_Run_Fails) + TEST_F(PythonBindingsExampleTest, Application_Run_Succeeds) { EXPECT_TRUE(s_application->Run()); } From 96da6c6438643ad61e377d076744d6b3ee054d1f Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Thu, 17 Jun 2021 14:36:09 -0700 Subject: [PATCH 229/233] LYN-3244 Remove ebp_sanity_smoke_no_gpu (#1386) --- cmake/LYTestWrappers.cmake | 65 -------------------------------- cmake/cmake_files.cmake | 1 - cmake/run_epbtest.cmake | 42 --------------------- scripts/ctest/CMakeLists.txt | 13 ------- scripts/ctest/epb_sanity_test.py | 21 ----------- 5 files changed, 142 deletions(-) delete mode 100644 cmake/run_epbtest.cmake delete mode 100755 scripts/ctest/epb_sanity_test.py diff --git a/cmake/LYTestWrappers.cmake b/cmake/LYTestWrappers.cmake index b4d6fe308e..aef119e459 100644 --- a/cmake/LYTestWrappers.cmake +++ b/cmake/LYTestWrappers.cmake @@ -302,71 +302,6 @@ function(ly_add_pytest) set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${LY_ADDED_TEST_NAME}_SCRIPT_PATH ${ly_add_pytest_PATH}) endfunction() -#! ly_add_editor_python_test: registers target Editor Python Bindings test with CTest -# -# \arg:NAME name of the test-module to register with CTest -# \arg:PATH path to the file (or dir) containing Editor Python Bindings-based tests -# \arg:TEST_PROJECT Name of the project to be set before running the test -# \arg:TEST_SUITE name of the test suite to register with CTest -# \arg:TEST_SERIAL (bool) disable parallel execution alongside other test modules, important when this test depends on shared resources or environment state -# \arg:TEST_REQUIRES (optional) list of system resources needed by the tests in this module. Used to filter out execution when those system resources are not available. For example, 'gpu' -# \arg:RUNTIME_DEPENDENCIES (optional) - List of additional runtime dependencies required by this test. -# "Editor" and "EditorPythonBindings" gem are automatically included as dependencies. -# \arg:COMPONENT (optional) - Scope of the feature area that the test belongs to (eg. physics, graphics, etc.). -# \arg:TIMEOUT (optional) The timeout in seconds for the module. If not set, will have its timeout set by ly_add_test to the default timeout. -function(ly_add_editor_python_test) - if(NOT PAL_TRAIT_TEST_PYTEST_SUPPORTED) - return() - endif() - - set(options TEST_SERIAL) - set(oneValueArgs NAME PATH TEST_SUITE TEST_PROJECT TIMEOUT) - set(multiValueArgs TEST_REQUIRES RUNTIME_DEPENDENCIES COMPONENT) - - cmake_parse_arguments(ly_add_editor_python_test "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - set(executable_target $) - - if(NOT TARGET Legacy::Editor) - message(FATAL_ERROR "Legacy::Editor was not recognized as a valid target") - endif() - - if(NOT ly_add_editor_python_test_PATH) - message(FATAL_ERROR "Must supply a value for PATH to tests") - endif() - - if(NOT ly_add_editor_python_test_TEST_SUITE) - message(FATAL_ERROR "Must supply a value for TEST_SUITE") - endif() - - file(REAL_PATH ${ly_add_editor_python_test_TEST_PROJECT} project_real_path BASE_DIRECTORY ${LY_ROOT_FOLDER}) - - # Run test via the run_epbtest.cmake script. - # Parameters used are explained in run_epbtest.cmake. - ly_add_test( - NAME ${ly_add_editor_python_test_NAME} - TEST_REQUIRES ${ly_add_editor_python_test_TEST_REQUIRES} - TEST_COMMAND ${CMAKE_COMMAND} - -DCMD_ARG_TEST_PROJECT=${project_real_path} - -DCMD_ARG_EDITOR=$ - -DCMD_ARG_PYTHON_SCRIPT=${ly_add_editor_python_test_PATH} - -DPLATFORM=${PAL_PLATFORM_NAME} - -P ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/run_epbtest.cmake - RUNTIME_DEPENDENCIES - ${ly_add_editor_python_test_RUNTIME_DEPENDENCIES} - Gem::EditorPythonBindings.Editor - Legacy::Editor - TEST_SUITE ${ly_add_editor_python_test_TEST_SUITE} - LABELS FRAMEWORK_pytest - TEST_LIBRARY pytest_editor - TIMEOUT ${ly_add_editor_python_test_TIMEOUT} - COMPONENT ${ly_add_editor_python_test_COMPONENT} - ) - - set_tests_properties(${LY_ADDED_TEST_NAME} PROPERTIES RUN_SERIAL "${ly_add_editor_python_test_TEST_SERIAL}") - set_property(GLOBAL APPEND PROPERTY LY_ALL_TESTS_${LY_ADDED_TEST_NAME}_SCRIPT_PATH ${ly_add_editor_python_test_PATH}) -endfunction() - #! ly_add_googletest: Adds a new RUN_TEST using for the specified target using the supplied command or fallback to running # googletest tests through AzTestRunner # \arg:NAME Name to for the test run target diff --git a/cmake/cmake_files.cmake b/cmake/cmake_files.cmake index a1fd66a06d..3d7ca9794f 100644 --- a/cmake/cmake_files.cmake +++ b/cmake/cmake_files.cmake @@ -35,7 +35,6 @@ set(FILES PAL.cmake PALTools.cmake Projects.cmake - run_epbtest.cmake RuntimeDependencies.cmake SettingsRegistry.cmake UnitTest.cmake diff --git a/cmake/run_epbtest.cmake b/cmake/run_epbtest.cmake deleted file mode 100644 index a0c5eb2b17..0000000000 --- a/cmake/run_epbtest.cmake +++ /dev/null @@ -1,42 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -# Script for running a test that uses EditorPythonBindings. Takes care of: -# 1. Activating a project. -# 2. Enabling the EditorPythonBindings gem. -# 3. Invoking the Editor executable with the parameters to load the test script. -# 4. Kills the AssetProcessor process -# The following arguments are required: -# CMD_ARG_TEST_PROJECT - name of the project to enable via lmbr. -# CMD_ARG_EDITOR - full path to the Editor executable. -# CMD_ARG_PYTHON_SCRIPT - full path to the python script to be executed by the Editor. - -# EditorPythonBindings need to be enabled for the project we launch - -execute_process( - COMMAND ${CMD_ARG_EDITOR} -NullRenderer --skipWelcomeScreenDialog --autotest_mode --regset="/Amazon/AzCore/Bootstrap/project_path=${CMD_ARG_TEST_PROJECT}" --runpython ${CMD_ARG_PYTHON_SCRIPT} - TIMEOUT 1800 - RESULT_VARIABLE TEST_CMD_RESULT -) - -if(${PLATFORM} STREQUAL "Windows") - execute_process( - COMMAND taskkill /F /IM AssetProcessor.exe - ) -else() - execute_process( - COMMAND killall -I AssetProcessor - ) -endif() - -if(TEST_CMD_RESULT) - message(FATAL_ERROR "Error running EditorPythonBindings Test via CMake Wrapper, result ${TEST_CMD_RESULT}") -endif() \ No newline at end of file diff --git a/scripts/ctest/CMakeLists.txt b/scripts/ctest/CMakeLists.txt index c07aaf4bff..3a327592db 100644 --- a/scripts/ctest/CMakeLists.txt +++ b/scripts/ctest/CMakeLists.txt @@ -37,19 +37,6 @@ if(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED) endforeach() endif() -# EPB Sanity test is being registered here to validate that the ly_add_editor_python_test function works. -#if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedTesting IN_LIST LY_PROJECTS_TARGET_NAME) -# ly_add_editor_python_test( -# NAME epb_sanity_smoke_no_gpu -# TEST_PROJECT AutomatedTesting -# PATH ${CMAKE_CURRENT_LIST_DIR}/epb_sanity_test.py -# TEST_SUITE smoke -# TEST_SERIAL TRUE -# RUNTIME_DEPENDENCIES -# AutomatedTesting.Assets -# ) -#endif() - # add a custom test which makes sure that the test filtering works! ly_add_test( diff --git a/scripts/ctest/epb_sanity_test.py b/scripts/ctest/epb_sanity_test.py deleted file mode 100755 index 9a532536b4..0000000000 --- a/scripts/ctest/epb_sanity_test.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -its licensors. - -For complete copyright and license terms please see the LICENSE at the root of this -distribution (the "License"). All use of this software is governed by the License, -or, if provided, by the license below or the license accompanying this file. Do not -remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -""" - -# Sanity test for EditorPythonBindings CTest wrapper - -import azlmbr.framework as framework - -print("EditorPythonBindings CTest Sanity Test") - -# A test should have logic to determine success (zero) or failure (non-zero) and -# return it to the caller. In this sanity test, always return success. -return_code = 0 -framework.Terminate(return_code) From a43682ef5a4f0aa31956b00fe377608cbf8a5008 Mon Sep 17 00:00:00 2001 From: mnaumov Date: Thu, 17 Jun 2021 17:21:04 -0700 Subject: [PATCH 230/233] [LYN-4253] Improving Editor performance while thumbnails are rendering --- .../Code/Source/Thumbnail/ImageThumbnail.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp index dda4587588..600b012af0 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp @@ -63,7 +63,7 @@ namespace ImageProcessingAtom void ImageThumbnail::LoadThread() { - AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::QueueEvent( + AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::Event( AZ::RPI::StreamingImageAsset::RTTI_Type(), &AzToolsFramework::Thumbnailer::ThumbnailerRendererRequests::RenderThumbnail, m_key, ImageThumbnailSize); From 75e640b908acf4a18ee5cee5903c4c3d109e554b Mon Sep 17 00:00:00 2001 From: Hasareej <82398396+Hasareej@users.noreply.github.com> Date: Fri, 18 Jun 2021 09:50:02 +0100 Subject: [PATCH 231/233] Hasareej lyn 4389 fix cluster overlap with ImGui (#1329) Fixing the cluster overlap with ImGui & a ViewportDisplayLayout alignment issue. --- .../ViewportUi/ViewportUiDisplay.cpp | 3 -- .../ViewportUi/ViewportUiDisplayLayout.cpp | 45 ++++++++++++++++--- .../ViewportUi/ViewportUiDisplayLayout.h | 5 +++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index bb2aeed4d6..81be2bba90 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -24,8 +24,6 @@ namespace AzToolsFramework::ViewportUi::Internal { - // margin for the Viewport UI Overlay in pixels - const static int ViewportUiOverlayMargin = 5; const static int HighlightBorderSize = 5; const static int TopHighlightBorderSize = 25; const static char* HighlightBorderColor = "#44B2F8"; @@ -387,7 +385,6 @@ namespace AzToolsFramework::ViewportUi::Internal m_fullScreenLayout.setSpacing(0); m_fullScreenLayout.setContentsMargins(0, 0, 0, 0); m_fullScreenLayout.addLayout(&m_uiOverlayLayout, 0, 0, 1, 1); - m_uiOverlayLayout.setMargin(ViewportUiOverlayMargin); // format the label which will appear on top of the highlight border AZStd::string styleSheet = AZStd::string::format( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.cpp index bab664d832..92fad8cc93 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.cpp @@ -25,13 +25,15 @@ namespace AzToolsFramework::ViewportUi::Internal : QGridLayout(parent) { // set margins and spacing for internal contents - setContentsMargins(0, 0, 0, 0); + setContentsMargins( + ViewportUiOverlayMargin, ViewportUiOverlayMargin + ViewportUiOverlayTopMarginPadding, ViewportUiOverlayMargin, + ViewportUiOverlayMargin); setSpacing(ViewportUiDisplayLayoutSpacing); // create a 3x2 map of sub layouts which will stack widgets according to their mapped alignment m_internalLayouts = AZStd::unordered_map { CreateSubLayout(new QVBoxLayout(), 0, 0, Qt::AlignTop | Qt::AlignLeft), - CreateSubLayout(new QHBoxLayout(), 1, 0, Qt::AlignBottom | Qt::AlignLeft), + CreateSubLayout(new QVBoxLayout(), 1, 0, Qt::AlignBottom | Qt::AlignLeft), CreateSubLayout(new QVBoxLayout(), 0, 1, Qt::AlignTop), CreateSubLayout(new QHBoxLayout(), 1, 1, Qt::AlignBottom), CreateSubLayout(new QVBoxLayout(), 0, 2, Qt::AlignTop | Qt::AlignRight), @@ -50,9 +52,42 @@ namespace AzToolsFramework::ViewportUi::Internal if (auto layoutForAlignment = m_internalLayouts.find(alignment); layoutForAlignment != m_internalLayouts.end()) { - // place the widget before the invisible spacer - // spacer must be last item in layout to not interfere with positioning - int index = layoutForAlignment->second->count() - 1; + // place the widget before or after the invisible spacer + // depending on the layout alignment + int index = 0; + switch (alignment) + { + case Qt::AlignTop | Qt::AlignLeft: + case Qt::AlignTop: + index = layoutForAlignment->second->count() - 1; + break; + case Qt::AlignBottom | Qt::AlignRight: + case Qt::AlignBottom: + index = layoutForAlignment->second->count(); + break; + // TopRight and BottomLeft are special cases + // place the spacer differently according to whether it's a vertical or horizontal layout + case Qt::AlignTop | Qt::AlignRight: + if (QVBoxLayout* vLayout = qobject_cast(layoutForAlignment->second)) + { + index = layoutForAlignment->second->count() - 1; + } + else if (QHBoxLayout* hLayout = qobject_cast(layoutForAlignment->second)) + { + index = layoutForAlignment->second->count(); + } + break; + case Qt::AlignBottom | Qt::AlignLeft: + if (QVBoxLayout* vLayout = qobject_cast(layoutForAlignment->second)) + { + index = layoutForAlignment->second->count(); + } + else if (QHBoxLayout* hLayout = qobject_cast(layoutForAlignment->second)) + { + index = layoutForAlignment->second->count() - 1; + } + break; + } layoutForAlignment->second->insertWidget(index, widget); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h index 0beb0d5bd6..8d710264be 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h @@ -19,6 +19,11 @@ namespace AzToolsFramework::ViewportUi::Internal { + // margin for the Viewport UI Overlay in pixels + constexpr int ViewportUiOverlayMargin = 5; + // padding to make space for ImGui + constexpr int ViewportUiOverlayTopMarginPadding = 20; + //! QGridLayout implementation that uses a grid of QVBox/QHBoxLayouts internally to stack widgets. class ViewportUiDisplayLayout : public QGridLayout { From 4ad97342c2e9120bce09fb23fc3127f3c851ced6 Mon Sep 17 00:00:00 2001 From: Hasareej <82398396+Hasareej@users.noreply.github.com> Date: Fri, 18 Jun 2021 11:45:48 +0100 Subject: [PATCH 232/233] Hasareej fix whitebox and physx cluster icons (#1398) Fixing the issue with the icons not appearing & deleting duplicate icons. --- Assets/Editor/Icons/PhysX/Move.svg | 28 ------------------- Assets/Editor/Icons/PhysX/Rotate.svg | 21 -------------- Assets/Editor/Icons/PhysX/Scale.svg | 22 --------------- Assets/Editor/Icons/WhiteBox/Move.svg | 28 ------------------- Assets/Editor/Icons/WhiteBox/Rotate.svg | 21 -------------- Assets/Editor/Icons/WhiteBox/Scale.svg | 22 --------------- .../img/UI20/toolbar}/RestoreMode.svg | 0 .../toolbar/{Translate.svg => Rotate.svg} | 0 .../img/UI20/toolbar}/SketchMode.svg | 0 .../AzQtComponents/Components/resources.qrc | 4 ++- .../EditorTransformComponentSelection.cpp | 2 +- .../Code/Editor/ColliderComponentMode.cpp | 2 +- .../Source/EditorWhiteBoxComponentMode.cpp | 2 +- 13 files changed, 6 insertions(+), 146 deletions(-) delete mode 100644 Assets/Editor/Icons/PhysX/Move.svg delete mode 100644 Assets/Editor/Icons/PhysX/Rotate.svg delete mode 100644 Assets/Editor/Icons/PhysX/Scale.svg delete mode 100644 Assets/Editor/Icons/WhiteBox/Move.svg delete mode 100644 Assets/Editor/Icons/WhiteBox/Rotate.svg delete mode 100644 Assets/Editor/Icons/WhiteBox/Scale.svg rename {Assets/Editor/Icons/WhiteBox => Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar}/RestoreMode.svg (100%) rename Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/{Translate.svg => Rotate.svg} (100%) rename {Assets/Editor/Icons/WhiteBox => Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar}/SketchMode.svg (100%) diff --git a/Assets/Editor/Icons/PhysX/Move.svg b/Assets/Editor/Icons/PhysX/Move.svg deleted file mode 100644 index e9019bf226..0000000000 --- a/Assets/Editor/Icons/PhysX/Move.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Assets/Editor/Icons/PhysX/Rotate.svg b/Assets/Editor/Icons/PhysX/Rotate.svg deleted file mode 100644 index 79bfb77540..0000000000 --- a/Assets/Editor/Icons/PhysX/Rotate.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - Icons / Toolbar / Rotate - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Assets/Editor/Icons/PhysX/Scale.svg b/Assets/Editor/Icons/PhysX/Scale.svg deleted file mode 100644 index f4879b89f6..0000000000 --- a/Assets/Editor/Icons/PhysX/Scale.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - Icons / Toolbar / Scale - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Assets/Editor/Icons/WhiteBox/Move.svg b/Assets/Editor/Icons/WhiteBox/Move.svg deleted file mode 100644 index e9019bf226..0000000000 --- a/Assets/Editor/Icons/WhiteBox/Move.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Assets/Editor/Icons/WhiteBox/Rotate.svg b/Assets/Editor/Icons/WhiteBox/Rotate.svg deleted file mode 100644 index 79bfb77540..0000000000 --- a/Assets/Editor/Icons/WhiteBox/Rotate.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - Icons / Toolbar / Rotate - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Assets/Editor/Icons/WhiteBox/Scale.svg b/Assets/Editor/Icons/WhiteBox/Scale.svg deleted file mode 100644 index f4879b89f6..0000000000 --- a/Assets/Editor/Icons/WhiteBox/Scale.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - Icons / Toolbar / Scale - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Assets/Editor/Icons/WhiteBox/RestoreMode.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/RestoreMode.svg similarity index 100% rename from Assets/Editor/Icons/WhiteBox/RestoreMode.svg rename to Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/RestoreMode.svg diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Translate.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Rotate.svg similarity index 100% rename from Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Translate.svg rename to Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Rotate.svg diff --git a/Assets/Editor/Icons/WhiteBox/SketchMode.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/SketchMode.svg similarity index 100% rename from Assets/Editor/Icons/WhiteBox/SketchMode.svg rename to Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/SketchMode.svg diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc b/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc index 7070bd372b..d6cd99b1d9 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc @@ -370,6 +370,7 @@ img/UI20/toolbar/Redo.svg img/UI20/toolbar/remove_link.svg img/UI20/toolbar/Reset_physics_state.svg + img/UI20/toolbar/RestoreMode.svg img/UI20/toolbar/Save.svg img/UI20/toolbar/Scale.svg img/UI20/toolbar/Select.svg @@ -377,9 +378,10 @@ img/UI20/toolbar/Select_terrain.svg img/UI20/toolbar/Simulate_Physics.svg img/UI20/toolbar/Simulate_Physics_on_selected_objects.svg + img/UI20/toolbar/SketchMode.svg img/UI20/toolbar/Terrain.svg img/UI20/toolbar/Terrain_Texture.svg - img/UI20/toolbar/Translate.svg + img/UI20/toolbar/Rotate.svg img/UI20/toolbar/undo.svg img/UI20/toolbar/Unlocked.svg img/UI20/toolbar/Vertex_snapping.svg diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 3e16088f0e..54934c13c7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -2489,7 +2489,7 @@ namespace AzToolsFramework // create and register the buttons (strings correspond to icons even if the values appear different) m_translateButtonId = RegisterClusterButton(m_transformModeClusterId, "Move"); - m_rotateButtonId = RegisterClusterButton(m_transformModeClusterId, "Translate"); + m_rotateButtonId = RegisterClusterButton(m_transformModeClusterId, "Rotate"); m_scaleButtonId = RegisterClusterButton(m_transformModeClusterId, "Scale"); // set button tooltips diff --git a/Gems/PhysX/Code/Editor/ColliderComponentMode.cpp b/Gems/PhysX/Code/Editor/ColliderComponentMode.cpp index 7caa497344..0957985623 100644 --- a/Gems/PhysX/Code/Editor/ColliderComponentMode.cpp +++ b/Gems/PhysX/Code/Editor/ColliderComponentMode.cpp @@ -223,7 +223,7 @@ namespace PhysX AzToolsFramework::ViewportUi::ViewportUiRequestBus::EventResult( buttonId, AzToolsFramework::ViewportUi::DefaultViewportId, &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::CreateClusterButton, clusterId, - AZStd::string::format("Icons/PhysX/%s.svg", iconName)); + AZStd::string::format(":/stylesheet/img/UI20/toolbar/%s.svg", iconName)); return buttonId; } diff --git a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponentMode.cpp b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponentMode.cpp index 0772851cba..a3ec073e41 100644 --- a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponentMode.cpp +++ b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponentMode.cpp @@ -465,7 +465,7 @@ namespace WhiteBox AzToolsFramework::ViewportUi::ViewportUiRequestBus::EventResult( buttonId, AzToolsFramework::ViewportUi::DefaultViewportId, &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::CreateClusterButton, clusterId, - AZStd::string::format("Icons/WhiteBox/%s.svg", iconName)); + AZStd::string::format(":/stylesheet/img/UI20/toolbar/%s.svg", iconName)); return buttonId; } From 34e64ca5b0eccaf82bcff6b087663b6d738413c5 Mon Sep 17 00:00:00 2001 From: bosnichd Date: Fri, 18 Jun 2021 08:59:10 -0600 Subject: [PATCH 233/233] Remove unused MTPseudoRandom.h/.cpp to address SPEC-5990 (#1426) --- Code/CryEngine/CryCommon/MTPseudoRandom.cpp | 79 --------- Code/CryEngine/CryCommon/MTPseudoRandom.h | 166 ------------------ Code/CryEngine/CryCommon/Random.h | 1 - .../CryEngine/CryCommon/crycommon_files.cmake | 2 - 4 files changed, 248 deletions(-) delete mode 100644 Code/CryEngine/CryCommon/MTPseudoRandom.cpp delete mode 100644 Code/CryEngine/CryCommon/MTPseudoRandom.h diff --git a/Code/CryEngine/CryCommon/MTPseudoRandom.cpp b/Code/CryEngine/CryCommon/MTPseudoRandom.cpp deleted file mode 100644 index 0796d92f66..0000000000 --- a/Code/CryEngine/CryCommon/MTPseudoRandom.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ -// Original file Copyright Crytek GMBH or its affiliates, used under license. - -// Description : Marsenne Twister PRNG. See MT.h for more info. - -#include "MTPseudoRandom.h" -// non-inline function definitions and static member definitions cannot -// reside in header file because of the risk of multiple declarations - -void CMTRand_int32::gen_state() // generate new m_nState vector -{ - for (int i = 0; i < (n - m); ++i) - { - m_nState[i] = m_nState[i + m] ^ twiddle(m_nState[i], m_nState[i + 1]); - } - for (int i = n - m; i < (n - 1); ++i) - { - m_nState[i] = m_nState[i + m - n] ^ twiddle(m_nState[i], m_nState[i + 1]); - } - m_nState[n - 1] = m_nState[m - 1] ^ twiddle(m_nState[n - 1], m_nState[0]); - p = 0; // reset position -} - -void CMTRand_int32::seed(uint32 s) // init by 32 bit seed -{ //if (s == 0) - //m_nRandom = 1; - for (int i = 0; i < n; ++i) - { - m_nState[i] = 0x0UL; - } - m_nState[0] = s; - for (int i = 1; i < n; ++i) - { - m_nState[i] = 1812433253UL * (m_nState[i - 1] ^ (m_nState[i - 1] >> 30)) + i; - // see Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier - // in the previous versions, MSBs of the seed affect only MSBs of the array m_nState - // 2002/01/09 modified by Makoto Matsumoto - } - p = n; // force gen_state() to be called for next random number -} - -void CMTRand_int32::seed(const uint32* array, int size) // init by array -{ - seed(19650218UL); - int i = 1, j = 0; - for (int k = ((n > size) ? n : size); k; --k) - { - m_nState[i] = (m_nState[i] ^ ((m_nState[i - 1] ^ (m_nState[i - 1] >> 30)) * 1664525UL)) - + array[j] + j; // non linear - ++j; - j %= size; - if ((++i) == n) - { - m_nState[0] = m_nState[n - 1]; - i = 1; - } - } - for (int k = n - 1; k; --k) - { - PREFAST_SUPPRESS_WARNING(6385) PREFAST_SUPPRESS_WARNING(6386) m_nState[i] = (m_nState[i] ^ ((m_nState[i - 1] ^ (m_nState[i - 1] >> 30)) * 1566083941UL)) - i; - if ((++i) == n) - { - m_nState[0] = m_nState[n - 1]; - i = 1; - } - } - m_nState[0] = 0x80000000UL; // MSB is 1; assuring non-zero initial array - p = n; // force gen_state() to be called for next random number -} diff --git a/Code/CryEngine/CryCommon/MTPseudoRandom.h b/Code/CryEngine/CryCommon/MTPseudoRandom.h deleted file mode 100644 index 40075cf167..0000000000 --- a/Code/CryEngine/CryCommon/MTPseudoRandom.h +++ /dev/null @@ -1,166 +0,0 @@ -// mtrand.h -// C++ include file for MT19937, with initialization improved 2002/1/26. -// Coded by Takuji Nishimura and Makoto Matsumoto. -// Ported to C++ by Jasper Bedaux 2003/1/1 (see http://www.bedaux.net/mtrand/). -// The generators returning floating point numbers are based on -// a version by Isaku Wada, 2002/01/09 -// Static shared data converted to per-instance, 2008-11-13 by JSP. -// -// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. The names of its contributors may not be used to endorse or promote -// products derived from this software without specific prior written -// permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Any feedback is very welcome. -// http://www.math.keio.ac.jp/matumoto/emt.html -// email: matumoto@math.keio.ac.jp -// -// Feedback about the C++ port should be sent to Jasper Bedaux, -// see http://www.bedaux.net/mtrand/ for e-mail address and info. - -//------------------------------------------------------------------------- -// History: -// - 28:7:2005: File created and minor changes by Marco Corbetta -// -//*************************************************************************/ -// Modifications copyright Amazon.com, Inc. or its affiliates - -#ifndef CRYINCLUDE_CRYCOMMON_MTPSEUDORANDOM_H -#define CRYINCLUDE_CRYCOMMON_MTPSEUDORANDOM_H -#pragma once - -#include - -////////////////////////////////////////////////////////////////////////// -class CMTRand_int32 -{ - // Mersenne Twister random number generator - -public: - // default constructor - CMTRand_int32() { seed(5489UL); } - // constructor with 32 bit int as seed - CMTRand_int32(uint32 seed_value) { seed(seed_value); } - // constructor with array of 32 bit integers as seed - CMTRand_int32(const uint32* array, int size) { seed(array, size); } - // seeds with 32 bit integer - void seed(uint32 seed_value); - // seeds with array - void seed(const uint32*, int size); - // overloaded operator() to make this a generator (functor) - //uint32 operator()() { return rand_int32(); } - - ~CMTRand_int32() {} - - // Functions with PascalCase names were added for - // interchangeability with CRndGen (see LCGRandom.h). - - void Seed(uint32 seed_value) - { - seed(seed_value); - } - - uint32 GenerateUint32() - { - return rand_int32(); - } - - uint64 GenerateUint64() - { - const uint32 a = GenerateUint32(); - const uint32 b = GenerateUint32(); - return ((uint64)b << 32) | (uint64)a; - } - - float GenerateFloat() - { - return (float)GenerateUint32() * (1.0f / 4294967295.0f); - } - - // Ranged function returns random value within the *inclusive* range - // between minValue and maxValue. - // Any orderings work correctly: minValue <= maxValue and - // minValue >= minValue. - template - T GetRandom(const T minValue, const T maxValue) - { - return CryRandom_Internal::BoundedRandom::Get(*this, minValue, maxValue); - } - - // Vector (Vec2, Vec3, Vec4) ranged function returns vector with - // every component within the *inclusive* ranges between minValue.component - // and maxValue.component. - // All orderings work correctly: minValue.component <= maxValue.component and - // minValue.component >= maxValue.component. - template - T GetRandomComponentwise(const T& minValue, const T& maxValue) - { - return CryRandom_Internal::BoundedRandomComponentwise::Get(*this, minValue, maxValue); - } - - // The function returns a random unit vector (Vec2, Vec3, Vec4). - template - T GetRandomUnitVector() - { - return CryRandom_Internal::GetRandomUnitVector(*this); - } - -protected: // used by derived classes, otherwise not accessible; use the ()-operator - // generates 32 bit random int - uint32 rand_int32() - { - if (p >= n) gen_state(); // new m_nState vector needed - // gen_state() is split off to be non-inline, because it is only called once - // in every 624 calls and otherwise irand() would become too big to get inlined - uint32 x = m_nState[p++]; - x ^= (x >> 11); - x ^= (x << 7) & 0x9D2C5680UL; - x ^= (x << 15) & 0xEFC60000UL; - return x ^ (x >> 18); - } - -private: - static const int n = 624, m = 397; // compile time constants - - // the variables below are static (no duplicates can exist) - uint32 m_nState[n+1]; // m_nState vector array - int p; // position in m_nState array - // private functions used to generate the pseudo random numbers - uint32 twiddle(uint32 u, uint32 v) - { - return (((u & 0x80000000UL) | (v & 0x7FFFFFFFUL)) >> 1) - ^ ((v & 1UL) ? 0x9908B0DFUL : 0x0UL); - } - void gen_state(); // generate new m_nState - // make copy constructor and assignment operator unavailable, they don't make sense - CMTRand_int32(const CMTRand_int32&); // copy constructor not defined - void operator=(const CMTRand_int32&); // assignment operator not defined -}; - - -#endif // CRYINCLUDE_CRYCOMMON_MTPSEUDORANDOM_H diff --git a/Code/CryEngine/CryCommon/Random.h b/Code/CryEngine/CryCommon/Random.h index 0f0a9f0e89..6de9136113 100644 --- a/Code/CryEngine/CryCommon/Random.h +++ b/Code/CryEngine/CryCommon/Random.h @@ -17,7 +17,6 @@ #include "BaseTypes.h" #include "LCGRandom.h" -#include "MTPseudoRandom.h" namespace CryRandom_Internal { diff --git a/Code/CryEngine/CryCommon/crycommon_files.cmake b/Code/CryEngine/CryCommon/crycommon_files.cmake index 660cf20276..567bbefca7 100644 --- a/Code/CryEngine/CryCommon/crycommon_files.cmake +++ b/Code/CryEngine/CryCommon/crycommon_files.cmake @@ -69,7 +69,6 @@ set(FILES CryRandomInternal.h Random.h LCGRandom.h - MTPseudoRandom.cpp CryTypeInfo.cpp BaseTypes.h CompileTimeAssert.h @@ -102,7 +101,6 @@ set(FILES LegacyAllocator.h MetaUtils.h MiniQueue.h - MTPseudoRandom.h MultiThread.h MultiThread_Containers.h NullAudioSystem.h