Files
horizon-airdef/tests/test_parity_main.py
Ludovic Marie 9e76f8c4f0 parity-main: a failure that passes again at HEAD counts as a pass
Twice today (runs 40301239508, 40311712302) mob_ai failed in parity-main, passed when bisect ran it again at
the same HEAD, and the run still ended red, so no game release could follow main for three hours. The
harness step now continues on error and the bisect step gives the verdict: 0 when every failed harness
passes again at HEAD, 1 when they fail at the base too or a merge is named.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 14:12:04 +01:00

74 lines
4.2 KiB
Python

"""tools/parity_main.py: the base of a parity-main run, and the bisect after a failure."""
from pathlib import Path
import tempfile
import unittest
from unittest import mock
from tools import parity_main
class ParityMainTests(unittest.TestCase):
def test_first_bad_finds_the_first_failing_merge(self):
commits = list("abcdefgh") # h failed; the base before a passed
for culprit in commits:
tried = []
fails = lambda commit: tried.append(commit) or commit >= culprit
self.assertEqual(parity_main.first_bad(commits, fails), culprit)
self.assertLessEqual(len(tried), 3, "log2 of 8 merges")
self.assertEqual(parity_main.first_bad(["a"], lambda commit: self.fail("nothing to try")), "a")
def test_base_is_the_newest_green_run_head_contains(self):
with tempfile.TemporaryDirectory() as tmp, mock.patch.object(parity_main, "ROOT", Path(tmp)):
git = lambda *arguments: parity_main.git(*arguments).stdout.strip()
git("init", "-q")
for n in range(3):
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-q", "--allow-empty", "-m", str(n))
head, parent, grandparent = git("rev-parse", "HEAD"), git("rev-parse", "HEAD^1"), git("rev-parse", "HEAD~2")
git("checkout", "-q", "-b", "side", grandparent)
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-q", "--allow-empty", "-m", "side")
side = git("rev-parse", "HEAD")
git("checkout", "-q", head)
self.assertEqual(parity_main.base(runs=[side, grandparent, parent]), grandparent,
"a run on a commit HEAD doesn't contain is skipped")
self.assertEqual(parity_main.base(runs=[]), parent, "no green run yet: the last merge")
self.assertEqual(parity_main.base(runs=[head]), head)
def test_bisect_names_a_merge_only_when_head_fails_and_the_base_passes(self):
with tempfile.TemporaryDirectory() as tmp, mock.patch.object(parity_main, "ROOT", Path(tmp)):
git = lambda *arguments: parity_main.git(*arguments).stdout.strip()
git("init", "-q")
for n in range(4):
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-q", "--allow-empty",
"-m", f"Merge pull request #{n} from x/y")
commits = git("rev-list", "--reverse", "HEAD").split()
last_run = Path(tmp) / ".local/parity-runner/last-run.json"
last_run.parent.mkdir(parents=True)
last_run.write_text('{"failed": ["music"]}')
real_run = parity_main.subprocess.run
for broken_from, blamed, code in ((None, None, 0), (0, None, 1), (2, "2", 1)):
comments = []
# git runs for real in the scratch repo; a `gh pr comment` is only recorded, never sent.
fake_run = lambda command, **kwargs: (comments.append(command) if command[0] == "gh"
else real_run(command, **kwargs))
with mock.patch.object(parity_main, "LAST_RUN", last_run), \
mock.patch.object(parity_main, "run_harnesses", lambda names, timeout: broken_from is not None
and commits.index(git("rev-parse", "HEAD")) >= broken_from), \
mock.patch.object(parity_main.subprocess, "run", fake_run), \
mock.patch("builtins.print") as printed:
self.assertEqual(parity_main.bisect(commits[0], 60, comment=True), code,
"0 only when the failures pass again at HEAD")
if blamed:
self.assertEqual([command[3] for command in comments], [blamed])
else:
self.assertEqual(comments, [], printed.call_args_list[-1])
self.assertEqual(git("rev-parse", "HEAD"), commits[-1], "HEAD checked out again")
def test_a_harness_a_commit_does_not_have_cannot_fail_there(self):
with mock.patch.object(parity_main.subprocess, "run") as run:
self.assertFalse(parity_main.run_harnesses(["not_a_harness_yet"], 60))
run.assert_not_called()
if __name__ == "__main__":
unittest.main()