Date: prev next · Thread: first prev next last
2013 Archives by date, by thread · List index


Hi,

I have submitted a patch for review:

    https://gerrit.libreoffice.org/3128

To pull it, you can do:

    git pull ssh://gerrit.libreoffice.org:29418/core refs/changes/28/3128/1

set up python unit test infrastructure

Extract boostraping code from convwatch.py to unotest.py. Use python builtin
unittest module as unit test framework. Specify the unit test modules in make
file. Another option would be to use discover mode of unittest module.

Add __pycache__ to global .gitignore to keep the source directory clean.
Another option would be to deliver the unit tests to workdir prior to test
execution.

Currently only system python3 is supported.

Change-Id: Id481af5635e23fecfb9505e6e54a546a01602419
---
M .gitignore
A solenv/gbuild/PythonTest.mk
M solenv/gbuild/TargetLocations.mk
M solenv/gbuild/gbuild.mk
M sw/Module_sw.mk
A sw/PythonTest_sw_unoapi.mk
A sw/qa/unoapi/python/get_expression.py
A sw/qa/unoapi/python/set_expression.py
A unotest/source/python/org/__init__.py
A unotest/source/python/org/libreoffice/__init__.py
A unotest/source/python/org/libreoffice/unotest.py
11 files changed, 421 insertions(+), 0 deletions(-)



