Files
ProjectQuint/Tools/tests/test_release_mac_alpha.py
T

155 lines
6.2 KiB
Python

import importlib.util
import json
import tempfile
import unittest
import sys
from pathlib import Path
from unittest import mock
MODULE_PATH = Path(__file__).resolve().parents[1] / "release_mac_alpha.py"
sys.path.insert(0, str(MODULE_PATH.parent))
SPEC = importlib.util.spec_from_file_location("release_mac_alpha", MODULE_PATH)
release = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(release)
class ReleaseHarnessTests(unittest.TestCase):
def test_parse_revision_accepts_current_alpha_contract(self):
self.assertEqual(release.parse_revision_from_version("0.1.0-alpha.12"), 12)
with self.assertRaises(release.ReleaseError):
release.parse_revision_from_version("0.1.0")
def test_replace_exact_regex_fails_closed_on_ambiguous_input(self):
self.assertEqual(
release.replace_exact_regex(
"revision=2",
r"revision=\d+",
"revision=3",
label="revision",
),
"revision=3",
)
with self.assertRaises(release.ReleaseError):
release.replace_exact_regex(
"revision=2\nrevision=2",
r"revision=\d+",
"revision=3",
label="revision",
)
def test_set_release_version_updates_source_and_independent_contract_test(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
source = root / "ProjectQuintReleaseVersion.cs"
tests = root / "ProjectQuintReleaseVersionTests.cs"
source.write_text(
"public const int ChannelRevision = 2;\n"
"public const int BuildNumber = 2;\n",
encoding="utf-8",
)
tests.write_text(
"Assert.That(ProjectQuintReleaseVersion.ChannelRevision, Is.EqualTo(2));\n"
"Assert.That(ProjectQuintReleaseVersion.BuildNumber, Is.EqualTo(2));\n"
'Assert.That(ProjectQuintReleaseVersion.DisplayVersion, Is.EqualTo("0.1.0-alpha.2"));\n'
'Is.EqualTo("0.1.0-alpha.2 (build 2)"));\n',
encoding="utf-8",
)
with mock.patch.object(release, "VERSION_SOURCE", source), mock.patch.object(
release, "VERSION_TEST", tests
):
release.set_release_version(3)
self.assertIn("ChannelRevision = 3", source.read_text())
self.assertIn("BuildNumber = 3", source.read_text())
self.assertIn("0.1.0-alpha.3", tests.read_text())
self.assertIn("build 3", tests.read_text())
def test_update_site_writes_download_manifest_and_one_changelog_entry(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
download = root / "download.astro"
changelog = root / "changelog.astro"
manifest = root / "latest.json"
appcast = root / "appcast.xml"
readme = root / "README.md"
download.write_text(
'href="/downloads/0.1.0-alpha.2/ProjectQuint-0.1.0-alpha.2-macOS-universal.dmg"\n'
"<strong>0.1.0 ALPHA 2</strong>\n"
"<div><dt>VERSION</dt><dd>0.1.0-alpha.2</dd></div>\n"
"<div><dt>SIZE</dt><dd>77.7 MB</dd></div>\n"
f"<code>{'a' * 64}</code>\n",
encoding="utf-8",
)
changelog.write_text(' <section class="timeline">\n', encoding="utf-8")
manifest.write_text("{}\n", encoding="utf-8")
appcast.write_text("", encoding="utf-8")
readme.write_text("# Site\n", encoding="utf-8")
digest = "b" * 64
signature = "A" * 86 + "=="
patches = [
mock.patch.object(release, "DOWNLOAD_PAGE", download),
mock.patch.object(release, "CHANGELOG_PAGE", changelog),
mock.patch.object(release, "UPDATE_MANIFEST", manifest),
mock.patch.object(release, "SPARKLE_APPCAST", appcast),
mock.patch.object(release, "SITE_README", readme),
]
for patch in patches:
patch.start()
try:
release.update_site(
3,
digest,
80_000_000,
signature,
"Release title",
["Change one", "Change two"],
"2026-07-29T18:00:00+09:00",
)
release.update_site(
3,
digest,
80_000_000,
signature,
"Release title",
["Change one", "Change two"],
"2026-07-29T18:00:00+09:00",
)
finally:
for patch in reversed(patches):
patch.stop()
self.assertIn("0.1.0-alpha.3", download.read_text())
self.assertIn("80.0 MB", download.read_text())
self.assertEqual(changelog.read_text().count("0.1.0-ALPHA.3"), 1)
self.assertEqual(readme.read_text().count("## 0.1.0-alpha.3 release"), 1)
data = json.loads(manifest.read_text())
self.assertEqual(data["buildNumber"], 3)
self.assertEqual(data["sha256"], digest)
self.assertEqual(data["sizeBytes"], 80_000_000)
appcast_text = appcast.read_text()
self.assertIn("0.1.0-alpha.3", appcast_text)
self.assertIn('sparkle:version>3</sparkle:version', appcast_text)
self.assertIn('length="80000000"', appcast_text)
self.assertIn(signature, appcast_text)
def test_build_stable_does_not_block_for_a_running_existing_app(self):
with mock.patch.object(release, "run") as run:
app = release.build_stable()
self.assertEqual(app, release.STABLE_APP)
run.assert_called_once()
self.assertEqual(
run.call_args.args[0],
[release.ROOT / "Tools/build-mac-stable-release.sh"],
)
self.assertEqual(
run.call_args.kwargs["env"]["PROJECTQUINT_BUILD_PATH"],
str(release.STABLE_APP),
)
if __name__ == "__main__":
unittest.main()