summaryrefslogtreecommitdiff
path: root/tests/helpers
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-12 22:12:02 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-12 22:12:02 +0300
commit94c5e361fbb229b0cd6853e2102a7d95f1f358b3 (patch)
tree3c0f11af9dcb84dac8067a31a6979acd6dd56fbf /tests/helpers
parent08dc9047098d6d96791024ac7d8f5837633673ef (diff)
better public interface and doxygen
Diffstat (limited to 'tests/helpers')
-rw-r--r--tests/helpers/check_exports.py11
-rw-r--r--tests/helpers/package_cocoa_tests.py70
-rw-r--r--tests/helpers/run_backends.py8
-rw-r--r--tests/helpers/run_cocoa_bundle.py68
4 files changed, 153 insertions, 4 deletions
diff --git a/tests/helpers/check_exports.py b/tests/helpers/check_exports.py
index 0d10014..acb727d 100644
--- a/tests/helpers/check_exports.py
+++ b/tests/helpers/check_exports.py
@@ -9,6 +9,7 @@ import subprocess
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--allow-export', action='append', default=[])
+ parser.add_argument('--platform', choices=['linux', 'darwin'], default='linux')
parser.add_argument('nm')
parser.add_argument('library')
parser.add_argument('headers', nargs='+')
@@ -23,10 +24,14 @@ def main():
if not expected:
raise SystemExit("No public function declarations found")
- output = subprocess.check_output(
- [args.nm, "-D", "--defined-only", "--format=posix", args.library], text=True
- )
+ nm_options = ["-g", "-U", "-P"] if args.platform == 'darwin' else [
+ "-D", "--defined-only", "--format=posix"
+ ]
+ output = subprocess.check_output([args.nm, *nm_options, args.library], text=True)
exported = {line.split()[0] for line in output.splitlines() if line.strip()}
+ if args.platform == 'darwin':
+ # Mach-O decorates C symbols with one leading underscore.
+ exported = {name[1:] if name.startswith('_') else name for name in exported}
missing = expected - exported
leaked = exported - expected - set(args.allow_export)
if missing or leaked:
diff --git a/tests/helpers/package_cocoa_tests.py b/tests/helpers/package_cocoa_tests.py
new file mode 100644
index 0000000..8f801f8
--- /dev/null
+++ b/tests/helpers/package_cocoa_tests.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+"""Package compiled Cocoa tests for execution on a matching macOS desktop."""
+
+import argparse
+import hashlib
+import json
+from pathlib import Path
+import shutil
+import tarfile
+import tempfile
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('build', type=Path)
+ parser.add_argument('output', type=Path)
+ args = parser.parse_args()
+ build = args.build.resolve()
+ info = build / 'meson-info'
+ host = json.loads((info / 'intro-machines.json').read_text())['host']
+ if host['system'] != 'darwin':
+ parser.error('Use a configured and compiled Cocoa build.')
+ tests = json.loads((info / 'intro-tests.json').read_text())
+ required = {'native', 'native-ownership', 'keyboard-cocoa', 'tablet-cocoa',
+ 'cursor-cocoa', 'hello-cocoa-window', 'tablet-cocoa-window'}
+ missing = required - {test['name'] for test in tests}
+ if missing:
+ parser.error('Enable integration_tests; missing: ' + ', '.join(sorted(missing)))
+ options = json.loads((info / 'intro-buildoptions.json').read_text())
+ sanitizer = next(option['value'] for option in options if option['name'] == 'b_sanitize')
+ if sanitizer not in ('none', [], ['none']):
+ parser.error('Use an unsanitized build; sanitizer runtimes are not bundled.')
+
+ with tempfile.TemporaryDirectory(prefix='yasl-cocoa-bundle-') as directory:
+ root = Path(directory) / 'yasl-cocoa-tests'
+ root.mkdir()
+ files = {}
+
+ def copy(source, relative):
+ destination = root / relative
+ destination.parent.mkdir(parents=True, exist_ok=True)
+ shutil.copy2(source, destination)
+ files[str(relative)] = hashlib.sha256(destination.read_bytes()).hexdigest()
+
+ copy(build / 'libyasl.dylib', Path('libyasl.dylib'))
+ source_root = Path(__file__).resolve().parents[2]
+ copy(source_root / 'LICENSE', Path('LICENSE'))
+ copy(Path(__file__).with_name('run_cocoa_bundle.py'), Path('run.py'))
+ commands = []
+ for test in tests:
+ if test['name'] == 'public-exports':
+ continue # Check the dylib with nm on the build machine first.
+ executable = Path(test['cmd'][0]).resolve()
+ try:
+ relative = executable.relative_to(build)
+ except ValueError:
+ parser.error('Unexpected external test command: ' + str(executable))
+ copy(executable, relative)
+ commands.append({'name': test['name'], 'cmd': [str(relative), *test['cmd'][1:]],
+ 'timeout': test['timeout']})
+ manifest = {'architecture': host['cpu_family'], 'minimum_macos': 14,
+ 'tests': commands, 'sha256': files}
+ (root / 'manifest.json').write_text(json.dumps(manifest, indent=2) + '\n')
+ with tarfile.open(args.output, 'w:gz') as archive:
+ archive.add(root, arcname=root.name)
+ print(f'Packaged {len(commands)} runtime tests for {host["cpu_family"]}: {args.output}')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/helpers/run_backends.py b/tests/helpers/run_backends.py
index 57cc9ab..70c82fb 100644
--- a/tests/helpers/run_backends.py
+++ b/tests/helpers/run_backends.py
@@ -21,7 +21,7 @@ def wait_for(process, ready):
def main():
- weston, xwayland, test, timers, tablet = sys.argv[1:]
+ weston, xwayland, test, timers, tablet, hello, input_x11 = sys.argv[1:]
servers = []
with tempfile.TemporaryDirectory(prefix="yasl-backends-") as directory:
root = Path(directory)
@@ -80,10 +80,16 @@ def main():
x_env["XDG_SESSION_TYPE"] = "x11"
print("Test: X11 surface timers", flush=True)
subprocess.run([timers], env=x_env, check=True, timeout=8)
+ print("Test: X11 UTF-8 keyboard fallback and clipboard", flush=True)
+ subprocess.run([input_x11], env=x_env, check=True, timeout=8)
for backend in ("wayland", "x11"):
print("Test: tablet example on", backend, flush=True)
subprocess.run([tablet, "--backend", backend, "--smoke-test"],
env=env, check=True, timeout=8)
+ print("Test: hello example on", backend, flush=True)
+ subprocess.run([hello, "--smoke-test"],
+ env=env if backend == "wayland" else x_env,
+ check=True, timeout=8)
except BaseException:
for log in (wl_log, x_log):
log.flush()
diff --git a/tests/helpers/run_cocoa_bundle.py b/tests/helpers/run_cocoa_bundle.py
new file mode 100644
index 0000000..338f38e
--- /dev/null
+++ b/tests/helpers/run_cocoa_bundle.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+"""Run a packaged Cocoa suite, preserving output and a machine-readable report."""
+
+import argparse
+import hashlib
+import json
+import os
+from pathlib import Path
+import platform
+import re
+import subprocess
+import sys
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--list', action='store_true', help='List tests without executing them.')
+ args = parser.parse_args()
+ root = Path(__file__).resolve().parent
+ manifest = json.loads((root / 'manifest.json').read_text())
+ if args.list:
+ print('Architecture:', manifest['architecture'])
+ for test in manifest['tests']:
+ print(test['name'])
+ return
+ if sys.platform != 'darwin':
+ parser.error('Run this suite in a macOS desktop session.')
+ if int(platform.mac_ver()[0].split('.')[0]) < manifest['minimum_macos']:
+ parser.error('This suite requires macOS 14 or newer.')
+ architecture = {'arm64': 'aarch64'}.get(platform.machine(), platform.machine())
+ if architecture != manifest['architecture']:
+ parser.error('Use the bundle matching this Python process architecture.')
+ for relative, expected in manifest['sha256'].items():
+ if hashlib.sha256((root / relative).read_bytes()).hexdigest() != expected:
+ parser.error('Bundle file changed: ' + relative)
+
+ logs = root / 'logs'
+ logs.mkdir(exist_ok=True)
+ env = os.environ.copy()
+ env['DYLD_LIBRARY_PATH'] = str(root)
+ results = []
+ for test in manifest['tests']:
+ name = test['name']
+ log = logs / (re.sub(r'[^A-Za-z0-9_-]', '_', name) + '.log')
+ command = [str(root / test['cmd'][0]), *test['cmd'][1:]]
+ with log.open('w') as output:
+ try:
+ process = subprocess.run(command, cwd=root, env=env, stdout=output,
+ stderr=subprocess.STDOUT, timeout=test['timeout'] or None)
+ result = {'name': name, 'returncode': process.returncode,
+ 'status': 'pass' if process.returncode == 0 else 'fail'}
+ except subprocess.TimeoutExpired:
+ output.write('\nTest timed out.\n')
+ result = {'name': name, 'status': 'timeout'}
+ except OSError as error:
+ output.write(str(error) + '\n')
+ result = {'name': name, 'status': 'error'}
+ results.append(result)
+ print(f'{name}: {result["status"]}', flush=True)
+ report = root / 'results.json'
+ report.write_text(json.dumps(results, indent=2) + '\n')
+ failed = sum(result['status'] != 'pass' for result in results)
+ print(f'{len(results) - failed} passed, {failed} failed. Logs: {logs}')
+ sys.exit(1 if failed else 0)
+
+
+if __name__ == '__main__':
+ main()