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################################################################ 21 22"""A running office and its worker for one ODF save mode: wires install, office and client into an OfficeSession.""" 23import os 24import time 25from pathlib import Path 26 27import aootest_worker 28from aootest import install, office 29from aootest.client import WorkerClient, WorkerCrash, WorkerHang 30 31SRC_DIR = str(Path(aootest_worker.__file__).resolve().parent.parent) 32 33 34def worker_env(layout, base=None): 35 """Environment for the worker's Python: the office program dir, URE_BOOTSTRAP, and this package on the path.""" 36 env = install.probe_env(layout, base) 37 env["PYTHONPATH"] = os.pathsep.join([SRC_DIR, env["PYTHONPATH"]]) 38 return env 39 40 41def uno_url(accept): 42 return "uno:" + accept + "StarOffice.ComponentContext" 43 44 45class ModeSession(office.OfficeSession): 46 """OfficeSession for one mode (None = product default, or the Save/ODF/DefaultVersion number). The profile lives in 47 `workdir/profile_<mode>` and is kept across restarts.""" 48 49 def __init__(self, layout, python, mode, workdir, connect="socket", max_restarts=5, connect_timeout=90.0, hello_timeout=60.0, 50 extra=None, tag=None): 51 self.layout, self.python, self.mode = layout, python, mode 52 self.name = ("default" if mode is None else str(mode)) + ("_" + tag if tag else "") 53 self.workdir = Path(workdir) 54 self.connect, self.connect_timeout, self.hello_timeout = connect, connect_timeout, hello_timeout 55 self.profile_url = office.make_profile(self.workdir / ("profile_" + self.name), mode, extra) 56 self.starts = 0 57 self.started = [] 58 super().__init__(self._start_office_proc, self._start_worker_proc, max_restarts) 59 60 def _start_office_proc(self): 61 accept = office.accept_string(self.connect) 62 self.starts += 1 63 log = self.workdir / ("office_%s_%d.log" % (self.name, self.starts)) 64 off = office.Office(office.command(self.layout, self.profile_url, accept), log=str(log)).start() 65 off.url = uno_url(accept) 66 self.started.append(off) 67 return off 68 69 def _start_worker_proc(self, off): 70 client = WorkerClient([self.python.path, "-m", "aootest_worker"], env=worker_env(self.layout), hello_timeout=self.hello_timeout) 71 try: 72 client.start() 73 deadline = time.time() + self.connect_timeout 74 while True: 75 if not off.alive(): 76 raise WorkerCrash("the office exited before the worker could connect", getattr(off._proc, "returncode", None)) 77 if client.call("connect", {"url": off.url, "wait": 2.0}, timeout=30)["connected"]: 78 return client 79 if time.time() > deadline: 80 raise WorkerHang("no connection to the office within %gs" % self.connect_timeout) 81 except BaseException: 82 client.close() 83 raise 84