test: register integration tests in CTest with Python wrapper
This commit is contained in:
@@ -4,6 +4,10 @@ project(D3D12_Integration)
|
||||
|
||||
set(ENGINE_ROOT_DIR ${CMAKE_SOURCE_DIR}/../engine)
|
||||
|
||||
find_package(Python3 REQUIRED)
|
||||
|
||||
enable_testing()
|
||||
|
||||
# Minimal test - just verifies initialization and render loop
|
||||
add_executable(D3D12_Minimal
|
||||
WIN32
|
||||
@@ -106,3 +110,35 @@ add_custom_command(TARGET D3D12_RenderModel POST_BUILD
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm
|
||||
$<TARGET_FILE_DIR:D3D12_RenderModel>/GT.ppm
|
||||
)
|
||||
|
||||
# Copy run_integration_test.py to output directories
|
||||
add_custom_command(TARGET D3D12_Minimal POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/run_integration_test.py
|
||||
$<TARGET_FILE_DIR:D3D12_Minimal>/run_integration_test.py
|
||||
)
|
||||
|
||||
add_custom_command(TARGET D3D12_RenderModel POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/run_integration_test.py
|
||||
$<TARGET_FILE_DIR:D3D12_RenderModel>/run_integration_test.py
|
||||
)
|
||||
|
||||
# Register integration tests with CTest
|
||||
add_test(NAME D3D12_Minimal_Integration
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/run_integration_test.py
|
||||
$<TARGET_FILE:D3D12_Minimal>
|
||||
minimal.ppm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm
|
||||
5
|
||||
WORKING_DIRECTORY $<TARGET_FILE_DIR:D3D12_Minimal>
|
||||
)
|
||||
|
||||
add_test(NAME D3D12_RenderModel_Integration
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/run_integration_test.py
|
||||
$<TARGET_FILE:D3D12_RenderModel>
|
||||
screenshot.ppm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm
|
||||
5
|
||||
WORKING_DIRECTORY $<TARGET_FILE_DIR:D3D12_RenderModel>
|
||||
)
|
||||
|
||||
124
tests/RHI/D3D12/integration/run_integration_test.py
Normal file
124
tests/RHI/D3D12/integration/run_integration_test.py
Normal file
@@ -0,0 +1,124 @@
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
import shutil
|
||||
|
||||
|
||||
def run_integration_test(exe_path, output_ppm, gt_ppm, threshold, timeout=120):
|
||||
"""
|
||||
Run a D3D12 integration test and compare output with golden template.
|
||||
|
||||
Args:
|
||||
exe_path: Path to the test executable
|
||||
output_ppm: Filename of the output screenshot
|
||||
gt_ppm: Path to the golden template PPM file
|
||||
threshold: Pixel difference threshold for comparison
|
||||
timeout: Maximum time to wait for test completion (seconds)
|
||||
|
||||
Returns:
|
||||
0 on success, non-zero on failure
|
||||
"""
|
||||
exe_dir = os.path.dirname(os.path.abspath(exe_path))
|
||||
output_path = os.path.join(exe_dir, output_ppm)
|
||||
|
||||
print(f"[Integration Test] Starting: {exe_path}")
|
||||
print(f"[Integration Test] Working directory: {exe_dir}")
|
||||
print(f"[Integration Test] Expected output: {output_path}")
|
||||
|
||||
if not os.path.exists(exe_path):
|
||||
print(f"[Integration Test] ERROR: Executable not found: {exe_path}")
|
||||
return 1
|
||||
|
||||
if not os.path.exists(gt_ppm):
|
||||
print(f"[Integration Test] ERROR: Golden template not found: {gt_ppm}")
|
||||
return 1
|
||||
|
||||
if os.path.exists(output_path):
|
||||
print(f"[Integration Test] Removing old output: {output_path}")
|
||||
os.remove(output_path)
|
||||
|
||||
try:
|
||||
print(f"[Integration Test] Launching process...")
|
||||
start_time = time.time()
|
||||
process = subprocess.Popen(
|
||||
[exe_path],
|
||||
cwd=exe_dir,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
creationflags=subprocess.CREATE_NEW_CONSOLE if os.name == "nt" else 0,
|
||||
)
|
||||
|
||||
returncode = None
|
||||
while time.time() - start_time < timeout:
|
||||
returncode = process.poll()
|
||||
if returncode is not None:
|
||||
break
|
||||
time.sleep(0.5)
|
||||
|
||||
if returncode is None:
|
||||
print(f"[Integration Test] ERROR: Process timed out after {timeout}s")
|
||||
process.kill()
|
||||
return 1
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
print(
|
||||
f"[Integration Test] Process finished in {elapsed:.1f}s with exit code: {returncode}"
|
||||
)
|
||||
|
||||
if returncode != 0:
|
||||
print(f"[Integration Test] ERROR: Process returned non-zero exit code")
|
||||
stdout, stderr = process.communicate(timeout=5)
|
||||
if stdout:
|
||||
print(
|
||||
f"[Integration Test] STDOUT:\n{stdout.decode('utf-8', errors='replace')}"
|
||||
)
|
||||
if stderr:
|
||||
print(
|
||||
f"[Integration Test] STDERR:\n{stderr.decode('utf-8', errors='replace')}"
|
||||
)
|
||||
return 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"[Integration Test] ERROR: Failed to run process: {e}")
|
||||
return 1
|
||||
|
||||
if not os.path.exists(output_path):
|
||||
print(f"[Integration Test] ERROR: Output file not created: {output_path}")
|
||||
return 1
|
||||
|
||||
print(f"[Integration Test] Running image comparison...")
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
compare_script = os.path.join(script_dir, "compare_ppm.py")
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[sys.executable, compare_script, output_path, gt_ppm, str(threshold)],
|
||||
cwd=exe_dir,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
print(result.stdout)
|
||||
if result.stderr:
|
||||
print(f"[Integration Test] Comparison STDERR: {result.stderr}")
|
||||
return result.returncode
|
||||
|
||||
except Exception as e:
|
||||
print(f"[Integration Test] ERROR: Failed to run comparison: {e}")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 5:
|
||||
print(
|
||||
"Usage: run_integration_test.py <exe_path> <output_ppm> <gt_ppm> <threshold>"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
exe_path = sys.argv[1]
|
||||
output_ppm = sys.argv[2]
|
||||
gt_ppm = sys.argv[3]
|
||||
threshold = int(sys.argv[4])
|
||||
|
||||
exit_code = run_integration_test(exe_path, output_ppm, gt_ppm, threshold)
|
||||
sys.exit(exit_code)
|
||||
Reference in New Issue
Block a user