529 lines
20 KiB
Python
529 lines
20 KiB
Python
#!/usr/bin/env python3
|
|||
|
|
"""Behavior tests for scripts/set_native_revision.sh.
|
||
|
|
|
||
|
|
The script moves ten pin sites at once and promises all-or-nothing: either
|
||
|
|
every Apple requirement, every SwiftPM lock, and mpv-build.lock.json move to
|
||
|
|
the new commit together, or nothing is written. These tests drive the real
|
||
|
|
script end to end against a synthetic Plezy tree and a real (temporary) git
|
||
|
|
checkout of the unified mpv-build repo, because the failure that matters --
|
||
|
|
a half-moved pin -- can only happen across process and file boundaries.
|
||
|
|
|
||
|
|
The pin-site fixtures reproduce the exact anchors the rewrites key on: the
|
||
|
|
pbxproj carries the `/* XCRemoteSwiftPackageReference "MPVKit" */ = {` object
|
||
|
|
plus the same comment inside a packageReferences list and a product
|
||
|
|
dependency (which must NOT match), and the SwiftPM locks are version-3 files
|
||
|
|
with an unrelated pin that must survive untouched. The fixtures are staged at
|
||
|
|
the pre-flip edde746/MPVKit state, so every successful run also exercises the
|
||
|
|
repo flip to edde746/mpv-build: repositoryURL, SwiftPM identity, and the
|
||
|
|
pbxproj comment strings move with the revision in one all-or-nothing pass.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import json
|
||
|
|
import http.server
|
||
|
|
import os
|
||
|
|
import shutil
|
||
|
|
import subprocess
|
||
|
|
import tempfile
|
||
|
|
import threading
|
||
|
|
import unittest
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
||
|
|
SCRIPT = SCRIPT_DIR / "set_native_revision.sh"
|
||
|
|
|
||
|
|
OLD_SHA = "b" * 40
|
||
|
|
OLD_URL = "https://github.com/edde746/MPVKit"
|
||
|
|
NEW_REPO = "edde746/mpv-build"
|
||
|
|
NEW_URL = f"https://github.com/{NEW_REPO}"
|
||
|
|
NEW_KEY_ANDROID = "aa11bb22cc33"
|
||
|
|
NEW_KEY_LINUX = "dd44ee55ff66"
|
||
|
|
|
||
|
|
PBXPROJ_TEMPLATE = """\
|
||
|
|
// !$*UTF8*$!
|
||
|
|
{{
|
||
|
|
\tobjects = {{
|
||
|
|
\t\t97C146E61CF9000F007C117D /* Project object */ = {{
|
||
|
|
\t\t\tisa = PBXProject;
|
||
|
|
\t\t\tpackageReferences = (
|
||
|
|
\t\t\t\tCEE842A5A25923620300AB90 /* XCRemoteSwiftPackageReference "MPVKit" */,
|
||
|
|
\t\t\t);
|
||
|
|
\t\t}};
|
||
|
|
\t\tCEE842A5A25923620300AB90 /* XCRemoteSwiftPackageReference "MPVKit" */ = {{
|
||
|
|
\t\t\tisa = XCRemoteSwiftPackageReference;
|
||
|
|
\t\t\trepositoryURL = "https://github.com/edde746/MPVKit";
|
||
|
|
\t\t\trequirement = {{
|
||
|
|
\t\t\t\tkind = revision;
|
||
|
|
\t\t\t\trevision = {revision};
|
||
|
|
\t\t\t}};
|
||
|
|
\t\t}};
|
||
|
|
\t\tEA0F4263E7B912702C490108 /* MPVKit */ = {{
|
||
|
|
\t\t\tisa = XCSwiftPackageProductDependency;
|
||
|
|
\t\t\tpackage = CEE842A5A25923620300AB90 /* XCRemoteSwiftPackageReference "MPVKit" */;
|
||
|
|
\t\t\tproductName = MPVKit;
|
||
|
|
\t\t}};
|
||
|
|
\t}};
|
||
|
|
\trootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||
|
|
}}
|
||
|
|
"""
|
||
|
|
|
||
|
|
RESOLVED_TEMPLATE = {
|
||
|
|
"originHash": "c535d3cdb62bd4e11e67f3c9e68290fa7ee9306634c5f56d2b0396eb75ed41ee",
|
||
|
|
"pins": [
|
||
|
|
{
|
||
|
|
"identity": "mpvkit",
|
||
|
|
"kind": "remoteSourceControl",
|
||
|
|
"location": "https://github.com/edde746/MPVKit",
|
||
|
|
"state": {"revision": OLD_SHA},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"identity": "unrelated",
|
||
|
|
"kind": "remoteSourceControl",
|
||
|
|
"location": "https://example.invalid/unrelated",
|
||
|
|
"state": {"revision": "c" * 40},
|
||
|
|
},
|
||
|
|
],
|
||
|
|
"version": 3,
|
||
|
|
}
|
||
|
|
|
||
|
|
VERSIONS_FIXTURE = {"formatVersion": 1, "components": {"mpv": {"version": "0.40.0"}}}
|
||
|
|
|
||
|
|
|
||
|
|
def android_section() -> dict:
|
||
|
|
return {
|
||
|
|
"assetBase": "https://github.com/edde746/MPVKit/releases/download/binaries-android",
|
||
|
|
"libraries": {
|
||
|
|
"libmpv-android": {
|
||
|
|
"key": NEW_KEY_ANDROID,
|
||
|
|
"prebuilt": {
|
||
|
|
abi: {
|
||
|
|
"asset": f"libmpv-android-{NEW_KEY_ANDROID}-{abi}.tar.gz",
|
||
|
|
"checksum": format(index, "x") * 64,
|
||
|
|
}
|
||
|
|
for index, abi in enumerate(
|
||
|
|
("arm64-v8a", "armeabi-v7a", "x86", "x86_64"), start=1
|
||
|
|
)
|
||
|
|
},
|
||
|
|
}
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def linux_section() -> dict:
|
||
|
|
return {
|
||
|
|
"assetBase": "https://github.com/edde746/MPVKit/releases/download/binaries-linux",
|
||
|
|
"libraries": {
|
||
|
|
"libmpv-linux": {
|
||
|
|
"key": NEW_KEY_LINUX,
|
||
|
|
"prebuilt": {
|
||
|
|
"x86_64": {
|
||
|
|
"asset": f"libmpv-linux-{NEW_KEY_LINUX}-x86_64.tar.gz",
|
||
|
|
"checksum": "5" * 64,
|
||
|
|
}
|
||
|
|
},
|
||
|
|
}
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def apple_section() -> dict:
|
||
|
|
return {
|
||
|
|
"assetBase": "https://github.com/edde746/MPVKit/releases/download/binaries-apple",
|
||
|
|
"libraries": {
|
||
|
|
"libmpv": {
|
||
|
|
"frameworks": {
|
||
|
|
"Libmpv": {
|
||
|
|
"asset": "Libmpv-e73a34ad67f2.xcframework.zip",
|
||
|
|
"checksum": "6" * 64,
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"key": "e73a34ad67f2",
|
||
|
|
"prebuilt": {"ios": "libmpv-all-e73a34ad67f2-ios.zip"},
|
||
|
|
}
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def artifacts_fixture() -> dict:
|
||
|
|
# windows deliberately absent: its omission note is part of the contract.
|
||
|
|
return {
|
||
|
|
"platforms": {
|
||
|
|
"apple": apple_section(),
|
||
|
|
"android": android_section(),
|
||
|
|
"linux": linux_section(),
|
||
|
|
},
|
||
|
|
"schema": 2,
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
class SetNativeRevisionTest(unittest.TestCase):
|
||
|
|
def setUp(self) -> None:
|
||
|
|
self.temporary = Path(tempfile.mkdtemp(prefix="set-native-revision-test."))
|
||
|
|
self.addCleanup(shutil.rmtree, self.temporary, ignore_errors=True)
|
||
|
|
self.root = self.temporary / "plezy"
|
||
|
|
self._stage_plezy_tree()
|
||
|
|
|
||
|
|
# ---- fixtures ---------------------------------------------------------
|
||
|
|
|
||
|
|
def _stage_plezy_tree(self) -> None:
|
||
|
|
scripts = self.root / "scripts"
|
||
|
|
scripts.mkdir(parents=True)
|
||
|
|
shutil.copy2(SCRIPT, scripts / SCRIPT.name)
|
||
|
|
for platform in ("ios", "macos", "tvos"):
|
||
|
|
base = self.root / platform
|
||
|
|
(base / "Runner.xcodeproj").mkdir(parents=True)
|
||
|
|
self._pbxproj(platform).write_text(
|
||
|
|
PBXPROJ_TEMPLATE.format(revision=OLD_SHA), encoding="utf-8"
|
||
|
|
)
|
||
|
|
for lock in self._resolved(platform):
|
||
|
|
lock.parent.mkdir(parents=True)
|
||
|
|
lock.write_text(
|
||
|
|
json.dumps(RESOLVED_TEMPLATE, indent=2) + "\n", encoding="utf-8"
|
||
|
|
)
|
||
|
|
|
||
|
|
def _pbxproj(self, platform: str) -> Path:
|
||
|
|
return self.root / platform / "Runner.xcodeproj" / "project.pbxproj"
|
||
|
|
|
||
|
|
def _resolved(self, platform: str) -> list[Path]:
|
||
|
|
return [
|
||
|
|
self.root
|
||
|
|
/ platform
|
||
|
|
/ "Runner.xcodeproj"
|
||
|
|
/ "project.xcworkspace"
|
||
|
|
/ "xcshareddata"
|
||
|
|
/ "swiftpm"
|
||
|
|
/ "Package.resolved",
|
||
|
|
self.root
|
||
|
|
/ platform
|
||
|
|
/ "Runner.xcworkspace"
|
||
|
|
/ "xcshareddata"
|
||
|
|
/ "swiftpm"
|
||
|
|
/ "Package.resolved",
|
||
|
|
]
|
||
|
|
|
||
|
|
def _pin_sites(self) -> list[Path]:
|
||
|
|
sites = []
|
||
|
|
for platform in ("ios", "macos", "tvos"):
|
||
|
|
sites.append(self._pbxproj(platform))
|
||
|
|
sites.extend(self._resolved(platform))
|
||
|
|
return sites
|
||
|
|
|
||
|
|
def _make_mpv_build(
|
||
|
|
self, name: str = "mpv-build", *, artifacts: dict | None = None, manifests: bool = True
|
||
|
|
) -> tuple[Path, str]:
|
||
|
|
"""A real git checkout carrying the manifests, and its HEAD commit."""
|
||
|
|
repo = self.temporary / name
|
||
|
|
repo.mkdir()
|
||
|
|
if manifests:
|
||
|
|
(repo / "artifacts.json").write_text(
|
||
|
|
json.dumps(artifacts or artifacts_fixture(), indent=2, sort_keys=True) + "\n",
|
||
|
|
encoding="utf-8",
|
||
|
|
)
|
||
|
|
(repo / "versions.json").write_text(
|
||
|
|
json.dumps(VERSIONS_FIXTURE, indent=2, sort_keys=True) + "\n",
|
||
|
|
encoding="utf-8",
|
||
|
|
)
|
||
|
|
else:
|
||
|
|
(repo / "README.md").write_text("pre-migration MPVKit\n", encoding="utf-8")
|
||
|
|
git = self._git(repo)
|
||
|
|
subprocess.run(git[:3] + ["init", "-q"], check=True)
|
||
|
|
subprocess.run(git + ["add", "-A"], check=True)
|
||
|
|
subprocess.run(git + ["commit", "-q", "-m", "fixture"], check=True)
|
||
|
|
head = subprocess.run(
|
||
|
|
git[:3] + ["rev-parse", "HEAD"], check=True, capture_output=True, text=True
|
||
|
|
).stdout.strip()
|
||
|
|
return repo, head
|
||
|
|
|
||
|
|
def _git(self, repo: Path) -> list[str]:
|
||
|
|
return [
|
||
|
|
"git",
|
||
|
|
"-C",
|
||
|
|
str(repo),
|
||
|
|
"-c",
|
||
|
|
"user.name=fixture",
|
||
|
|
"-c",
|
||
|
|
"user.email=fixture@example.invalid",
|
||
|
|
"-c",
|
||
|
|
"commit.gpgsign=false",
|
||
|
|
]
|
||
|
|
|
||
|
|
def _advance(self, repo: Path) -> str:
|
||
|
|
"""A second commit in the fixture repo, so the pin can move again."""
|
||
|
|
(repo / "CHANGELOG").write_text("moved\n", encoding="utf-8")
|
||
|
|
git = self._git(repo)
|
||
|
|
subprocess.run(git + ["add", "-A"], check=True)
|
||
|
|
subprocess.run(git + ["commit", "-q", "-m", "fixture 2"], check=True)
|
||
|
|
return subprocess.run(
|
||
|
|
git[:3] + ["rev-parse", "HEAD"], check=True, capture_output=True, text=True
|
||
|
|
).stdout.strip()
|
||
|
|
|
||
|
|
def _run(self, *args: str) -> subprocess.CompletedProcess:
|
||
|
|
return subprocess.run(
|
||
|
|
["bash", str(self.root / "scripts" / SCRIPT.name), *args],
|
||
|
|
check=False,
|
||
|
|
capture_output=True,
|
||
|
|
text=True,
|
||
|
|
)
|
||
|
|
|
||
|
|
def _snapshot(self) -> dict[Path, str]:
|
||
|
|
"""Byte content of every pin site that exists right now."""
|
||
|
|
return {
|
||
|
|
site: site.read_text(encoding="utf-8")
|
||
|
|
for site in self._pin_sites()
|
||
|
|
if site.exists()
|
||
|
|
}
|
||
|
|
|
||
|
|
def _expected_lock(self, commit: str, repo: str = NEW_REPO) -> str:
|
||
|
|
artifacts = {}
|
||
|
|
for group, section in (("android", android_section()), ("linux", linux_section())):
|
||
|
|
((_, entry),) = section["libraries"].items()
|
||
|
|
artifacts[group] = {
|
||
|
|
"assetBase": section["assetBase"],
|
||
|
|
"assets": entry["prebuilt"],
|
||
|
|
"key": entry["key"],
|
||
|
|
}
|
||
|
|
lock = {
|
||
|
|
"artifacts": artifacts,
|
||
|
|
"commit": commit,
|
||
|
|
"formatVersion": 1,
|
||
|
|
"repo": repo,
|
||
|
|
}
|
||
|
|
return json.dumps(lock, indent=2, sort_keys=True) + "\n"
|
||
|
|
|
||
|
|
# ---- the pin moves as one unit ----------------------------------------
|
||
|
|
|
||
|
|
def _assert_pinned(self, commit: str, url: str = NEW_URL) -> None:
|
||
|
|
"""All nine Apple sites name url@commit with coherent names/identities."""
|
||
|
|
name = url.rsplit("/", 1)[-1]
|
||
|
|
for platform in ("ios", "macos", "tvos"):
|
||
|
|
pbxproj = self._pbxproj(platform).read_text(encoding="utf-8")
|
||
|
|
self.assertIn(f"revision = {commit};", pbxproj)
|
||
|
|
self.assertIn(f'repositoryURL = "{url}";', pbxproj)
|
||
|
|
# Xcode derives these comments from the URL's last path component;
|
||
|
|
# all three mentions (packageReferences list, object definition,
|
||
|
|
# product dependency) must carry the same name.
|
||
|
|
self.assertEqual(
|
||
|
|
pbxproj.count(f'/* XCRemoteSwiftPackageReference "{name}" */'), 3
|
||
|
|
)
|
||
|
|
# The product name comes from Package.swift, not the URL.
|
||
|
|
self.assertIn("productName = MPVKit;", pbxproj)
|
||
|
|
for lock in self._resolved(platform):
|
||
|
|
resolved = json.loads(lock.read_text(encoding="utf-8"))
|
||
|
|
by_identity = {pin["identity"]: pin for pin in resolved["pins"]}
|
||
|
|
pin = by_identity[name.lower()]
|
||
|
|
self.assertEqual(pin["location"], url)
|
||
|
|
self.assertEqual(pin["state"]["revision"], commit)
|
||
|
|
# Targeted edit: the unrelated pin and originHash never churn.
|
||
|
|
self.assertEqual(by_identity["unrelated"]["state"]["revision"], "c" * 40)
|
||
|
|
self.assertEqual(
|
||
|
|
by_identity["unrelated"]["location"], "https://example.invalid/unrelated"
|
||
|
|
)
|
||
|
|
self.assertEqual(resolved["originHash"], RESOLVED_TEMPLATE["originHash"])
|
||
|
|
|
||
|
|
def test_the_flip_rewrites_every_pin_site_and_derives_the_lock(self) -> None:
|
||
|
|
# The tree starts at the pre-flip edde746/MPVKit pin; a run against the
|
||
|
|
# unified repo must move URL, identity, comments, and revision at all
|
||
|
|
# nine Apple sites and derive the lock's repo from the source used.
|
||
|
|
repo, commit = self._make_mpv_build()
|
||
|
|
|
||
|
|
result = self._run(commit, "--repo", str(repo))
|
||
|
|
|
||
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
||
|
|
self._assert_pinned(commit)
|
||
|
|
for platform in ("ios", "macos", "tvos"):
|
||
|
|
pbxproj = self._pbxproj(platform).read_text(encoding="utf-8")
|
||
|
|
self.assertNotIn(OLD_SHA, pbxproj)
|
||
|
|
self.assertNotIn(OLD_URL, pbxproj)
|
||
|
|
self.assertNotIn('XCRemoteSwiftPackageReference "MPVKit"', pbxproj)
|
||
|
|
self.assertEqual(
|
||
|
|
(self.root / "mpv-build.lock.json").read_text(encoding="utf-8"),
|
||
|
|
self._expected_lock(commit),
|
||
|
|
)
|
||
|
|
self.assertIn(
|
||
|
|
"note: no windows artifacts published at this commit; "
|
||
|
|
"omitted from mpv-build.lock.json",
|
||
|
|
result.stdout,
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_default_source_is_the_sibling_checkout(self) -> None:
|
||
|
|
# No --repo: the ../mpv-build convention must find the checkout
|
||
|
|
# sitting next to the Plezy tree.
|
||
|
|
_, commit = self._make_mpv_build()
|
||
|
|
|
||
|
|
result = self._run(commit)
|
||
|
|
|
||
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
||
|
|
self.assertIn("../mpv-build", result.stdout)
|
||
|
|
self.assertIn(f"revision = {commit};", self._pbxproj("ios").read_text(encoding="utf-8"))
|
||
|
|
|
||
|
|
def test_remote_source_serves_the_manifests_by_commit(self) -> None:
|
||
|
|
docroot = self.temporary / "raw"
|
||
|
|
commit = "d" * 40
|
||
|
|
(docroot / commit).mkdir(parents=True)
|
||
|
|
(docroot / commit / "artifacts.json").write_text(
|
||
|
|
json.dumps(artifacts_fixture()), encoding="utf-8"
|
||
|
|
)
|
||
|
|
(docroot / commit / "versions.json").write_text(
|
||
|
|
json.dumps(VERSIONS_FIXTURE), encoding="utf-8"
|
||
|
|
)
|
||
|
|
handler = lambda *args, **kwargs: http.server.SimpleHTTPRequestHandler( # noqa: E731
|
||
|
|
*args, directory=str(docroot), **kwargs
|
||
|
|
)
|
||
|
|
server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), handler)
|
||
|
|
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||
|
|
thread.start()
|
||
|
|
self.addCleanup(server.server_close)
|
||
|
|
self.addCleanup(server.shutdown)
|
||
|
|
|
||
|
|
result = self._run(commit, "--repo", f"http://127.0.0.1:{server.server_address[1]}")
|
||
|
|
|
||
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
||
|
|
self.assertEqual(
|
||
|
|
(self.root / "mpv-build.lock.json").read_text(encoding="utf-8"),
|
||
|
|
self._expected_lock(commit),
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_rerun_with_the_same_commit_is_a_reported_noop(self) -> None:
|
||
|
|
repo, commit = self._make_mpv_build()
|
||
|
|
first = self._run(commit, "--repo", str(repo))
|
||
|
|
self.assertEqual(first.returncode, 0, first.stderr)
|
||
|
|
before = self._snapshot()
|
||
|
|
lock_before = (self.root / "mpv-build.lock.json").read_text(encoding="utf-8")
|
||
|
|
|
||
|
|
second = self._run(commit, "--repo", str(repo))
|
||
|
|
|
||
|
|
self.assertEqual(second.returncode, 0, second.stderr)
|
||
|
|
self.assertIn("nothing to do", second.stdout)
|
||
|
|
self.assertEqual(self._snapshot(), before)
|
||
|
|
self.assertEqual(
|
||
|
|
(self.root / "mpv-build.lock.json").read_text(encoding="utf-8"), lock_before
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_a_move_within_the_target_repo_only_touches_revisions(self) -> None:
|
||
|
|
# Once flipped, a later pin move must not churn URLs, identities, or
|
||
|
|
# comment names -- only the revisions and the lock's commit change.
|
||
|
|
repo, first_commit = self._make_mpv_build()
|
||
|
|
first = self._run(first_commit, "--repo", str(repo))
|
||
|
|
self.assertEqual(first.returncode, 0, first.stderr)
|
||
|
|
second_commit = self._advance(repo)
|
||
|
|
|
||
|
|
result = self._run(second_commit, "--repo", str(repo))
|
||
|
|
|
||
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
||
|
|
self._assert_pinned(second_commit)
|
||
|
|
self.assertEqual(
|
||
|
|
(self.root / "mpv-build.lock.json").read_text(encoding="utf-8"),
|
||
|
|
self._expected_lock(second_commit),
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_lock_repo_derives_from_the_local_checkouts_origin_remote(self) -> None:
|
||
|
|
# The pin names whichever GitHub repo actually served the commit, so a
|
||
|
|
# checkout cloned from a fork flips every site to that fork.
|
||
|
|
repo, commit = self._make_mpv_build()
|
||
|
|
subprocess.run(
|
||
|
|
self._git(repo)[:3]
|
||
|
|
+ ["remote", "add", "origin", "git@github.com:someone/mpv-build.git"],
|
||
|
|
check=True,
|
||
|
|
)
|
||
|
|
|
||
|
|
result = self._run(commit, "--repo", str(repo))
|
||
|
|
|
||
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
||
|
|
self._assert_pinned(commit, url="https://github.com/someone/mpv-build")
|
||
|
|
self.assertEqual(
|
||
|
|
(self.root / "mpv-build.lock.json").read_text(encoding="utf-8"),
|
||
|
|
self._expected_lock(commit, repo="someone/mpv-build"),
|
||
|
|
)
|
||
|
|
|
||
|
|
# ---- refusals leave every byte alone ----------------------------------
|
||
|
|
|
||
|
|
def test_refuses_a_malformed_sha(self) -> None:
|
||
|
|
before = self._snapshot()
|
||
|
|
|
||
|
|
result = self._run("deadbeef")
|
||
|
|
|
||
|
|
self.assertEqual(result.returncode, 2)
|
||
|
|
self.assertIn("not a full 40-character lowercase commit sha", result.stderr)
|
||
|
|
self.assertEqual(self._snapshot(), before)
|
||
|
|
|
||
|
|
def test_refuses_when_a_pin_site_is_missing(self) -> None:
|
||
|
|
repo, commit = self._make_mpv_build()
|
||
|
|
removed = self._resolved("macos")[1]
|
||
|
|
removed.unlink()
|
||
|
|
before = self._snapshot()
|
||
|
|
|
||
|
|
result = self._run(commit, "--repo", str(repo))
|
||
|
|
|
||
|
|
self.assertEqual(result.returncode, 1)
|
||
|
|
self.assertIn("missing native pin site(s)", result.stderr)
|
||
|
|
self.assertIn(str(removed.relative_to(self.root)), result.stderr)
|
||
|
|
self.assertEqual(self._snapshot(), before)
|
||
|
|
self.assertFalse((self.root / "mpv-build.lock.json").exists())
|
||
|
|
|
||
|
|
def test_an_ambiguous_edit_aborts_before_any_write(self) -> None:
|
||
|
|
repo, commit = self._make_mpv_build()
|
||
|
|
pbxproj = self._pbxproj("tvos")
|
||
|
|
content = pbxproj.read_text(encoding="utf-8")
|
||
|
|
anchor = '/* XCRemoteSwiftPackageReference "MPVKit" */ = {'
|
||
|
|
duplicated = content.replace(
|
||
|
|
"\trootObject",
|
||
|
|
"\t\tDUPLICATE " + anchor + "\n"
|
||
|
|
'\t\t\trepositoryURL = "https://github.com/edde746/MPVKit";\n'
|
||
|
|
"\t\t\trequirement = {\n"
|
||
|
|
"\t\t\t\tkind = revision;\n"
|
||
|
|
f"\t\t\t\trevision = {OLD_SHA};\n"
|
||
|
|
"\t\t\t};\n"
|
||
|
|
"\t\t};\n"
|
||
|
|
"\trootObject",
|
||
|
|
)
|
||
|
|
pbxproj.write_text(duplicated, encoding="utf-8")
|
||
|
|
before = self._snapshot()
|
||
|
|
|
||
|
|
result = self._run(commit, "--repo", str(repo))
|
||
|
|
|
||
|
|
self.assertEqual(result.returncode, 1)
|
||
|
|
self.assertIn("expected exactly 1 native pin, found 2", result.stderr)
|
||
|
|
# All-or-nothing: the eight unambiguous sites must not have moved, and
|
||
|
|
# no lock may appear alongside a refused pin.
|
||
|
|
self.assertEqual(self._snapshot(), before)
|
||
|
|
self.assertFalse((self.root / "mpv-build.lock.json").exists())
|
||
|
|
|
||
|
|
def test_refuses_a_commit_that_predates_the_unified_repo(self) -> None:
|
||
|
|
repo, commit = self._make_mpv_build(name="MPVKit", manifests=False)
|
||
|
|
before = self._snapshot()
|
||
|
|
|
||
|
|
result = self._run(commit, "--repo", str(repo))
|
||
|
|
|
||
|
|
self.assertEqual(result.returncode, 1)
|
||
|
|
self.assertIn("not a unified mpv-build commit", result.stderr)
|
||
|
|
self.assertEqual(self._snapshot(), before)
|
||
|
|
|
||
|
|
def test_refuses_a_checksumless_prebuilt_asset(self) -> None:
|
||
|
|
artifacts = artifacts_fixture()
|
||
|
|
android = artifacts["platforms"]["android"]["libraries"]["libmpv-android"]
|
||
|
|
android["prebuilt"]["arm64-v8a"] = f"libmpv-android-{NEW_KEY_ANDROID}-arm64-v8a.tar.gz"
|
||
|
|
repo, commit = self._make_mpv_build(artifacts=artifacts)
|
||
|
|
before = self._snapshot()
|
||
|
|
|
||
|
|
result = self._run(commit, "--repo", str(repo))
|
||
|
|
|
||
|
|
self.assertEqual(result.returncode, 1)
|
||
|
|
self.assertIn("carries no checksum", result.stderr)
|
||
|
|
self.assertEqual(self._snapshot(), before)
|
||
|
|
self.assertFalse((self.root / "mpv-build.lock.json").exists())
|
||
|
|
|
||
|
|
def test_a_group_with_no_published_libraries_is_omitted_with_a_note(self) -> None:
|
||
|
|
artifacts = artifacts_fixture()
|
||
|
|
artifacts["platforms"]["linux"]["libraries"] = {}
|
||
|
|
repo, commit = self._make_mpv_build(artifacts=artifacts)
|
||
|
|
|
||
|
|
result = self._run(commit, "--repo", str(repo))
|
||
|
|
|
||
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
||
|
|
lock = json.loads((self.root / "mpv-build.lock.json").read_text(encoding="utf-8"))
|
||
|
|
self.assertEqual(sorted(lock["artifacts"]), ["android"])
|
||
|
|
self.assertIn("linux has published no libraries", result.stdout)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
unittest.main()
|