summaryrefslogtreecommitdiffstats
path: root/unotest/source/python
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /unotest/source/python
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--unotest/source/python/org/__init__.py0
-rw-r--r--unotest/source/python/org/libreoffice/__init__.py0
-rw-r--r--unotest/source/python/org/libreoffice/unittest.py29
-rw-r--r--unotest/source/python/org/libreoffice/unotest.py326
4 files changed, 355 insertions, 0 deletions
diff --git a/unotest/source/python/org/__init__.py b/unotest/source/python/org/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/unotest/source/python/org/__init__.py
diff --git a/unotest/source/python/org/libreoffice/__init__.py b/unotest/source/python/org/libreoffice/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/unotest/source/python/org/libreoffice/__init__.py
diff --git a/unotest/source/python/org/libreoffice/unittest.py b/unotest/source/python/org/libreoffice/unittest.py
new file mode 100644
index 000000000..f6f93080b
--- /dev/null
+++ b/unotest/source/python/org/libreoffice/unittest.py
@@ -0,0 +1,29 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# 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 gc
+import pyuno
+import unittest
+
+class LoTestResult(unittest.TextTestResult):
+ def stopTestRun(self):
+ # HACK calling gc.collect() to get rid of as many still existing UNO proxies to
+ # SwXTextDocument and friends as possible; those C++ classes' dtors call
+ # Application::GetSolarMutex via sw::UnoImplPtrDeleter, so the dtors must be called before
+ # DeInitVCL in the call to pyuno.private_deinitTestEnvironment(); any remaining proxies
+ # that are still referenced (UnoInProcess' self.xDoc in
+ # unotest/source/python/org/libreoffice/unotest.py, or per-class variables in the various
+ # PythonTests) need to be individually released (each marked as "HACK" in the code):
+ gc.collect()
+ pyuno.private_deinitTestEnvironment()
+
+if __name__ == '__main__':
+ unittest.main(module=None, testRunner=unittest.TextTestRunner(resultclass=LoTestResult))
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/unotest/source/python/org/libreoffice/unotest.py b/unotest/source/python/org/libreoffice/unotest.py
new file mode 100644
index 000000000..e27f9e145
--- /dev/null
+++ b/unotest/source/python/org/libreoffice/unotest.py
@@ -0,0 +1,326 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# 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 pathlib
+import subprocess
+import time
+import uuid
+import argparse
+import os
+import shutil
+import urllib.parse
+import urllib.request
+
+try:
+ import pyuno
+ import uno
+except ImportError:
+ print("pyuno not found: try to set PYTHONPATH and URE_BOOTSTRAP variables")
+ print("to something like:")
+ print(" PYTHONPATH=/installation/opt/program")
+ print(" URE_BOOTSTRAP=file:///installation/opt/program/fundamentalrc")
+ raise
+
+try:
+ from com.sun.star.document import XDocumentEventListener
+except ImportError:
+ print("UNO API class not found: try to set URE_BOOTSTRAP variable")
+ print("to something like:")
+ print(" URE_BOOTSTRAP=file:///installation/opt/program/fundamentalrc")
+ raise
+
+try:
+ from urllib.parse import quote
+except ImportError:
+ from urllib import quote
+
+### utilities ###
+
+def mkPropertyValue(name, value):
+ return uno.createUnoStruct("com.sun.star.beans.PropertyValue", name, 0, value, 0)
+
+def mkPropertyValues(**kwargs):
+ '''mkPropertyValues(Name=Value, Name=Value,...) -> (PropertyValue, PropertyValue,...)
+ ex. : mkPropertyValues(Hidden=True, ReadOnly=False)'''
+ from com.sun.star.beans import PropertyValue
+ return tuple(PropertyValue(k,0,kwargs[k],0) for k in kwargs)
+
+def fileUrlToSystemPath(url):
+ return pyuno.fileUrlToSystemPath(url)
+
+def systemPathToFileUrl(systemPath):
+ return pyuno.systemPathToFileUrl(systemPath)
+
+### UNO utilities ###
+
+class OfficeConnection(object):
+
+ def __init__(self, args):
+ self.args = args
+ self.soffice = None
+ self.xContext = None
+ self.channel = None
+
+ def setUp(self):
+ try:
+ self.verbose = self.args["verbose"]
+ except KeyError:
+ self.verbose = False
+ try:
+ prog = self.args["program"]
+ except KeyError:
+ prog = os.getenv("SOFFICE_BIN")
+ if not (prog):
+ raise Exception("SOFFICE_BIN must be set")
+ channel = "pipe,name=pytest" + str(uuid.uuid1())
+ try:
+ userdir = self.args["userdir"]
+ except KeyError:
+ userdir = "file:///tmp"
+ if not(userdir.startswith("file://")):
+ raise Exception("--userdir must be file URL")
+ self.soffice = self.bootstrap(prog, userdir, channel)
+ return self.connect(channel)
+
+ def bootstrap(self, soffice, userdir, channel):
+ argv = [ soffice, "--accept=" + channel + ";urp",
+ "-env:UserInstallation=" + userdir,
+ "--quickstart=no",
+ "--norestore", "--nologo", "--headless"]
+ if "--valgrind" in self.args:
+ argv.append("--valgrind")
+ if self.verbose:
+ print ("starting LibreOffice with channel: ", channel)
+ return subprocess.Popen(argv)
+
+ def connect(self, channel):
+ xLocalContext = uno.getComponentContext()
+ xUnoResolver = xLocalContext.ServiceManager.createInstanceWithContext(
+ "com.sun.star.bridge.UnoUrlResolver", xLocalContext)
+ url = ("uno:%s;urp;StarOffice.ComponentContext" % channel)
+ if self.verbose:
+ print("Connecting to: ", url)
+ while True:
+ try:
+ self.xContext = xUnoResolver.resolve(url)
+ return self.xContext
+# except com.sun.star.connection.NoConnectException
+ except pyuno.getClass("com.sun.star.connection.NoConnectException"):
+ print("WARN: NoConnectException: sleeping...")
+ time.sleep(1)
+
+ def tearDown(self):
+ if self.soffice:
+ if self.xContext:
+ try:
+ if self.verbose:
+ print("tearDown: calling terminate()...")
+ xMgr = self.xContext.ServiceManager
+ xDesktop = xMgr.createInstanceWithContext(
+ "com.sun.star.frame.Desktop", self.xContext)
+ xDesktop.terminate()
+ if self.verbose:
+ print("...done")
+# except com.sun.star.lang.DisposedException:
+ except pyuno.getClass("com.sun.star.beans.UnknownPropertyException"):
+ print("caught UnknownPropertyException")
+ pass # ignore, also means disposed
+ except pyuno.getClass("com.sun.star.lang.DisposedException"):
+ print("caught DisposedException")
+ pass # ignore
+ else:
+ self.soffice.terminate()
+ ret = self.soffice.wait()
+ self.xContext = None
+ self.socket = None
+ self.soffice = None
+ if ret != 0:
+ raise Exception("Exit status indicates failure: " + str(ret))
+
+ def getContext(self):
+ return self.xContext
+
+class UnoRemoteConnection:
+ def __init__(self, args):
+ self.args = args
+ self.connection = None
+ def getContext(self):
+ return self.connection.xContext
+ def getDoc(self):
+ return self.xDoc
+ def setUp(self):
+ conn = OfficeConnection(self.args)
+ conn.setUp()
+ self.connection = conn
+ def openEmptyWriterDoc(self):
+ assert(self.connection)
+ smgr = self.getContext().ServiceManager
+ desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", self.getContext())
+ props = [("Hidden", True), ("ReadOnly", False)]
+ loadProps = tuple([mkPropertyValue(name, value) for (name, value) in props])
+ self.xDoc = desktop.loadComponentFromURL("private:factory/swriter", "_blank", 0, loadProps)
+ return self.xDoc
+
+ def checkProperties(self, obj, dict, test):
+ for k,v in dict.items():
+ obj.setPropertyValue(k, v)
+ value = obj.getPropertyValue(k)
+ test.assertEqual(value, v)
+
+ def postTest(self):
+ assert(self.connection)
+ def tearDown(self):
+ if self.connection:
+ try:
+ self.connection.tearDown()
+ finally:
+ self.connection = None
+
+havePonies = False
+
+class UnoInProcess:
+ def getContext(self):
+ return self.xContext
+ def getDoc(self):
+ return self.xDoc
+ def setUp(self):
+ self.xContext = pyuno.getComponentContext()
+ global havePonies
+ if not(havePonies):
+ pyuno.private_initTestEnvironment()
+ havePonies = True
+
+ def openEmptyWriterDoc(self):
+ return self.openEmptyDoc("private:factory/swriter")
+
+ def openEmptyCalcDoc(self):
+ return self.openEmptyDoc("private:factory/scalc")
+
+ def openEmptyDoc(self, url, bHidden = True, bReadOnly = False):
+ props = [("Hidden", bHidden), ("ReadOnly", bReadOnly)]
+ return self.__openDocFromURL(url, props)
+
+ def openTemplateFromTDOC(self, file):
+ return self.openDocFromTDOC(file, True)
+
+ def openDocFromTDOC(self, file, asTemplate = False):
+ path = makeCopyFromTDOC(file)
+ return self.openDocFromAbsolutePath(path, asTemplate)
+
+ def openDocFromAbsolutePath(self, file, asTemplate = False):
+ return self.openDocFromURL(pathlib.Path(file).as_uri(), asTemplate)
+
+ def openDocFromURL(self, url, asTemplate = False):
+ props = [("Hidden", True), ("ReadOnly", False), ("AsTemplate", asTemplate)]
+ return self.__openDocFromURL(url, props)
+
+ def __openDocFromURL(self, url, props):
+ assert(self.xContext)
+ smgr = self.getContext().ServiceManager
+ desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", self.getContext())
+ loadProps = tuple([mkPropertyValue(name, value) for (name, value) in props])
+ self.xDoc = desktop.loadComponentFromURL(url, "_blank", 0, loadProps)
+ assert(self.xDoc)
+ return self.xDoc
+
+ def checkProperties(self, obj, dict, test):
+ for k,v in dict.items():
+ obj.setPropertyValue(k, v)
+ value = obj.getPropertyValue(k)
+ test.assertEqual(value, v)
+
+ def setProperties(self, obj, dict):
+ for k,v in dict.items():
+ obj.setPropertyValue(k, v)
+
+ def postTest(self):
+ assert(self.xContext)
+ def tearDown(self):
+ if hasattr(self, 'xDoc'):
+ if self.xDoc:
+ self.xDoc.close(True)
+ # HACK in case self.xDoc holds a UNO proxy to an SwXTextDocument (whose dtor calls
+ # Application::GetSolarMutex via sw::UnoImplPtrDeleter), which would potentially only be
+ # garbage-collected after VCL has already been deinitialized:
+ self.xDoc = None
+
+def simpleInvoke(connection, test):
+ try:
+ connection.preTest()
+ test.run(connection.getContext())
+ finally:
+ connection.postTest()
+
+def retryInvoke(connection, test):
+ tries = 5
+ while tries > 0:
+ try:
+ tries -= 1
+ try:
+ connection.preTest()
+ test.run(connection.getContext())
+ return
+ finally:
+ connection.postTest()
+ except KeyboardInterrupt:
+ raise # Ctrl+C should work
+ except:
+ print("retryInvoke: caught exception")
+ raise Exception("FAILED retryInvoke")
+
+def runConnectionTests(connection, invoker, tests):
+ try:
+ connection.setUp()
+ for test in tests:
+ invoker(connection, test)
+ finally:
+ connection.tearDown()
+
+def makeCopyFromTDOC(file):
+ src = os.getenv("TDOC")
+ assert(src is not None)
+ src = os.path.join(src, file)
+ dst = os.getenv("TestUserDir")
+ assert(dst is not None)
+ uri = urllib.parse.urlparse(dst)
+ assert(uri.scheme.casefold() == "file")
+ assert(uri.netloc == "" or uri.netloc.casefold() == "localhost")
+ assert(uri.params == "")
+ assert(uri.query == "")
+ assert(uri.fragment == "")
+ dst = urllib.request.url2pathname(uri.path)
+ dst = os.path.join(dst, "tmp", file)
+ os.makedirs(os.path.dirname(dst), exist_ok=True)
+ try:
+ os.remove(dst)
+ except FileNotFoundError:
+ pass
+ shutil.copyfile(src, dst)
+ return dst
+
+### tests ###
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser("Help utilities for testing LibreOffice")
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true")
+ #parser.add_argument("p", type=str, help="program name")
+ args = parser.parse_args()
+ if args.verbose:
+ verbose = True
+ con = PersistentConnection({"verbose" : args.verbose})
+ print("starting soffice ... ", end="")
+ con.setUp()
+ print("done")
+ con.get
+ print ("shutting down ... ", end="")
+ con.tearDown()
+ print("done")
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab: