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