104 lines
3.7 KiB
Python
104 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import io
|
|
import json
|
|
import tempfile
|
|
import unittest
|
|
from argparse import Namespace
|
|
from contextlib import redirect_stdout
|
|
from pathlib import Path
|
|
|
|
|
|
SCRIPT = Path(__file__).parents[1] / "android_release_artifacts.py"
|
|
SPEC = importlib.util.spec_from_file_location("android_release_artifacts", SCRIPT)
|
|
assert SPEC and SPEC.loader
|
|
MODULE = importlib.util.module_from_spec(SPEC)
|
|
SPEC.loader.exec_module(MODULE)
|
|
|
|
|
|
class AndroidReleaseArtifactsTest(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
self.root = Path(self.temp.name)
|
|
self.apk = self.root / "hermes-relay-1.15.1-sideload-release.apk"
|
|
self.aab = self.root / "hermes-relay-1.15.1-googlePlay-release.aab"
|
|
self.sideload_mapping = self.root / "sideload-mapping.txt"
|
|
self.play_mapping = self.root / "play-mapping.txt"
|
|
for path, payload in (
|
|
(self.apk, b"apk"),
|
|
(self.aab, b"aab"),
|
|
(self.sideload_mapping, b"sideload mapping"),
|
|
(self.play_mapping, b"play mapping"),
|
|
):
|
|
path.write_bytes(payload)
|
|
self.output = self.root / "output"
|
|
self.package_args = Namespace(
|
|
version="1.15.1",
|
|
version_code="54",
|
|
commit="a" * 40,
|
|
tree="b" * 40,
|
|
sideload_apk=self.apk,
|
|
google_play_aab=self.aab,
|
|
sideload_mapping=self.sideload_mapping,
|
|
google_play_mapping=self.play_mapping,
|
|
output=self.output,
|
|
)
|
|
self.verify_args = Namespace(
|
|
version="1.15.1",
|
|
version_code="54",
|
|
tree="b" * 40,
|
|
directory=self.output,
|
|
)
|
|
|
|
def tearDown(self) -> None:
|
|
self.temp.cleanup()
|
|
|
|
def package(self) -> None:
|
|
with redirect_stdout(io.StringIO()):
|
|
MODULE.package_artifacts(self.package_args)
|
|
|
|
def test_package_and_verify_round_trip(self) -> None:
|
|
self.package()
|
|
with redirect_stdout(io.StringIO()):
|
|
MODULE.verify_artifacts(self.verify_args)
|
|
manifest = json.loads(
|
|
(self.output / MODULE.MANIFEST_NAME).read_text(encoding="utf-8")
|
|
)
|
|
self.assertEqual(
|
|
set(MODULE.EXPECTED_ROLES),
|
|
{item["role"] for item in manifest["artifacts"]},
|
|
)
|
|
checksums = (self.output / MODULE.CHECKSUMS_NAME).read_text(encoding="utf-8")
|
|
self.assertIn(self.apk.name, checksums)
|
|
self.assertIn(self.aab.name, checksums)
|
|
self.assertNotIn("mapping", checksums)
|
|
|
|
def test_verify_rejects_tampered_binary(self) -> None:
|
|
self.package()
|
|
(self.output / self.apk.name).write_bytes(b"tampered")
|
|
with self.assertRaisesRegex(MODULE.ArtifactError, "SHA-256 mismatch"):
|
|
MODULE.verify_artifacts(self.verify_args)
|
|
|
|
def test_verify_rejects_wrong_tree(self) -> None:
|
|
self.package()
|
|
self.verify_args.tree = "c" * 40
|
|
with self.assertRaisesRegex(MODULE.ArtifactError, "tree mismatch"):
|
|
MODULE.verify_artifacts(self.verify_args)
|
|
|
|
def test_verify_rejects_extra_file(self) -> None:
|
|
self.package()
|
|
(self.output / "unexpected.bin").write_bytes(b"unexpected")
|
|
with self.assertRaisesRegex(MODULE.ArtifactError, "artifact set mismatch"):
|
|
MODULE.verify_artifacts(self.verify_args)
|
|
|
|
def test_package_requires_canonical_public_names(self) -> None:
|
|
self.package_args.sideload_apk = self.root / "wrong.apk"
|
|
self.package_args.sideload_apk.write_bytes(b"wrong")
|
|
with self.assertRaisesRegex(MODULE.ArtifactError, "must be named"):
|
|
MODULE.package_artifacts(self.package_args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|