xref: /trunk/test/aoo-test/src/aootest_worker/ops/scripts.py (revision 85d6e4e58e50c1ed01601e2737a66d9715456246)
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