Files
DroidDeck/tools/tests/test_steam_exit_status.py

86 lines
4.0 KiB
Python

import os
from pathlib import Path
import re
import subprocess
import tempfile
import unittest
SESSION = Path(__file__).resolve().parents[1] / 'linuxfs/overlay/usr/local/bin/droiddeck-session'
class SteamExitStatusTest(unittest.TestCase):
def run_session(self, child_status, compositor_status=0, report=True, report_value=None,
stale_status=None, diagnostics=False, output=False):
source = SESSION.read_text()
function = re.search(r'^run_gamescope\(\) \(\n.*?^\)\n', source, re.M | re.S)
self.assertIsNotNone(function)
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
stale_file = root / 'droiddeck-steam-exit.stale'
if stale_status is not None:
stale_file.write_text(str(stale_status) + '\n')
compositor = root / 'gamescope'
compositor.write_text('#!/bin/bash\n"$@"\nexit ' + str(compositor_status) + '\n')
compositor.chmod(0o755)
child = root / 'steam'
report_arg = '"$1"' if report_value is None else '"$2"'
report_line = ('printf "%s\\n" ' + report_arg + ' > "$BL_STEAM_EXIT_FILE"\n'
if report else '')
child.write_text('#!/bin/bash\n' + report_line + 'exit "$1"\n')
child.chmod(0o755)
env = dict(os.environ, PATH=str(root) + os.pathsep + os.environ['PATH'],
XDG_RUNTIME_DIR=str(root))
env['DROIDDECK_ENHANCED_DIAGNOSTICS'] = '1' if diagnostics else '0'
result = subprocess.run(['bash', '-c', function.group() + '\nrun_gamescope "$@"',
'session', str(child), str(child_status),
str(report_value) if report_value is not None else ''], env=env,
capture_output=True, text=True, timeout=10)
self.assertEqual(list(root.glob('droiddeck-steam-exit.??????')), [])
if stale_status is not None:
self.assertEqual(stale_file.read_text(), str(stale_status) + '\n')
return result if output else result.returncode
def test_steam_crash_survives_successful_compositor_shutdown(self):
self.assertEqual(self.run_session(139), 139)
def test_clean_steam_shutdown_remains_success(self):
self.assertEqual(self.run_session(0), 0)
def test_compositor_failure_remains_visible(self):
self.assertEqual(self.run_session(0, compositor_status=1), 1)
def test_early_child_failure_preserves_compositor_result(self):
self.assertEqual(self.run_session(127, compositor_status=127, report=False), 127)
def test_no_report_does_not_invent_a_failure(self):
self.assertEqual(self.run_session(0, report=False), 0)
def test_invalid_report_cannot_become_a_shell_exit_status(self):
self.assertEqual(self.run_session(999), 0)
def test_malformed_report_is_ignored(self):
self.assertEqual(self.run_session(0, report_value='not-an-exit-code'), 0)
def test_stale_marker_does_not_affect_a_new_launch(self):
self.assertEqual(self.run_session(0, report=False, stale_status=139), 0)
def test_diagnostics_preserve_separate_steam_and_compositor_statuses(self):
result = self.run_session(139, diagnostics=True, output=True)
self.assertEqual(result.returncode, 139)
self.assertIn('event=session_exit compositor_status=0 steam_status=139', result.stdout)
def test_diagnostics_report_an_unavailable_child_status(self):
result = self.run_session(0, compositor_status=1, report=False, diagnostics=True, output=True)
self.assertEqual(result.returncode, 1)
self.assertIn('event=session_exit compositor_status=1 steam_status=unavailable', result.stdout)
def test_diagnostics_are_disabled_by_default(self):
result = self.run_session(0, output=True)
self.assertEqual(result.returncode, 0)
self.assertNotIn('DROIDDECK_DIAGNOSTICS', result.stdout)
if __name__ == '__main__':
unittest.main()