openmw/scripts/integration_tests.py
elsid c298210844
Make integration_tests.py output more verbose
* Make it look more like googletest.
* Print total and failed number of tests.
* Print failed tests names.
* Print duration of each test and total.
* Hide all logs by default.
2025-03-05 22:18:46 +01:00

157 lines
6.1 KiB
Python
Executable file

#!/usr/bin/env python3
import argparse
import datetime
import os
import shutil
import subprocess
import sys
import time
from pathlib import Path
parser = argparse.ArgumentParser(description="OpenMW integration tests.")
parser.add_argument(
"example_suite",
type=str,
help="path to openmw example suite (use 'git clone https://gitlab.com/OpenMW/example-suite/' to get it)",
)
parser.add_argument("--omw", type=str, default="openmw", help="path to openmw binary")
parser.add_argument(
"--workdir", type=str, default="integration_tests_output", help="directory for temporary files and logs"
)
parser.add_argument("--verbose", action='store_true', help="print all openmw output")
args = parser.parse_args()
example_suite_dir = Path(args.example_suite).resolve()
content_paths = (
example_suite_dir / "game_template" / "data" / "template.omwgame",
example_suite_dir / "example_animated_creature" / "data" / "landracer.omwaddon",
example_suite_dir / "the_hub" / "data" / "the_hub.omwaddon",
)
for path in content_paths:
if not path.is_file():
sys.exit(f"{path} is not found, use 'git clone https://gitlab.com/OpenMW/example-suite/' to get it")
openmw_binary = Path(args.omw).resolve()
if not openmw_binary.is_file():
sys.exit(f"{openmw_binary} not found")
work_dir = Path(args.workdir).resolve()
work_dir.mkdir(parents=True, exist_ok=True)
config_dir = work_dir / "config"
userdata_dir = work_dir / "userdata"
tests_dir = Path(__file__).resolve().parent / "data" / "integration_tests"
testing_util_dir = tests_dir / "testing_util"
time_str = datetime.datetime.now().strftime("%Y-%m-%d-%H.%M.%S")
def run_test(test_name):
start = time.time()
print(f'[----------] Running tests from {test_name}')
shutil.rmtree(config_dir, ignore_errors=True)
config_dir.mkdir()
shutil.copyfile(example_suite_dir / "settings.cfg", config_dir / "settings.cfg")
test_dir = tests_dir / test_name
with open(config_dir / "openmw.cfg", "w", encoding="utf-8") as omw_cfg:
for path in content_paths:
omw_cfg.write(f'data="{path.parent}"\n')
omw_cfg.writelines(
(
f'data="{testing_util_dir}"\n',
f'data-local="{test_dir}"\n',
f'user-data="{userdata_dir}"\n',
)
)
for path in content_paths:
omw_cfg.write(f'content={path.name}\n')
with open(test_dir / "openmw.cfg") as stream:
omw_cfg.write(stream.read())
with open(config_dir / "settings.cfg", "a", encoding="utf-8") as settings_cfg:
settings_cfg.write(
"[Video]\n"
"resolution x = 640\n"
"resolution y = 480\n"
"framerate limit = 60\n"
"[Game]\n"
"smooth animation transitions = true\n"
"[Lua]\n"
f"memory limit = {1024 * 1024 * 256}\n"
)
stdout_lines = list()
test_success = True
fatal_errors = list()
with subprocess.Popen(
[openmw_binary, "--replace=config", "--config", config_dir, "--skip-menu", "--no-grab", "--no-sound"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
encoding="utf-8",
env={
"OPENMW_OSG_STATS_FILE": str(work_dir / f"{test_name}.{time_str}.osg_stats.log"),
"OPENMW_OSG_STATS_LIST": "times",
**os.environ,
},
) as process:
quit_requested = False
running_test_number = None
running_test_name = None
count = 0
failed_tests = list()
test_start = None
for line in process.stdout:
if args.verbose:
sys.stdout.write(line)
else:
stdout_lines.append(line)
if "Quit requested by a Lua script" in line:
quit_requested = True
elif "TEST_START" in line:
test_start = time.time()
number, name = line.split("TEST_START")[1].strip().split("\t", maxsplit=1)
running_test_number = int(number)
running_test_name = name
count += 1
print(f"[ RUN ] {running_test_name}")
elif "TEST_OK" in line:
duration = (time.time() - test_start) * 1000
number, name = line.split("TEST_OK")[1].strip().split("\t", maxsplit=1)
assert running_test_number == int(number)
print(f"[ OK ] {running_test_name} ({duration:.3f} ms)")
elif "TEST_FAILED" in line:
duration = (time.time() - test_start) * 1000
number, name, error = line.split("TEST_FAILED")[1].strip().split("\t", maxsplit=2)
assert running_test_number == int(number)
print(error)
print(f"[ FAILED ] {running_test_name} ({duration:.3f} ms)")
failed_tests.append(running_test_name)
process.wait(5)
if not quit_requested:
fatal_errors.append("unexpected termination")
if process.returncode != 0:
fatal_errors.append(f"openmw exited with code {process.returncode}")
if os.path.exists(config_dir / "openmw.log"):
shutil.copyfile(config_dir / "openmw.log", work_dir / f"{test_name}.{time_str}.log")
if fatal_errors and not args.verbose:
sys.stdout.writelines(stdout_lines)
total_duration = (time.time() - start) * 1000
print(f'\n[----------] {count} tests from {test_name} ({total_duration:.3f} ms total)')
print(f"[ PASSED ] {count - len(failed_tests)} tests.")
if fatal_errors:
print(f"[ FAILED ] fatal error: {'; '.join(fatal_errors)}")
if failed_tests:
print(f"[ FAILED ] {len(failed_tests)} tests, listed below:")
for failed_test in failed_tests:
print(f"[ FAILED ] {failed_test}")
return len(failed_tests) == 0 and not fatal_errors
status = 0
for entry in tests_dir.glob("test_*"):
if entry.is_dir():
if not run_test(entry.name):
status = -1
if status == 0:
shutil.rmtree(config_dir, ignore_errors=True)
shutil.rmtree(userdata_dir, ignore_errors=True)
exit(status)