diff options
Diffstat (limited to 'uitest/libreoffice/connection.py')
-rw-r--r-- | uitest/libreoffice/connection.py | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/uitest/libreoffice/connection.py b/uitest/libreoffice/connection.py new file mode 100644 index 000000000..2b0e9c227 --- /dev/null +++ b/uitest/libreoffice/connection.py @@ -0,0 +1,193 @@ +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +import subprocess +import time +import traceback +import uuid +import os + +try: + import pyuno + import uno + import unohelper +except ImportError: + print("pyuno not found: try to set PYTHONPATH and URE_BOOTSTRAP variables") + print("PYTHONPATH=/installation/opt/program") + print("URE_BOOTSTRAP=file:///installation/opt/program/fundamentalrc") + raise + + +class OfficeConnection: + def __init__(self, args): + self.args = args + self.soffice = None + self.xContext = None + + def setUp(self): + """ Create a new connection to a LibreOffice process + + If the connection method is path the instance will be created as a + new subprocess. If the connection method is connect the instance tries + to connect to an existing instance with the specified socket string """ + (method, sep, rest) = self.args["--soffice"].partition(":") + if sep != ":": + raise Exception("soffice parameter does not specify method") + if method == "path": + socket = "pipe,name=pytest" + str(uuid.uuid1()) + try: + userdir = self.args["--userdir"] + except KeyError: + raise Exception("'path' method requires --userdir") + if not(userdir.startswith("file://")): + raise Exception("--userdir must be file URL") + self.soffice = self.bootstrap(rest, userdir, socket) + elif method == "connect": + socket = rest + else: + raise Exception("unsupported connection method: " + method) + + # connect to the soffice instance + success = False + try: + self.xContext = self.connect(socket) + success = True + finally: + if not success and self.soffice: + self.soffice.terminate() + self.soffice.wait() + self.soffice = None + + def bootstrap(self, soffice, userdir, socket): + """ Creates a new LibreOffice process + + @param soffice Path to the soffice installation + @param userdir Directory of the user profile, only one process per user + profile is possible + @param socket The socket string used for the PyUNO connection """ + + argv = [soffice, "--accept=" + socket + ";urp", + "-env:UserInstallation=" + userdir, + "--quickstart=no", "--nofirststartwizard", + "--norestore", "--nologo"] + if "--valgrind" in self.args: + argv.append("--valgrind") + + if "--gdb" in self.args: + argv.insert(0, "gdb") + argv.insert(1, "-ex") + argv.insert(2, "run") + argv.insert(3, "--args") + argv[4] = argv[4].replace("soffice", "soffice.bin") + + env = None + environ = dict(os.environ) + if 'LIBO_LANG' in environ: + env = environ + env['LC_ALL'] = environ['LIBO_LANG'] + + return subprocess.Popen(argv, env=env) + + def connect(self, socket): + """ Tries to connect to the LibreOffice instance through the specified socket""" + xLocalContext = uno.getComponentContext() + xUnoResolver = xLocalContext.ServiceManager.createInstanceWithContext( + "com.sun.star.bridge.UnoUrlResolver", xLocalContext) + url = "uno:" + socket + ";urp;StarOffice.ComponentContext" + print("OfficeConnection: connecting to: " + url) + while True: + if self.soffice and self.soffice.poll() is not None: + raise Exception("soffice has stopped.") + + try: + xContext = xUnoResolver.resolve(url) + return xContext + except pyuno.getClass("com.sun.star.connection.NoConnectException"): + print("NoConnectException: sleeping...") + time.sleep(1) + + def tearDown(self): + """Terminate a LibreOffice instance created with the path connection method. + + Tries to terminate the soffice instance through the normal + XDesktop::terminate method and waits indefinitely for the subprocess + to terminate """ + + if self.soffice: + if self.xContext: + try: + print("tearDown: calling terminate()...") + xMgr = self.xContext.ServiceManager + xDesktop = xMgr.createInstanceWithContext( + "com.sun.star.frame.Desktop", self.xContext) + xDesktop.terminate() + print("...done") + except pyuno.getClass("com.sun.star.beans.UnknownPropertyException"): + print("caught while TearDown:\n", traceback.format_exc()) + pass # ignore, also means disposed + except pyuno.getClass("com.sun.star.lang.DisposedException"): + print("caught while TearDown:\n", traceback.format_exc()) + pass # ignore + else: + self.soffice.terminate() + + ret = self.soffice.wait() + self.xContext = None + self.soffice = None + if ret != 0: + raise Exception("Exit status indicates failure: " + str(ret)) + + @classmethod + def getHelpText(cls): + message = """ + --soffice=method:location + specify soffice instance to connect to + supported methods: 'path', 'connect' + --userdir=URL specify user installation directory for 'path' method + --valgrind pass --valgrind to soffice for 'path' method + + 'location' is a pathname, not a URL. 'userdir' is a URL. + """ + return message + + +class PersistentConnection: + def __init__(self, args): + self.args = args + self.connection = None + + def getContext(self): + """ Returns the XContext corresponding to the LibreOffice instance + + This is the starting point for any PyUNO access to the LibreOffice + instance.""" + return self.connection.xContext + + def setUp(self): + # don't create two connections + if self.connection: + return + + conn = OfficeConnection(self.args) + conn.setUp() + self.connection = conn + + def tearDown(self): + if self.connection: + try: + self.connection.tearDown() + finally: + self.connection = None + + def kill(self): + """ Kills the LibreOffice instance if it was created through the connection + + Only works with the connection method path""" + if self.connection and self.connection.soffice: + self.connection.soffice.kill() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: |