Use pinned nextest for full native tests while retaining doctests, production-feature checks and both macOS release checks. Publish shared dependency snapshots from successful default-branch runs, keep compiled artifacts architecture-specific, and preserve cross-process exclusion for shared theme fixtures. Run the full platform matrix for both draft and ready pull requests without repeating it for readiness-only transitions.
176 lines
7.3 KiB
Python
176 lines
7.3 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
|
|
SCRIPT = Path(__file__).resolve().parents[1] / "ci_plan.py"
|
|
|
|
|
|
class CiPlanCliTests(unittest.TestCase):
|
|
def run_cli(self, event_name: str, payload: object, output: Path) -> subprocess.CompletedProcess[str]:
|
|
event_path = output.parent / "event.json"
|
|
event_path.write_text(json.dumps(payload), encoding="utf-8")
|
|
return subprocess.run(
|
|
[
|
|
sys.executable,
|
|
str(SCRIPT),
|
|
"--event-name",
|
|
event_name,
|
|
"--event-path",
|
|
str(event_path),
|
|
"--output",
|
|
str(output),
|
|
],
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
)
|
|
|
|
@staticmethod
|
|
def outputs(path: Path) -> dict[str, object]:
|
|
lines = path.read_text(encoding="utf-8").splitlines()
|
|
return {name: json.loads(value) for name, value in (line.split("=", 1) for line in lines)}
|
|
|
|
def test_draft_pr_has_the_same_full_native_coverage_as_ready_pr(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
output = Path(directory) / "github-output"
|
|
result = self.run_cli("pull_request", {"pull_request": {"draft": True}}, output)
|
|
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertEqual(
|
|
output.read_text(encoding="utf-8"),
|
|
'native_matrix=[{"os":"ubuntu-24.04"},{"os":"windows-2022"},{"os":"macos-26"},'
|
|
'{"os":"windows-11-arm"},{"os":"macos-26-intel"}]\n'
|
|
'release_matrix=[{"os":"macos-26"},{"os":"macos-26-intel"}]\n',
|
|
)
|
|
plan = self.outputs(output)
|
|
ready_output = Path(directory) / "ready-output"
|
|
ready = self.run_cli("pull_request", {"pull_request": {"draft": False}}, ready_output)
|
|
self.assertEqual(ready.returncode, 0, ready.stderr)
|
|
self.assertEqual(plan, self.outputs(ready_output))
|
|
|
|
def test_ready_pr_has_all_native_and_release_checks(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
output = Path(directory) / "github-output"
|
|
result = self.run_cli("pull_request", {"pull_request": {"draft": False}}, output)
|
|
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
plan = self.outputs(output)
|
|
self.assertEqual(
|
|
[row["os"] for row in plan["native_matrix"]],
|
|
["ubuntu-24.04", "windows-2022", "macos-26", "windows-11-arm", "macos-26-intel"],
|
|
)
|
|
self.assertEqual(
|
|
[row["os"] for row in plan["release_matrix"]],
|
|
["macos-26", "macos-26-intel"],
|
|
)
|
|
|
|
def test_success_appends_outputs_without_replacing_existing_lines(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
output = Path(directory) / "github-output"
|
|
output.write_text("existing=preserved\n", encoding="utf-8")
|
|
result = self.run_cli("push", {}, output)
|
|
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
lines = output.read_text(encoding="utf-8").splitlines()
|
|
self.assertEqual(lines[0], "existing=preserved")
|
|
self.assertTrue(lines[1].startswith("native_matrix=[{"))
|
|
self.assertTrue(lines[2].startswith("release_matrix=[{"))
|
|
|
|
def test_merge_group_and_non_pr_events_have_full_coverage(self) -> None:
|
|
events = ("merge_group", "push", "workflow_dispatch", "workflow_call", "schedule")
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
for event_name in events:
|
|
with self.subTest(event_name=event_name):
|
|
output = Path(directory) / f"{event_name}.out"
|
|
result = self.run_cli(event_name, {}, output)
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
plan = self.outputs(output)
|
|
self.assertEqual(
|
|
[row["os"] for row in plan["native_matrix"]],
|
|
["ubuntu-24.04", "windows-2022", "macos-26", "windows-11-arm", "macos-26-intel"],
|
|
)
|
|
self.assertEqual(
|
|
[row["os"] for row in plan["release_matrix"]],
|
|
["macos-26", "macos-26-intel"],
|
|
)
|
|
|
|
def test_invalid_events_fail_without_matrix_outputs(self) -> None:
|
|
cases = (
|
|
("pull_request", {}, "missing pull request"),
|
|
("pull_request", {"pull_request": []}, "wrong pull request type"),
|
|
("pull_request", {"pull_request": {}}, "missing draft"),
|
|
("pull_request", {"pull_request": {"draft": "false"}}, "wrong draft type"),
|
|
("pull_request", {"pull_request": {"draft": 0}}, "integer draft type"),
|
|
("unknown", {}, "unknown event"),
|
|
("push", [], "non-object payload"),
|
|
)
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
for event_name, payload, label in cases:
|
|
with self.subTest(label=label):
|
|
output = Path(directory) / f"{label.replace(' ', '-')}.out"
|
|
output.write_text("existing=preserved\n", encoding="utf-8")
|
|
result = self.run_cli(event_name, payload, output)
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn("ci_plan:", result.stderr)
|
|
self.assertEqual(output.read_text(encoding="utf-8"), "existing=preserved\n")
|
|
|
|
def test_malformed_json_fails_without_creating_output(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
event_path = root / "broken.json"
|
|
output = root / "github-output"
|
|
event_path.write_text('{"pull_request":', encoding="utf-8")
|
|
result = subprocess.run(
|
|
[
|
|
sys.executable,
|
|
str(SCRIPT),
|
|
"--event-name",
|
|
"pull_request",
|
|
"--event-path",
|
|
str(event_path),
|
|
"--output",
|
|
str(output),
|
|
],
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
)
|
|
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertFalse(output.exists())
|
|
|
|
def test_non_standard_json_constant_is_rejected(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
event_path = root / "non-standard.json"
|
|
output = root / "github-output"
|
|
event_path.write_text('{"value":NaN}', encoding="utf-8")
|
|
result = subprocess.run(
|
|
[
|
|
sys.executable,
|
|
str(SCRIPT),
|
|
"--event-name",
|
|
"push",
|
|
"--event-path",
|
|
str(event_path),
|
|
"--output",
|
|
str(output),
|
|
],
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
)
|
|
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertFalse(output.exists())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|