diff --git a/.gitignore b/.gitignore
index fc3ecb5..1a9bf30 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,5 +75,6 @@
 /solenv/gdb/libreoffice/*.pyo
 /solenv/gdb/libreoffice/util/*.pyo
 /moz/zipped/*.zip
+__pycache__
 
 
diff --git a/solenv/gbuild/PythonTest.mk b/solenv/gbuild/PythonTest.mk
new file mode 100644
index 0000000..d97b2a8
--- /dev/null
+++ b/solenv/gbuild/PythonTest.mk
@@ -0,0 +1,83 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+# PythonTest class
+
+# TODO: FixMe problem with internal python:
+# Fatal Python error: Py_Initialize: Unable to get the locale encoding
+#gb_Python_EXE := $(call gb_Executable_get_command,python)
+gb_Python_EXE := python3
+
+gb_PythonTest_COMMAND := $(gb_Python_EXE) -m unittest
+
+.PHONY : $(call gb_PythonTest_get_clean_target,%)
+$(call gb_PythonTest_get_clean_target,%) :
+       $(call gb_Helper_abbreviate_dirs,\
+               rm -f $@ $@.log)
+
+ifneq ($(DISABLE_PYTHON),TRUE)
+
+.PHONY : $(call gb_PythonTest_get_target,%)
+$(call gb_PythonTest_get_target,%) :
+       $(call gb_Output_announce,$*,$(true),PYT,2)
+       $(call gb_Helper_abbreviate_dirs,\
+        mkdir -p $(dir $(call gb_PythonTest_get_target,$*)) && \
+        (PYTHONPATH=$(SRCDIR)/unotest/source/python:$(DEVINSTALLDIR)/opt/program \
+         SOFFICE_BIN=$(DEVINSTALLDIR)/opt/program/soffice \
+         URE_BOOTSTRAP=file://$(DEVINSTALLDIR)/opt/program/fundamentalrc \
+                   $(gb_PythonTest_COMMAND) \
+            $(CLASSES) > $@.log 2>&1 || \
+               (cat $@.log \
+               && false)))
+       $(CLEAN_CMD)
+
+define gb_PythonTest_PythonTest
+$(call gb_PythonTest_get_target,$(1)) : T_CP :=
+$(call gb_PythonTest_get_target,$(1)) : CLASSES :=
+
+$(eval $(call gb_Module_register_target,$(call gb_PythonTest_get_target,$(1)),$(call 
gb_PythonTest_get_clean_target,$(1))))
+$(call gb_Helper_make_userfriendly_targets,$(1),PythonTest)
+
+endef
+
+define gb_PythonTest_add_classes
+$(call gb_PythonTest_get_target,$(1)) : CLASSES += $(2)
+
+endef
+
+define gb_PythonTest_add_class
+$(call gb_PythonTest_add_classes,$(1),$(2))
+
+endef
+
+define gb_PythonTest_use_customtarget
+$(call gb_PythonTest_get_target,$(1)) : $(call gb_CustomTarget_get_workdir,$(2))
+
+endef
+
+
+else # DISABLE_PYTHON
+
+.PHONY : $(call gb_PythonTest_get_target,$(1))
+$(call gb_PythonTest_get_target,%) :
+       $(call gb_Output_announce,$* (skipped - no PythonTest),$(true),PYT,2)
+       @true
+
+define gb_PythonTest_PythonTest
+$(eval $(call gb_Module_register_target,$(call gb_PythonTest_get_target,$(1)),$(call 
gb_PythonTest_get_clean_target,$(1))))
+$(call gb_Helper_make_userfriendly_targets,$(1),PythonTest)
+
+endef
+
+gb_PythonTest_add_classes :=
+gb_PythonTest_add_class :=
+gb_JunitTest_use_customtarget :=
+
+endif # DISABLE_PYTHON
+# vim: set noet sw=4:
diff --git a/solenv/gbuild/TargetLocations.mk b/solenv/gbuild/TargetLocations.mk
index b738b65..25dd542 100644
--- a/solenv/gbuild/TargetLocations.mk
+++ b/solenv/gbuild/TargetLocations.mk
@@ -147,6 +147,7 @@
 gb_JunitTest_get_classsetname = JunitTest/$(1)
 gb_JunitTest_get_target = $(WORKDIR)/JunitTest/$(1)/done
 gb_JunitTest_get_userdir = $(WORKDIR)/JunitTest/$(1)/user
+gb_PythonTest_get_target = $(WORKDIR)/PythonTest/$(1)/done
 gb_LinkTarget_get_external_headers_target = $(WORKDIR)/ExternalHeaders/$(1)
 gb_LinkTarget_get_headers_target = $(WORKDIR)/Headers/$(1)
 gb_LinkTarget_get_target = $(WORKDIR)/LinkTarget/$(1)
@@ -289,6 +290,7 @@
        Pagein \
        PrecompiledHeader \
        Pyuno \
+       PythonTest \
        Rdb \
        ResTarget \
        ScpMergeTarget \
diff --git a/solenv/gbuild/gbuild.mk b/solenv/gbuild/gbuild.mk
index 3cc234e..d95cd32 100644
--- a/solenv/gbuild/gbuild.mk
+++ b/solenv/gbuild/gbuild.mk
@@ -315,6 +315,7 @@
        Pagein \
        PrecompiledHeaders \
        Pyuno \
+       PythonTest \
        Rdb \
        CppunitTest \
        Jar \
diff --git a/sw/Module_sw.mk b/sw/Module_sw.mk
index d11f167..2f6e75b 100644
--- a/sw/Module_sw.mk
+++ b/sw/Module_sw.mk
@@ -66,4 +66,13 @@
 ))
 endif
 
+# TODO: FixMe restrict to system python
+ifneq ($(DISABLE_PYTHON),TRUE)
+ifeq ($(SYSTEM_PYTHON),YES)
+$(eval $(call gb_Module_add_subsequentcheck_targets,sw,\
+       PythonTest_sw_unoapi \
+))
+endif
+endif
+
 # vim: set noet sw=4 ts=4:
diff --git a/sw/PythonTest_sw_unoapi.mk b/sw/PythonTest_sw_unoapi.mk
new file mode 100644
index 0000000..de486ef
--- /dev/null
+++ b/sw/PythonTest_sw_unoapi.mk
@@ -0,0 +1,17 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_PythonTest_PythonTest,sw_unoapi))
+
+$(eval $(call gb_PythonTest_add_classes,sw_unoapi,\
+    $(SRCDIR)/sw/qa/unoapi/python/set_expression.py \
+    $(SRCDIR)/sw/qa/unoapi/python/get_expression.py \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sw/qa/unoapi/python/get_expression.py b/sw/qa/unoapi/python/get_expression.py
new file mode 100644
index 0000000..0ffe6ca
--- /dev/null
+++ b/sw/qa/unoapi/python/get_expression.py
@@ -0,0 +1,53 @@
+import unittest
+from org.libreoffice.unotest import UnoConnection
+
+class TestGetExpression(unittest.TestCase):
+    _unoCon = None
+    _xDoc = None
+
+    @classmethod
+    def setUpClass(cls):
+        cls._unoCon = UnoConnection({})
+        cls._unoCon.setUp();
+        cls._xDoc = cls._unoCon.openEmptyWriterDoc()
+
+    @classmethod
+    def tearDownClass(cls):
+        cls._unoCon.tearDown()
+
+    def test_get_expression(self):
+        self.__class__._unoCon.checkProperties(
+            self.__class__._xDoc.createInstance("com.sun.star.text.textfield.GetExpression"),
+            {"Content": "foo",
+             "CurrentPresentation": "bar",
+             "NumberFormat": 0,
+             "IsShowFormula": False,
+             "SubType": 0,
+             "VariableSubtype": 1,
+             "IsFixedLanguage": False,
+             },
+            self
+            )
+
+    # property 'Value' is read only?
+    @unittest.expectedFailure
+    def test_get_expression_veto_read_only(self):
+        self.__class__._unoCon.checkProperties(
+            self.__class__._xDoc.createInstance("com.sun.star.text.textfield.GetExpression"),
+            {"Value": 0.0},
+            self
+            )
+
+    # property 'NumberingType' is unknown?
+    @unittest.expectedFailure
+    def test_get_expression_unknown_property(self):
+        self.__class__._unoCon.checkProperties(
+            self.__class__._xDoc.createInstance("com.sun.star.text.textfield.GetExpression"),
+            {"NumberingType": 0},
+            self
+            )
+
+
+if __name__ == '__main__':
+    unittest.main()
+
diff --git a/sw/qa/unoapi/python/set_expression.py b/sw/qa/unoapi/python/set_expression.py
new file mode 100644
index 0000000..806667e
--- /dev/null
+++ b/sw/qa/unoapi/python/set_expression.py
@@ -0,0 +1,40 @@
+import unittest
+from org.libreoffice.unotest import UnoConnection
+
+#@unittest.skip("that seems to work")
+class TestSetExpresion(unittest.TestCase):
+    _unoCon = None
+    _xDoc = None
+
+    @classmethod
+    def setUpClass(cls):
+        cls._unoCon = UnoConnection({})
+        cls._unoCon.setUp();
+        cls._xDoc = cls._unoCon.openEmptyWriterDoc()
+
+    @classmethod
+    def tearDownClass(cls):
+        cls._unoCon.tearDown()
+
+    def test_set_expression(self):
+        self.__class__._unoCon.checkProperties(
+            self.__class__._xDoc.createInstance("com.sun.star.text.textfield.SetExpression"),
+            {"NumberingType": 0,
+             "Content": "foo",
+             "CurrentPresentation": "bar",
+             "NumberFormat": 0,
+             "NumberingType": 0,
+             "IsShowFormula": False,
+             "IsInput": False,
+             "IsVisible": True,
+             "SequenceValue": 0,
+             "SubType": 0,
+             "Value": 1.0,
+             "IsFixedLanguage": False
+             },
+            self
+            )
+
+if __name__ == '__main__':
+    unittest.main()
+
diff --git a/unotest/source/python/org/__init__.py b/unotest/source/python/org/__init__.py
new file mode 100644
index 0000000..e69de29
--- /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 0000000..e69de29
--- /dev/null
+++ b/unotest/source/python/org/libreoffice/__init__.py
diff --git a/unotest/source/python/org/libreoffice/unotest.py 
b/unotest/source/python/org/libreoffice/unotest.py
new file mode 100644
index 0000000..b855b58
--- /dev/null
+++ b/unotest/source/python/org/libreoffice/unotest.py
@@ -0,0 +1,215 @@
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-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 subprocess
+import time
+import uuid
+import argparse
+import os
+
+try:
+    import pyuno
+    import uno
+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
+
+try:
+    from com.sun.star.document import XDocumentEventListener
+except ImportError:
+    print("UNO API class not found: try to set URE_BOOTSTRAP variable")
+    print("URE_BOOTSTRAP=file:///installation/opt/program/fundamentalrc")
+    raise
+
+### utilities ###
+
+def mkPropertyValue(name, value):
+    return uno.createUnoStruct("com.sun.star.beans.PropertyValue", name, 0, value, 0)
+
+### 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["programm"]
+        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", "--nofirststartwizard",
+                "--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
+# WTF 255 return value?
+#            if ret != 0:
+#                raise Exception("Exit status indicates failure: " + str(ret))
+#            return ret
+
+    def getContext(self):
+        return self.xContext
+
+class UnoConnection:
+    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
+
+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()
+
+### 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="programm 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:

-- 
To view, visit https://gerrit.libreoffice.org/3128
To unsubscribe, visit https://gerrit.libreoffice.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Id481af5635e23fecfb9505e6e54a546a01602419
Gerrit-PatchSet: 1
Gerrit-Project: core
Gerrit-Branch: master
Gerrit-Owner: David Ostrovsky <David.Ostrovsky@gmx.de>


Context


Privacy Policy | Impressum (Legal Info) | Copyright information: Unless otherwise specified, all text and images on this website are licensed under the Creative Commons Attribution-Share Alike 3.0 License. This does not include the source code of LibreOffice, which is licensed under the Mozilla Public License (MPLv2). "LibreOffice" and "The Document Foundation" are registered trademarks of their corresponding registered owners or are in actual use as trademarks in one or more countries. Their respective logos and icons are also subject to international copyright laws. Use thereof is explained in our trademark policy.