clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

feat/atlas-next-horizon
atlas / tests / release / test_packaging.py
test_packaging.py 6.6K
1"""Static acceptance for the native packages produced by release.yml."""23from pathlib import Path4import plistlib5import subprocess6import tempfile7import unittest8import xml.etree.ElementTree as ET91011ROOT = Path(__file__).resolve().parents[2]12MIME = "application/vnd.felinestatemachine.atlas"13EXTENSION = "atlas"141516class NativePackagingTest(unittest.TestCase):17 def test_macos_exports_atlas_document_type(self) -> None:18 with (ROOT / "packaging/macos/Info.plist").open("rb") as source:19 info = plistlib.load(source)2021 self.assertEqual(info["CFBundleIdentifier"], "dev.felinestatemachine.atlas")22 self.assertTrue(info["LSSupportsOpeningDocumentsInPlace"])23 declaration = info["UTExportedTypeDeclarations"][0]24 self.assertIn(EXTENSION, declaration["UTTypeTagSpecification"]["public.filename-extension"])25 self.assertEqual(declaration["UTTypeTagSpecification"]["public.mime-type"], MIME)26 self.assertIn(declaration["UTTypeIdentifier"], info["CFBundleDocumentTypes"][0]["LSItemContentTypes"])2728 def test_linux_package_registers_atlas_mime(self) -> None:29 desktop = (ROOT / "packaging/linux/dev.felinestatemachine.Atlas.desktop").read_text()30 self.assertIn(f"MimeType={MIME};", desktop)31 self.assertIn("Exec=Atlas %f", desktop)32 self.assertIn("Terminal=false", desktop)3334 mime = ET.parse(ROOT / "packaging/linux/dev.felinestatemachine.atlas.xml").getroot()35 namespace = {"mime": "http://www.freedesktop.org/standards/shared-mime-info"}36 mime_type = mime.find("mime:mime-type", namespace)37 self.assertIsNotNone(mime_type)38 self.assertEqual(mime_type.attrib["type"], MIME)39 glob = mime_type.find("mime:glob", namespace)40 self.assertEqual(glob.attrib["pattern"], f"*.{EXTENSION}")41 subclass = mime_type.find("mime:sub-class-of", namespace)42 self.assertEqual(subclass.attrib["type"], "application/zip")43 magic = mime_type.find("mime:magic", namespace)44 self.assertEqual(magic.attrib["priority"], "90")45 signature = magic.find("mime:match", namespace)46 self.assertEqual(signature.attrib["offset"], "0")47 self.assertEqual(signature.attrib["value"], "PK")48 bootstrap = signature.find("mime:match", namespace)49 self.assertEqual(bootstrap.attrib["offset"], "30")50 self.assertEqual(bootstrap.attrib["value"], "atlas.json")5152 control = (ROOT / "packaging/linux/control").read_text()53 self.assertIn("Package: atlas-desktop", control)54 self.assertIn("Depends: libgtk-3-0, libwebkit2gtk-4.1-0", control)55 self.assertIn("desktop-file-utils", control)56 self.assertIn("shared-mime-info", control)5758 def test_windows_installer_registers_atlas_file(self) -> None:59 installer = (ROOT / "packaging/windows/Atlas.iss").read_text()60 self.assertIn("ChangesAssociations=yes", installer)61 self.assertIn("PrivilegesRequired=lowest", installer)62 self.assertIn("Software\\Classes\\.atlas", installer)63 self.assertIn('ValueData: "Atlas.Volume"', installer)64 self.assertIn(f'ValueData: "{MIME}"', installer)65 self.assertIn('ValueData: """{app}\\Atlas.exe"" ""%1"""', installer)66 self.assertIn("SHChangeNotify", installer)6768 def test_release_is_draft_first_and_verifies_content(self) -> None:69 workflow = (ROOT / ".github/workflows/release.yml").read_text()70 self.assertIn("scripts/release/verify-assets.sh", workflow)71 self.assertIn("--draft", workflow)72 self.assertIn("--draft=false", workflow)73 self.assertIn(".digest", workflow)74 self.assertIn("package-desktop.sh", workflow)75 self.assertIn("sign-windows.ps1", workflow)76 self.assertIn("package-smoke.sh", workflow)77 self.assertIn("smoke-macos.sh", workflow)78 self.assertIn("smoke-linux.sh", workflow)79 self.assertIn("smoke-windows.ps1", workflow)80 self.assertNotIn("& $installer /VERYSILENT", workflow)81 mac_package = (ROOT / "scripts/release/package-desktop.sh").read_text()82 self.assertIn("sign-macos.sh", mac_package)83 self.assertIn("stage=$(mktemp -d)", mac_package)84 self.assertNotIn('app="$root/Atlas.app"', mac_package)8586 mac_signer = (ROOT / "scripts/release/sign-macos.sh").read_text()87 self.assertIn('--keychain "$keychain"', mac_signer)88 self.assertNotIn("security list-keychains", mac_signer)89 self.assertIn('xcrun stapler validate "$app"', mac_signer)90 self.assertIn('codesign --force --deep --sign - "$app"', mac_signer)9192 windows_smoke = (ROOT / "scripts/release/smoke-windows.ps1").read_text()93 self.assertIn("Start-Process -FilePath $Installer", windows_smoke)94 self.assertIn("$install.ExitCode", windows_smoke)95 self.assertIn("application/vnd.felinestatemachine.atlas", windows_smoke)96 linux_smoke = (ROOT / "scripts/release/smoke-linux.sh").read_text()97 self.assertIn("dpkg-query", linux_smoke)98 self.assertIn("gio info --attributes=standard::content-type", linux_smoke)99 self.assertIn("ATLAS_BUNDLES_DIR", linux_smoke)100 mac_smoke = (ROOT / "scripts/release/smoke-macos.sh").read_text()101 self.assertIn("codesign --verify --deep --strict", mac_smoke)102 self.assertIn("dev.felinestatemachine.atlas.volume", mac_smoke)103 self.assertIn("ATLAS_BUNDLES_DIR", mac_smoke)104105 def test_release_asset_allowlist_is_exact(self) -> None:106 version = "v1.2.3"107 assets = [108 f"Atlas-{version}-darwin-arm64.zip",109 f"Atlas-{version}-linux-amd64.deb",110 f"Atlas-{version}-windows-amd64-setup.exe",111 f"atlas-cli-{version}-darwin-amd64.tar.gz",112 f"atlas-cli-{version}-darwin-arm64.tar.gz",113 f"atlas-cli-{version}-linux-amd64.tar.gz",114 f"atlas-cli-{version}-linux-arm64.tar.gz",115 f"atlas-cli-{version}-windows-amd64.zip",116 ]117 with tempfile.TemporaryDirectory() as directory:118 dist = Path(directory)119 for asset in assets:120 (dist / asset).touch()121122 verifier = ROOT / "scripts/release/verify-assets.sh"123 subprocess.run([verifier, dist, version], check=True, capture_output=True)124 self.assertEqual(len((dist / "SHA256SUMS").read_text().splitlines()), len(assets))125126 (dist / "unexpected.zip").touch()127 rejected = subprocess.run([verifier, dist, version], capture_output=True)128 self.assertNotEqual(rejected.returncode, 0)129 self.assertIn(b"unexpected.zip", rejected.stdout + rejected.stderr)130131132if __name__ == "__main__":133 unittest.main()