summaryrefslogtreecommitdiff
path: root/tests/helpers/run_cocoa_bundle.py
blob: 338f38eea35d598c7c72f1ed5dcc63b10d7aa07b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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()