Files
DroidDeck/tools/tests/test_fex_install.py
The412Banner 292295ba30 feat: x86-64 client follows DroidDeck-Components' fex.json; fuller x86-64 session logs (#1053)
FEX builds can reach devices without an app release: while the x86-64
client is selected the app reads DroidDeck-Components' fex.json (directly,
on its own thread, not in Offline Mode) and a session passes that build to
droiddeck-steam-x64, which installs it only once its sha256 matches and its
programs load, keeping the replaced files aside until the installed copy
loads too; it keeps a working installed build otherwise and falls back to
the build it pins. FEX is built and published by Components' build-fex.yml
on its single fex release; this repository's build-fex.yml only checks the
build. x86-64 session logs name the FEX build and its source, the rootfs and
the client's builds, report the rootfs download in tenths, and say in one
line when the web helper's GPU process keeps exiting, with x86 glxinfo and
vulkaninfo through FEX. Settings shows the FEX build installed.
2026-10-18 03:40:17 -04:00

92 lines
4.2 KiB
Python

import hashlib
import os
from pathlib import Path
import subprocess
import tempfile
import textwrap
import unittest
SCRIPT = Path(__file__).resolve().parents[1] / "linuxfs/overlay/usr/local/bin/droiddeck-steam-x64"
SOURCE = SCRIPT.read_text()
START = SOURCE.index("fex_install() {")
END = SOURCE.index("\n}\n", START) + 3
FEX_INSTALL = SOURCE[START:END]
HARNESS = textwrap.dedent("""\
set -eu
# The archive comes from PKG; the installed tree loads when INSTALLED_LOADS=1, any staged tree always does.
curl() { local out=; while [ $# -gt 0 ]; do [ "$1" = -o ] && out=$2; shift; done; cp "$PKG" "$out"; }
fex_loads() { case $1 in "$ROOT"/) [ "${INSTALLED_LOADS:-1}" = 1 ] ;; *) true ;; esac; }
""")
class FexInstallTest(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
base = Path(self.temp.name)
self.root = base / "root"
old = self.root / "usr/local"
(old / "bin").mkdir(parents=True)
(old / "bin/FEX").write_text("old FEX\n")
(old / "share/fex-emu").mkdir(parents=True)
(old / "share/fex-emu/.droiddeck-tag").write_text("fex-2610-r5\n")
new = base / "pkg/usr/local"
(new / "bin").mkdir(parents=True)
(new / "bin/FEX").write_text("new FEX\n")
(new / "lib").mkdir()
(new / "lib/libxxhash.so.0").write_text("lib\n")
self.pkg = base / "fex.tzst"
subprocess.run(f"tar -C '{base / 'pkg'}' -c . | zstd -q -o '{self.pkg}'", shell=True, check=True)
self.sha = hashlib.sha256(self.pkg.read_bytes()).hexdigest()
def install(self, installed_loads, sha=None):
body = FEX_INSTALL.replace("local tag=$1 sha=$2 urls=$3 dst=/ ", "local tag=$1 sha=$2 urls=$3 dst=$ROOT/ ")
self.assertNotEqual(body, FEX_INSTALL)
script = HARNESS + body + 'if fex_install fex-2610-r6 "$SHA" u; then echo OK; else echo FAILED; fi\n'
env = dict(os.environ, ROOT=str(self.root), PKG=str(self.pkg), SHA=sha or self.sha,
INSTALLED_LOADS="1" if installed_loads else "0")
out = subprocess.run(["bash", "-c", script], env=env, text=True, capture_output=True, timeout=30)
self.assertEqual(0, out.returncode, out.stderr)
return out.stdout.strip().splitlines()[-1], out.stderr
def tag(self):
return (self.root / "usr/local/share/fex-emu/.droiddeck-tag").read_text().strip()
def test_a_build_that_loads_once_installed_replaces_the_old_one(self):
result, _ = self.install(installed_loads=True)
self.assertEqual("OK", result)
self.assertEqual("new FEX\n", (self.root / "usr/local/bin/FEX").read_text())
self.assertTrue((self.root / "usr/local/lib/libxxhash.so.0").is_file())
self.assertEqual("fex-2610-r6", self.tag())
def test_an_installed_copy_that_does_not_load_puts_the_old_files_back(self):
result, err = self.install(installed_loads=False)
self.assertEqual("FAILED", result)
self.assertIn("putting the previous files back", err)
self.assertEqual("old FEX\n", (self.root / "usr/local/bin/FEX").read_text())
self.assertFalse((self.root / "usr/local/lib/libxxhash.so.0").exists())
self.assertEqual("fex-2610-r5", self.tag())
def test_a_tag_that_cannot_be_written_puts_the_old_files_back(self):
share = self.root / "usr/local/share/fex-emu"
(share / ".droiddeck-tag").unlink()
share.rmdir()
share.write_text("not a folder\n")
result, err = self.install(installed_loads=True)
self.assertEqual("FAILED", result)
self.assertIn("putting the previous files back", err)
self.assertEqual("old FEX\n", (self.root / "usr/local/bin/FEX").read_text())
self.assertFalse((self.root / "usr/local/lib/libxxhash.so.0").exists())
def test_an_archive_that_does_not_match_installs_nothing(self):
result, err = self.install(installed_loads=True, sha="0" * 64)
self.assertEqual("FAILED", result)
self.assertIn("no source gave the archive", err)
self.assertEqual("old FEX\n", (self.root / "usr/local/bin/FEX").read_text())
self.assertEqual("fex-2610-r5", self.tag())
if __name__ == "__main__":
unittest.main()