mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 14:26:42 +00:00 
			
		
		
		
	This allows writing tests for menu scripts. Keep global script as entry point to morrowind tests. Fix menu.newGame and menu.loadGame to hide main menu.
		
			
				
	
	
		
			157 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			157 lines
		
	
	
	
		
			6 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, "--no-grab"],
 | 
						|
        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)
 |