Files
docview/tests/cmyk_lut/run_ubuntu_candidate.py
T
2026-09-21 13:41:40 +09:00

100 lines
5.4 KiB
Python

#!/usr/bin/env python3
"""Validate the fixed rendering-intent candidate in the dedicated Ubuntu guest."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import platform
import shutil
import subprocess
import sys
import time
ROOT = Path(__file__).resolve().parents[2]
EXPECTED_LIBRARY = '0c968f503ce549bad5301af2cc3fd0b83c37437315ac0e62dff439d6a1c6cc4d'
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--library', type=Path, required=True)
parser.add_argument('--output', type=Path, required=True)
parser.add_argument('--build-dir', type=Path, default=Path.home() / 'docview-build')
args = parser.parse_args()
os_release = platform.freedesktop_os_release()
assert os_release['ID'] == 'ubuntu' and os_release['VERSION_ID'] == '24.04'
library = args.library.resolve()
assert sha(library) == EXPECTED_LIBRARY
output = args.output.resolve()
build = args.build_dir.resolve()
inputs = [library, ROOT / '.deps/pdfium/lib/libpdfium.so']
inputs += [build / name for name in ('docview', 'docview-pdf-worker', 'docview-archive-worker', 'pdf-worker-evidence')]
identities = {str(path): sha(path) for path in inputs}
output.mkdir(parents=True, exist_ok=False)
env = os.environ.copy()
assert not env.get('LD_PRELOAD') and not env.get('LD_AUDIT')
assert not env.get('QTWEBENGINE_DISABLE_SANDBOX')
env.pop('WAYLAND_DISPLAY', None)
env.update(LD_LIBRARY_PATH=str(library.parent) + ':/opt/docview-deps/lib:/opt/docview-qt/6.11.2/gcc_64/lib',
QT_QPA_PLATFORM='xcb', QT_QUICK_BACKEND='software',
QTWEBENGINE_CHROMIUM_FLAGS='--disable-gpu', QT_SCALE_FACTOR='1',
QT_AUTO_SCREEN_SCALE_FACTOR='0', XDG_SESSION_TYPE='x11')
report = {'schemaVersion': 1, 'success': False, 'osRelease': os_release,
'platform': platform.platform(), 'pythonVersion': platform.python_version(),
'runnerSha256': sha(Path(__file__)), 'inputs': identities, 'commands': [],
'productionDependencyReplaced': False,
'scope': 'Same fixed Linux candidate library as Arch, used by the existing Ubuntu-built application and workers. Dedicated Ubuntu VM, Xvfb/software, ordinary worker sandbox. No package installation or physical-display acceptance.'}
def run(label, command):
start = time.monotonic()
log = output / (label + '.log')
with log.open('w') as stream:
result = subprocess.run(command, cwd=ROOT, env=env,
stdout=stream, stderr=subprocess.STDOUT, timeout=900)
report['commands'].append({'name': label, 'command': command, 'exitCode': result.returncode,
'seconds': time.monotonic() - start, 'logSha256': sha(log)})
print(label, result.returncode, flush=True)
return result.returncode
try:
linked = subprocess.run(['ldd', str(build / 'docview-pdf-worker')], env=env,
capture_output=True, text=True, check=True).stdout
assert str(library) in linked and 'not found' not in linked
(output / 'worker-ldd.txt').write_text(linked)
run('cmyk', [sys.executable, str(ROOT / 'tests/cmyk_lut/run.py'),
'--build-dir', str(build), '--output', str(output / 'cmyk')])
run('intents', [sys.executable, str(ROOT / 'tests/cmyk_lut/intents.py'),
'--build-dir', str(build), '--pdfium-library', str(library),
'--scales', '.5', '1', '4', '--output', str(output / 'intents')])
ctest_status = run('ctest', ['xvfb-run', '-a', '-s', '-screen 0 1100x760x24',
'dbus-run-session', '--', 'ctest', '--test-dir', str(build),
'--output-on-failure', '--output-junit', str(output / 'tests.xml')])
shutil.copyfile(build / 'Testing/Temporary/LastTest.log', output / 'LastTest.log')
cmyk = json.loads((output / 'cmyk/report.json').read_text())
intents = json.loads((output / 'intents/report.json').read_text())
report['cmykSuccess'] = cmyk['success']
report['intentPdfiumSuccess'] = intents.get('pdfiumSuccess', False)
report['intentPopplerSuccess'] = intents.get('popplerSuccess', False)
report['ctestSuccess'] = ctest_status == 0
report['allBinaryInputsUnchanged'] = all(sha(Path(path)) == digest for path, digest in identities.items())
report['candidateTestsPass'] = (report['cmykSuccess'] and report['intentPdfiumSuccess']
and report['ctestSuccess'] and report['allBinaryInputsUnchanged'])
report['success'] = report['candidateTestsPass'] and intents['renderingAcceptance']
finally:
report['evidenceSha256'] = {str(path.relative_to(output)): sha(path)
for path in sorted(output.rglob('*')) if path.is_file()}
(output / 'report.json').write_text(json.dumps(report, indent=2) + '\n')
print(json.dumps({key: report.get(key) for key in ('success', 'candidateTestsPass',
'intentPopplerSuccess', 'allBinaryInputsUnchanged')}))
if not report['success']:
raise SystemExit('Comparison not fully accepted; candidate and comparator verdicts are retained separately.')
if __name__ == '__main__':
main()