#!/usr/bin/env python3 """Test runner prerequisite decisions with fake tools, not actual Apple SDKs.""" import os import importlib.util from pathlib import Path import shlex import subprocess import sys import tempfile import unittest SCRIPT = Path(__file__).with_name("check-ci-runner.sh") SPEC = importlib.util.spec_from_file_location("apple_tools", SCRIPT.with_name("check-apple-tools.py")) APPLE_TOOLS = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(APPLE_TOOLS) class RunnerPrerequisiteTests(unittest.TestCase): def run_check(self, mode, system="Linux", architecture="x86_64", missing=None, broken_xcode=False, broken_xcrun=False): base = "/tmp/opencode" if Path("/tmp/opencode").exists() else None with tempfile.TemporaryDirectory(prefix="yuvomi-runner-test-", dir=base) as temporary: directory = Path(temporary) for name in ["git", "curl", "unzip", "python3", "xcodebuild", "xcrun"]: if name == missing: continue path = directory / name status = 1 if (name == "xcodebuild" and broken_xcode or name == "xcrun" and broken_xcrun) else 0 path.write_text(f"#!/bin/sh\nexec {shlex.quote(sys.executable)} \"$@\"\n" if name == "python3" else f"#!/bin/sh\nexit {status}\n") path.chmod(0o700) uname = directory / "uname" uname.write_text('#!/bin/sh\ncase "$1" in -s) echo "$TEST_SYSTEM";; -m) echo "$TEST_ARCH";; esac\n') uname.chmod(0o700) return subprocess.run(["/bin/bash", str(SCRIPT), mode], capture_output=True, text=True, env={**os.environ, "PATH": temporary, "TEST_SYSTEM": system, "TEST_ARCH": architecture}) def test_android_accepts_linux_x64(self): self.assertEqual(self.run_check("android").returncode, 0) def test_android_rejects_arm(self): self.assertNotEqual(self.run_check("android", architecture="aarch64").returncode, 0) def test_android_rejects_macos(self): self.assertNotEqual(self.run_check("android", system="Darwin").returncode, 0) def test_ios_accepts_native_arm_with_tools(self): self.assertEqual(self.run_check("ios", system="Darwin", architecture="arm64").returncode, 0) def test_ios_rejects_rosetta_or_intel(self): self.assertNotEqual(self.run_check("ios", system="Darwin").returncode, 0) def test_ios_rejects_linux(self): self.assertNotEqual(self.run_check("ios", architecture="arm64").returncode, 0) def test_missing_python_is_actionable(self): result = self.run_check("android", missing="python3") self.assertNotEqual(result.returncode, 0) self.assertIn("install python3", result.stderr) def test_missing_full_xcode_fails(self): result = self.run_check("ios", system="Darwin", architecture="arm64", missing="xcodebuild") self.assertNotEqual(result.returncode, 0) self.assertIn("full Xcode", result.stderr) def test_broken_xcode_fails(self): self.assertNotEqual(self.run_check("ios", system="Darwin", architecture="arm64", broken_xcode=True).returncode, 0) def test_unknown_mode_fails(self): self.assertNotEqual(self.run_check("other").returncode, 0) def test_missing_xcrun_is_actionable(self): result = self.run_check("ios", system="Darwin", architecture="arm64", missing="xcrun") self.assertNotEqual(result.returncode, 0) self.assertIn("Cannot start xcrun", result.stderr) def test_broken_xcrun_is_not_a_pass(self): result = self.run_check("ios", system="Darwin", architecture="arm64", broken_xcrun=True) self.assertNotEqual(result.returncode, 0) self.assertIn("xcrun --find swift exited 1", result.stderr) def test_unresponsive_apple_probe_times_out_with_guidance(self): with self.assertRaisesRegex(RuntimeError, "interactive login session"): APPLE_TOOLS.probe([sys.executable, "-c", "import time; time.sleep(30)"], timeout=0.05) def test_apple_probe_preserves_tool_failure_diagnostic(self): with self.assertRaisesRegex(RuntimeError, "fixture tool failure"): APPLE_TOOLS.probe([sys.executable, "-c", "import sys; sys.exit('fixture tool failure')"]) if __name__ == "__main__": unittest.main()