1################################################################ 2# 3# Licensed to the Apache Software Foundation (ASF) under one 4# or more contributor license agreements. See the NOTICE file 5# distributed with this work for additional information 6# regarding copyright ownership. The ASF licenses this file 7# to you under the Apache License, Version 2.0 (the 8# "License"); you may not use this file except in compliance 9# with the License. You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, 14# software distributed under the License is distributed on an 15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16# KIND, either express or implied. See the License for the 17# specific language governing permissions and limitations 18# under the License. 19 20"""Runs the large fixture scripts in aootest_worker/fixtures (copied from the ODF harness's fixtures/*/make.py) inside the worker. 21They were written as stand-alone programs: `make.py <port> <outdir>`. Here they run under runpy with that argv, an `unohelp` that 22hands them the worker's connection, and two guards for what cannot work from inside the worker: os._exit (the draw script ends 23with it) and subprocess.run (the draw script's watchdog restarts the office with a script of the old harness). They do not run as 24`__main__`: pyuno keeps its runtime in that module's dict, and runpy swaps it out while a script runs.""" 25import contextlib 26import os 27import runpy 28import subprocess 29import sys 30import traceback 31from pathlib import Path 32 33from aootest_worker import unohelp 34 35FIXTURES = Path(__file__).resolve().parent.parent / "fixtures" 36 37 38def _no_exit(code=0): 39 raise SystemExit(code) 40 41 42def _no_run(*args, **kwargs): 43 raise RuntimeError("a fixture script cannot restart the office from inside the worker") 44 45 46def run_script(script, outdir, args=()): 47 """Runs fixtures/<script> with `outdir` as its output directory and returns the names of the files it left there. The 48 scripts put scratch files next to `outdir`, so `outdir` should be a directory of its own. What the script printed goes to 49 `<outdir>/../<script>.log` as it runs, so a hung script leaves its log; a failing step is logged and does not stop the run, as in the old harness.""" 50 path = FIXTURES / script 51 os.makedirs(outdir, exist_ok=True) 52 logfile = Path(outdir).parent / (script + ".log") 53 log = open(logfile, "w", buffering=1, encoding="utf-8") 54 saved = (sys.argv, sys.modules.get("unohelp"), os._exit, subprocess.run) 55 sys.argv = [str(path), "0", str(outdir)] + list(args) 56 sys.modules["unohelp"] = unohelp 57 os._exit, subprocess.run = _no_exit, _no_run 58 code = 0 59 try: 60 with contextlib.redirect_stdout(log): 61 runpy.run_path(str(path), run_name="fixture_script") 62 except SystemExit as e: 63 code = e.code if isinstance(e.code, int) else (0 if e.code is None else 1) 64 except Exception: 65 log.write(traceback.format_exc()) 66 code = "error" 67 raise 68 finally: 69 sys.argv, mod, os._exit, subprocess.run = saved 70 if mod is None: 71 sys.modules.pop("unohelp", None) 72 else: 73 sys.modules["unohelp"] = mod 74 log.write("exit %s\n" % code) 75 log.close() 76 return sorted(p.name for p in Path(outdir).iterdir() if p.is_file()) 77