874b6f20c2
Основа — Horizon (MIT). История апстрима не переносится: этот репозиторий ведёт свою.
74 lines
4.2 KiB
Python
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()
|