#!/usr/bin/env python3 """Exercise a clean copy of NeitzelLib's actual fx-example in the packaged standalone host.""" import argparse import json import os from pathlib import Path import shutil import subprocess import tempfile import time import zipfile from native_crashes import NativeCrashGuard ROOT = Path(__file__).resolve().parent.parent parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--library", type=Path, required=True) parser.add_argument("--app", type=Path, required=True) parser.add_argument("--java-home", type=Path, required=True, help="JDK 25 for the external test agent compiler") parser.add_argument("--target-java-home", type=Path, required=True) args = parser.parse_args() output = Path(tempfile.mkdtemp(prefix="designer-library-example-")).resolve() print("Evidence: " + str(output), flush=True) library = output / "NeitzelLib" shutil.copytree(args.library, library, ignore=shutil.ignore_patterns(".git", "target", ".idea", ".neitzel-fx-designer")) app = output / "NeitzelFXDesigner.app" shutil.copytree(args.app, app, symlinks=True) local = library / ".neitzel-fx-designer" local.mkdir() settings = local / "runtimes.json" settings.write_text(json.dumps({"schemaVersion": "1.0", "runtimes": {"java21": { "build": str(args.target_java_home.resolve()), "target": str(args.target_java_home.resolve())}}})) classes = output / "agent-classes" classes.mkdir() jars = list((app / "Contents/app").glob("*.jar")) subprocess.run([args.java_home / "bin/javac", "-cp", os.pathsep.join(map(str, jars)), "-d", classes, ROOT / "integration-tests/fx-example/FxExampleProbe.java"], check=True) agent = output / "probe.jar" with zipfile.ZipFile(agent, "w") as jar: jar.writestr("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\nPremain-Class: qualification.FxExampleProbe\n\n") for file in classes.rglob("*.class"): jar.write(file, str(file.relative_to(classes))) config = app / "Contents/app/NeitzelFXDesigner.cfg" options = [f"-javaagent:{agent}", f"-Dexample.output={output}", f"-Dexample.library={library}"] config.write_text(config.read_text() + "\n" + "".join("java-options=" + opt + "\n" for opt in options)) env = dict(os.environ) for key in ("JAVA_TOOL_OPTIONS", "JDK_JAVA_OPTIONS", "_JAVA_OPTIONS"): env.pop(key, None) command = [app / "Contents/MacOS/NeitzelFXDesigner", "--configuration", library / "neitzel-fx-designer.json", "--settings", settings, "--maven", ROOT / "mvnw", library / "fx-example/src/main/resources/de/neitzel/fx/example/view/Desk.fxml"] crashes = NativeCrashGuard(ROOT) with (output / "launcher.log").open("w") as log: process = subprocess.Popen(list(map(str, command)), env=env, cwd=output, stdout=log, stderr=subprocess.STDOUT) try: code = process.wait(timeout=420) except subprocess.TimeoutExpired: process.terminate() try: process.wait(timeout=15) except subprocess.TimeoutExpired: process.kill() process.wait(timeout=10) raise if code != 0 or not (output / "SUCCESS.txt").exists(): raise RuntimeError((output / "launcher.log").read_text()[-10000:]) for pid in (output / "workers.txt").read_text().splitlines(): until = time.monotonic() + 10 while time.monotonic() < until: try: os.kill(int(pid), 0) except ProcessLookupError: break time.sleep(.1) else: raise AssertionError("Preview worker survived host: " + pid) crashes.check() print((output / "SUCCESS.txt").read_text(), end="")