"""Regression tests for macOS release planner source identity evidence."""
from __future__ import annotations
import importlib.util
import os
import subprocess
import tempfile
import unittest
from pathlib import Path
SCRIPT = Path(__file__).with_name("desktop-release-source-identity.py")
SPEC = importlib.util.spec_from_file_location("desktop_release_source_identity", SCRIPT)
assert SPEC and SPEC.loader
identity = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(identity)
PLANNED_SHA = "a" * 40
CHANGELOG_SHA = "b" * 40
MAIN_SHA = "c" * 40
PR_URL = "https://github.com/BasedHardware/omi/pull/12345"
RELEASE_TAG = "v1.2.3+10203-macos"
class DesktopReleaseSourceIdentityTests(unittest.TestCase):
def test_direct_candidate_stays_on_green_source_when_main_advances(self) -> None:
evidence = identity.build_evidence(
release_tag=RELEASE_TAG,
planned_source_sha=PLANNED_SHA,
candidate_source_sha=PLANNED_SHA,
origin_main_sha=MAIN_SHA,
)
self.assertEqual(evidence["schema"], "desktop-release-planner-source-identity/v3")
self.assertEqual(evidence["mode"], "direct")
self.assertEqual(evidence["candidate_source_sha"], PLANNED_SHA)
self.assertEqual(evidence["origin_main_sha"], MAIN_SHA)
def test_changelog_candidate_is_the_changelog_only_child(self) -> None:
evidence = identity.build_evidence(
release_tag=RELEASE_TAG,
planned_source_sha=PLANNED_SHA,
candidate_source_sha=CHANGELOG_SHA,
origin_main_sha=MAIN_SHA,
changelog_parent_sha=PLANNED_SHA,
changelog_commit=CHANGELOG_SHA,
changelog_pr=PR_URL,
)
self.assertEqual(evidence["mode"], "changelog-only")
self.assertEqual(evidence["candidate_source_sha"], CHANGELOG_SHA)
self.assertEqual(evidence["changelog_parent_sha"], PLANNED_SHA)
def test_direct_candidate_cannot_absorb_a_later_main_tip(self) -> None:
with self.assertRaisesRegex(ValueError, "direct candidate must exactly equal"):
identity.build_evidence(
release_tag=RELEASE_TAG,
planned_source_sha=PLANNED_SHA,
candidate_source_sha=MAIN_SHA,
origin_main_sha=MAIN_SHA,
)
def test_rejects_a_noncanonical_candidate_tag(self) -> None:
with self.assertRaisesRegex(ValueError, "release_tag must be an exact"):
identity.build_evidence(
release_tag="v1.2.3-macos",
planned_source_sha=PLANNED_SHA,
candidate_source_sha=PLANNED_SHA,
origin_main_sha=MAIN_SHA,
)
def _git(self, repository: Path, *args: str) -> str:
environment = {name: value for name, value in os.environ.items() if not name.startswith("GIT_")}
return subprocess.run(
["git", "-C", str(repository), *args],
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=environment,
).stdout.strip()
def _commit(self, repository: Path, path: str, contents: str, message: str) -> str:
file_path = repository / path
file_path.parent.mkdir(parents=True, exist_ok=True)
file_path.write_text(contents, encoding="utf-8")
self._git(repository, "add", path)
self._git(repository, "commit", "-m", message)
return self._git(repository, "rev-parse", "HEAD")
def _repository_with_planned_source(self) -> tuple[tempfile.TemporaryDirectory[str], Path, str, str]:
directory = tempfile.TemporaryDirectory()
repository = Path(directory.name)
self._git(repository, "init")
self._git(repository, "config", "user.name", "Release test")
self._git(repository, "config", "user.email", "release-test@example.com")
main_branch = self._git(repository, "branch", "--show-current")
planned_source_sha = self._commit(
repository,
"desktop/macos/Desktop/Sources/App.swift",
"let releaseSource = true\n",
"desktop source",
)
return directory, repository, main_branch, planned_source_sha
def test_busy_main_does_not_invalidate_the_exact_green_source(self) -> None:
"""Regression for issue #11936: later desktop merges belong to the next train."""
directory, repository, _main_branch, planned_source_sha = self._repository_with_planned_source()
with directory:
main_sha = self._commit(
repository,
"desktop/macos/Desktop/Sources/Newer.swift",
"let nextTrain = true\n",
"newer desktop source",
)
identity.ensure_candidate_history_is_safe(
repository_root=repository,
planned_source_sha=planned_source_sha,
candidate_source_sha=planned_source_sha,
origin_main_sha=main_sha,
)
def test_changelog_only_child_survives_concurrent_desktop_merges(self) -> None:
directory, repository, main_branch, planned_source_sha = self._repository_with_planned_source()
with directory:
self._git(repository, "checkout", "-b", "changelog", planned_source_sha)
changelog_sha = self._commit(
repository,
"desktop/macos/changelog/releases/1.2.3.json",
'{"version":"1.2.3"}\n',
"chore: consolidate changelog for v1.2.3",
)
self._git(repository, "checkout", main_branch)
self._commit(
repository,
"desktop/macos/Desktop/Sources/Newer.swift",
"let nextTrain = true\n",
"concurrent desktop source",
)
self._git(repository, "merge", "--no-ff", "changelog", "-m", "regular changelog merge")
main_sha = self._git(repository, "rev-parse", "HEAD")
identity.ensure_candidate_history_is_safe(
repository_root=repository,
planned_source_sha=planned_source_sha,
candidate_source_sha=changelog_sha,
origin_main_sha=main_sha,
changelog_commit=changelog_sha,
changelog_parent_sha=planned_source_sha,
)
def test_rejects_candidate_not_reachable_from_main(self) -> None:
directory, repository, main_branch, planned_source_sha = self._repository_with_planned_source()
with directory:
self._git(repository, "checkout", "-b", "unmerged", planned_source_sha)
changelog_sha = self._commit(
repository,
"desktop/macos/changelog/releases/1.2.3.json",
'{}\n',
"unmerged changelog",
)
self._git(repository, "checkout", main_branch)
main_sha = self._git(repository, "rev-parse", "HEAD")
with self.assertRaisesRegex(ValueError, "reachable from fresh origin/main"):
identity.ensure_candidate_history_is_safe(
repository_root=repository,
planned_source_sha=planned_source_sha,
candidate_source_sha=changelog_sha,
origin_main_sha=main_sha,
changelog_commit=changelog_sha,
changelog_parent_sha=planned_source_sha,
)
def test_rejects_non_changelog_content_in_candidate_child(self) -> None:
directory, repository, _main_branch, planned_source_sha = self._repository_with_planned_source()
with directory:
candidate_sha = self._commit(
repository,
"desktop/macos/Desktop/Sources/Ungated.swift",
"let ungated = true\n",
"not changelog only",
)
with self.assertRaisesRegex(ValueError, "non-changelog paths"):
identity.ensure_candidate_history_is_safe(
repository_root=repository,
planned_source_sha=planned_source_sha,
candidate_source_sha=candidate_sha,
origin_main_sha=candidate_sha,
changelog_commit=candidate_sha,
changelog_parent_sha=planned_source_sha,
)
if __name__ == "__main__":
unittest.main()