mirror of
https://github.com/Cuteminded/jellyframe.git
synced 2026-10-17 20:00:29 +02:00
113 lines
6.1 KiB
Python
113 lines
6.1 KiB
Python
import ctypes
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import platform
|
|
import subprocess
|
|
import struct
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
|
|
SCRIPT = Path(__file__).resolve().parents[1] / "tools" / "bundle-linux-libraries.py"
|
|
SPEC = importlib.util.spec_from_file_location("linux_bundle", SCRIPT)
|
|
linux_bundle = importlib.util.module_from_spec(SPEC)
|
|
SPEC.loader.exec_module(linux_bundle)
|
|
|
|
|
|
class DependencyChecks(unittest.TestCase):
|
|
def test_missing_transitive_dependency_stops_packaging(self):
|
|
with self.assertRaisesRegex(RuntimeError, "Unresolved"):
|
|
linux_bundle.parse_dependencies("libmpv.so.2 => /usr/lib/libmpv.so.2 (0x123)\nlibavcodec.so.60 => not found")
|
|
|
|
def test_graphics_drivers_and_glibc_stay_on_the_host(self):
|
|
host_libraries = (
|
|
"libc.so.6", "libm.so.6", "libpthread.so.0", "libstdc++.so.6", "libgcc_s.so.1",
|
|
"libGLX.so.0", "libvulkan.so.1", "libdrm_amdgpu.so.1",
|
|
"libxcb.so.1", "libxcb-dri3.so.0", "libxcb-present.so.0", "libX11.so.6",
|
|
"libX11-xcb.so.1", "libXrandr.so.2", "libwayland-client.so.0",
|
|
"libwayland-cursor.so.0", "libxkbcommon.so.0", "libdecor-0.so.0",
|
|
"libglib-2.0.so.0", "libgobject-2.0.so.0", "libgio-2.0.so.0",
|
|
"libgmodule-2.0.so.0", "libgthread-2.0.so.0", "libgdk_pixbuf-2.0.so.0",
|
|
"libgtk-3.so.0", "libgdk-3.so.0",
|
|
"libmount.so.1", "libblkid.so.1", "libuuid.so.1", "libsmartcols.so.1",
|
|
"libjpeg.so.8", "libtiff.so.6",
|
|
)
|
|
for name in host_libraries:
|
|
self.assertIsNotNone(linux_bundle.HOST_LIBRARIES.match(name), name)
|
|
for name in ("libmpv.so.2", "libavcodec.so.60", "libavutil.so.58", "libcairo.so.2", "libXpresent.so.1"):
|
|
self.assertIsNone(linux_bundle.HOST_LIBRARIES.match(name), name)
|
|
|
|
def test_system_library_resolution_is_not_a_successful_bundle(self):
|
|
with patch.object(linux_bundle, "dependencies", return_value={"libmpv.so.2": Path("/usr/lib/libmpv.so.2")}):
|
|
with self.assertRaisesRegex(RuntimeError, "bundled copy"):
|
|
linux_bundle.verify_bundle(Path("plugin.so"), Path("bundle/lib"), ["libmpv.so.2"])
|
|
|
|
|
|
@unittest.skipUnless(platform.system() == "Linux" and os.getenv("JELLYFRAME_BUNDLE_INTEGRATION") == "1", "Requires Linux build dependencies")
|
|
class BundleIntegration(unittest.TestCase):
|
|
def test_relocated_bundle_loads_decoder_and_preserves_system_package(self):
|
|
arch = "arm64" if platform.machine() in ("aarch64", "arm64") else "x86_64"
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
original = root / "system"
|
|
original.mkdir()
|
|
plugin = original / ("jellyframe_mpv.linux." + arch + ".so")
|
|
source = root / "probe.c"
|
|
source.write_text(
|
|
'#include <mpv/client.h>\n'
|
|
'int probe(void) { mpv_handle *h = mpv_create(); if (!h) return 1; mpv_destroy(h); return 0; }\n'
|
|
)
|
|
subprocess.run(["cc", "-shared", "-fPIC", str(source), "-lmpv", "-o", str(plugin)], check=True)
|
|
before = plugin.read_bytes()
|
|
linux_bundle.bundle(original, root / "bundled", arch)
|
|
relocated = root / "relocated"
|
|
(root / "bundled").rename(relocated)
|
|
linux_bundle.verify_bundle(relocated / plugin.name, relocated / "lib", ["libmpv.so.2"])
|
|
linux_bundle.verify_bundle(relocated / plugin.name, relocated / "lib", ["libXpresent.so.1"])
|
|
decoder = ctypes.CDLL(str(relocated / plugin.name))
|
|
self.assertEqual(decoder.probe(), 0)
|
|
check_host_tiff_loader(root)
|
|
self.assertEqual(plugin.read_bytes(), before)
|
|
self.assertFalse((original / "lib").exists())
|
|
self.assertFalse((relocated / "lib" / "libstdc++.so.6").exists())
|
|
self.assertFalse((relocated / "lib" / "libgcc_s.so.1").exists())
|
|
self.assertFalse((relocated / "lib" / "libjpeg.so.8").exists())
|
|
self.assertFalse((relocated / "lib" / "libtiff.so.6").exists())
|
|
for name in ("libglib-2.0.so.0", "libgobject-2.0.so.0", "libgio-2.0.so.0", "libmount.so.1", "libblkid.so.1", "libuuid.so.1"):
|
|
self.assertFalse((relocated / "lib" / name).exists())
|
|
for name in ("libxcb-dri3.so.0", "libX11.so.6", "libwayland-client.so.0", "libmount.so.1"):
|
|
self.assertFalse((relocated / "lib" / name).exists())
|
|
resolved = linux_bundle.dependencies(relocated / plugin.name)
|
|
self.assertIn(name, resolved)
|
|
self.assertNotEqual(resolved[name].resolve().parent, (relocated / "lib").resolve())
|
|
manifest = json.loads((relocated / "bundled-libraries.json").read_text())
|
|
self.assertTrue(any(item["library"] == "libmpv.so.2" for item in manifest))
|
|
self.assertTrue(all(item["source_version"] for item in manifest))
|
|
self.assertTrue(list((relocated / "licenses" / "linux-libraries").glob("*.txt")))
|
|
|
|
|
|
def check_host_tiff_loader(directory):
|
|
tags = ((256, 4, 1), (257, 4, 1), (258, 3, 8), (259, 3, 1), (262, 3, 1),
|
|
(273, 4, 122), (277, 3, 1), (278, 4, 1), (279, 4, 1))
|
|
image = directory / "probe.tiff"
|
|
data = b"II" + struct.pack("<HIH", 42, 8, len(tags))
|
|
data += b"".join(struct.pack("<HHII", tag, kind, 1, value) for tag, kind, value in tags)
|
|
image.write_bytes(data + struct.pack("<I", 0) + b"\x80")
|
|
pixbuf_library = ctypes.CDLL("libgdk_pixbuf-2.0.so.0")
|
|
pixbuf_library.gdk_pixbuf_new_from_file.argtypes = (ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p))
|
|
pixbuf_library.gdk_pixbuf_new_from_file.restype = ctypes.c_void_p
|
|
error = ctypes.c_void_p()
|
|
pixbuf = pixbuf_library.gdk_pixbuf_new_from_file(os.fsencode(image), ctypes.byref(error))
|
|
if not pixbuf:
|
|
raise AssertionError("The host TIFF loader failed after loading the bundled decoder.")
|
|
gobject = ctypes.CDLL("libgobject-2.0.so.0")
|
|
gobject.g_object_unref.argtypes = (ctypes.c_void_p,)
|
|
gobject.g_object_unref(pixbuf)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|