1*2cb6d6f1SJim Jagielski################################################################ 2*2cb6d6f1SJim Jagielski# 3*2cb6d6f1SJim Jagielski# Licensed to the Apache Software Foundation (ASF) under one 4*2cb6d6f1SJim Jagielski# or more contributor license agreements. See the NOTICE file 5*2cb6d6f1SJim Jagielski# distributed with this work for additional information 6*2cb6d6f1SJim Jagielski# regarding copyright ownership. The ASF licenses this file 7*2cb6d6f1SJim Jagielski# to you under the Apache License, Version 2.0 (the 8*2cb6d6f1SJim Jagielski# "License"); you may not use this file except in compliance 9*2cb6d6f1SJim Jagielski# with the License. You may obtain a copy of the License at 10*2cb6d6f1SJim Jagielski# 11*2cb6d6f1SJim Jagielski# http://www.apache.org/licenses/LICENSE-2.0 12*2cb6d6f1SJim Jagielski# 13*2cb6d6f1SJim Jagielski# Unless required by applicable law or agreed to in writing, 14*2cb6d6f1SJim Jagielski# software distributed under the License is distributed on an 15*2cb6d6f1SJim Jagielski# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16*2cb6d6f1SJim Jagielski# KIND, either express or implied. See the License for the 17*2cb6d6f1SJim Jagielski# specific language governing permissions and limitations 18*2cb6d6f1SJim Jagielski# under the License. 19*2cb6d6f1SJim Jagielski# 20*2cb6d6f1SJim Jagielski################################################################ 21*2cb6d6f1SJim Jagielski 22*2cb6d6f1SJim Jagielski"""A running office and its worker for one ODF save mode: wires install, office and client into an OfficeSession.""" 23*2cb6d6f1SJim Jagielskiimport os 24*2cb6d6f1SJim Jagielskiimport time 25*2cb6d6f1SJim Jagielskifrom pathlib import Path 26*2cb6d6f1SJim Jagielski 27*2cb6d6f1SJim Jagielskiimport aootest_worker 28*2cb6d6f1SJim Jagielskifrom aootest import install, office 29*2cb6d6f1SJim Jagielskifrom aootest.client import WorkerClient, WorkerCrash, WorkerHang 30*2cb6d6f1SJim Jagielski 31*2cb6d6f1SJim JagielskiSRC_DIR = str(Path(aootest_worker.__file__).resolve().parent.parent) 32*2cb6d6f1SJim Jagielski 33*2cb6d6f1SJim Jagielski 34*2cb6d6f1SJim Jagielskidef worker_env(layout, base=None): 35*2cb6d6f1SJim Jagielski """Environment for the worker's Python: the office program dir, URE_BOOTSTRAP, and this package on the path.""" 36*2cb6d6f1SJim Jagielski env = install.probe_env(layout, base) 37*2cb6d6f1SJim Jagielski env["PYTHONPATH"] = os.pathsep.join([SRC_DIR, env["PYTHONPATH"]]) 38*2cb6d6f1SJim Jagielski return env 39*2cb6d6f1SJim Jagielski 40*2cb6d6f1SJim Jagielski 41*2cb6d6f1SJim Jagielskidef uno_url(accept): 42*2cb6d6f1SJim Jagielski return "uno:" + accept + "StarOffice.ComponentContext" 43*2cb6d6f1SJim Jagielski 44*2cb6d6f1SJim Jagielski 45*2cb6d6f1SJim Jagielskiclass ModeSession(office.OfficeSession): 46*2cb6d6f1SJim Jagielski """OfficeSession for one mode (None = product default, or the Save/ODF/DefaultVersion number). The profile lives in 47*2cb6d6f1SJim Jagielski `workdir/profile_<mode>` and is kept across restarts.""" 48*2cb6d6f1SJim Jagielski 49*2cb6d6f1SJim Jagielski def __init__(self, layout, python, mode, workdir, connect="socket", max_restarts=5, connect_timeout=90.0, hello_timeout=60.0, 50*2cb6d6f1SJim Jagielski extra=None, tag=None): 51*2cb6d6f1SJim Jagielski self.layout, self.python, self.mode = layout, python, mode 52*2cb6d6f1SJim Jagielski self.name = ("default" if mode is None else str(mode)) + ("_" + tag if tag else "") 53*2cb6d6f1SJim Jagielski self.workdir = Path(workdir) 54*2cb6d6f1SJim Jagielski self.connect, self.connect_timeout, self.hello_timeout = connect, connect_timeout, hello_timeout 55*2cb6d6f1SJim Jagielski self.profile_url = office.make_profile(self.workdir / ("profile_" + self.name), mode, extra) 56*2cb6d6f1SJim Jagielski self.starts = 0 57*2cb6d6f1SJim Jagielski self.started = [] 58*2cb6d6f1SJim Jagielski super().__init__(self._start_office_proc, self._start_worker_proc, max_restarts) 59*2cb6d6f1SJim Jagielski 60*2cb6d6f1SJim Jagielski def _start_office_proc(self): 61*2cb6d6f1SJim Jagielski accept = office.accept_string(self.connect) 62*2cb6d6f1SJim Jagielski self.starts += 1 63*2cb6d6f1SJim Jagielski log = self.workdir / ("office_%s_%d.log" % (self.name, self.starts)) 64*2cb6d6f1SJim Jagielski off = office.Office(office.command(self.layout, self.profile_url, accept), log=str(log)).start() 65*2cb6d6f1SJim Jagielski off.url = uno_url(accept) 66*2cb6d6f1SJim Jagielski self.started.append(off) 67*2cb6d6f1SJim Jagielski return off 68*2cb6d6f1SJim Jagielski 69*2cb6d6f1SJim Jagielski def _start_worker_proc(self, off): 70*2cb6d6f1SJim Jagielski client = WorkerClient([self.python.path, "-m", "aootest_worker"], env=worker_env(self.layout), hello_timeout=self.hello_timeout) 71*2cb6d6f1SJim Jagielski try: 72*2cb6d6f1SJim Jagielski client.start() 73*2cb6d6f1SJim Jagielski deadline = time.time() + self.connect_timeout 74*2cb6d6f1SJim Jagielski while True: 75*2cb6d6f1SJim Jagielski if not off.alive(): 76*2cb6d6f1SJim Jagielski raise WorkerCrash("the office exited before the worker could connect", getattr(off._proc, "returncode", None)) 77*2cb6d6f1SJim Jagielski if client.call("connect", {"url": off.url, "wait": 2.0}, timeout=30)["connected"]: 78*2cb6d6f1SJim Jagielski return client 79*2cb6d6f1SJim Jagielski if time.time() > deadline: 80*2cb6d6f1SJim Jagielski raise WorkerHang("no connection to the office within %gs" % self.connect_timeout) 81*2cb6d6f1SJim Jagielski except BaseException: 82*2cb6d6f1SJim Jagielski client.close() 83*2cb6d6f1SJim Jagielski raise 84