office-gobmx/uitest/libreoffice/connection.py
Mike Kaganski 150b67b57b UITest: Actually time-out the wait
Without that, if XDesktop::terminate() fails (e.g., because an assert in
a test was false while a modal dialog was open, which left the dialog
open, and so TerminationVetoException was thrown in XDesktop::terminate()
by a listener), the wait never ends, and the assertion message gets lost
when buildbot terminates the build, as happened in
https://ci.libreoffice.org/job/gerrit_linux_clang_dbgutil/51496/console.

The timeout only available since python 3.3.

The soffice process is not terminated in this case; hopefully it will be
terminated when build cleanup will occur.

Change-Id: I924775d0e58619d1fbd603e80ed1f8d047c91145
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/87362
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
2020-01-25 20:03:15 +01:00

197 lines
7 KiB
Python

# -*- 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 uuid
import os
import sys
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.socket = None
self.xContext = None
self.pro = 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
self.xContext = self.connect(socket)
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']
self.pro = subprocess.Popen(argv, env=env)
return self.pro
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.
First tries to terminate the soffice instance through the normal
XDesktop::terminate method and waits for about 30 seconds before
considering this attempt failed. After the 30 seconds the subprocess
is terminated """
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 UnknownPropertyException while TearDown")
pass # ignore, also means disposed
except pyuno.getClass("com.sun.star.lang.DisposedException"):
print("caught DisposedException while TearDown")
pass # ignore
else:
self.soffice.terminate()
try:
if sys.version_info >= (3,3):
ret = self.soffice.wait(30) # will throw when timed out
else:
ret = self.soffice.wait() # no timeout in python that old
finally:
self.xContext = None
self.socket = 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: