#!/usr/bin/env python3 """Exercise the installed compositor in an isolated three-output session.""" import argparse import os from pathlib import Path import shlex import shutil import subprocess import tempfile import time parser = argparse.ArgumentParser() parser.add_argument('--source', type=Path, required=True, help='Prepared Wayfire source with the personal patches applied') parser.add_argument('--wayfire', default='wayfire', help='Compositor binary to test') parser.add_argument('--plugin-dir', type=Path, help='Directory of candidate plugins') parser.add_argument('--baseline', action='store_true', help='Report failures without asserting') args = parser.parse_args() if os.geteuid() == 0: parser.error('Run as a regular desktop user, not root') tests = Path(__file__).resolve().parent runtime = Path(tempfile.mkdtemp(prefix='wayfire-render-test-')) flags = shlex.split(subprocess.check_output( ['pkg-config', '--cflags', '--libs', 'wayfire', 'wf-utils'], text=True)) subprocess.run(['c++', '-std=c++17', '-shared', '-fPIC', '-DWAYFIRE_PLUGIN', '-DWLR_USE_UNSTABLE', '-I' + str(args.source / 'plugins/cube'), str(tests / 'render-test.cpp'), '-o', str(runtime / 'librender-test.so'), *flags], check=True) shutil.copyfile(tests / 'render-test.xml', runtime / 'render-test.xml') env = os.environ.copy() for key in ['WAYLAND_DISPLAY', 'WAYFIRE_SOCKET', '_WAYFIRE_SOCKET', 'DISPLAY', 'WAYFIRE_DEFAULT_CONFIG_BACKEND', 'WAYFIRE_CONFIG_FILE', 'LD_LIBRARY_PATH']: env.pop(key, None) env.update(XDG_RUNTIME_DIR=str(runtime), WLR_BACKENDS='headless', WLR_HEADLESS_OUTPUTS='3', WLR_RENDERER='gles2', WLR_RENDER_DRM_DEVICE=env.get('WLR_RENDER_DRM_DEVICE', '/dev/dri/renderD128'), WAYFIRE_PLUGIN_PATH=str(runtime) + (':' + str(args.plugin_dir) if args.plugin_dir else ''), WAYFIRE_PLUGIN_XML_PATH=str(runtime)) config = runtime / 'wayfire.ini' config.write_text('[core]\nplugins = ipc ipc-rules cube digitalrain-sampler render-test\n' 'xwayland = false\nvwidth = 3\nvheight = 1\n') with (runtime / 'wayfire.log').open('w') as log: process = subprocess.Popen([args.wayfire, '-c', str(config)], env=env, stdout=log, stderr=log) try: for _ in range(100): if process.poll() is not None: raise RuntimeError(f'Wayfire exited; see {runtime}/wayfire.log') sockets = list(runtime.glob('wayfire-*.socket')) displays = [p for p in runtime.glob('wayland-*') if not p.name.endswith('.lock')] if sockets and displays: env['WAYFIRE_SOCKET'] = str(sockets[0]) env['WAYLAND_DISPLAY'] = displays[0].name break time.sleep(.1) else: raise RuntimeError('Timed out waiting for compositor') time.sleep(.3) subprocess.run(['python3', str(tests / 'dynamic-check.py')], env=env, check=True) subprocess.run(['python3', str(tests / 'layer-check.py'), *(['--baseline'] if args.baseline else [])], env=env, check=True) subprocess.run(['python3', str(tests / 'redraw-check.py'), *(['--baseline'] if args.baseline else [])], env=env, check=True) finally: process.terminate() try: process.wait(timeout=10) except subprocess.TimeoutExpired: process.kill() process.wait() print(f'Test artifacts: {runtime}')