diff options
Diffstat (limited to 'uitest')
-rw-r--r-- | uitest/__init__.py | 0 | ||||
-rw-r--r-- | uitest/calc_tests/__init__.py | 0 | ||||
-rw-r--r-- | uitest/calc_tests/about_test.py | 48 | ||||
-rw-r--r-- | uitest/calc_tests/create_range_name.py | 42 | ||||
-rw-r--r-- | uitest/connection.py | 147 | ||||
-rw-r--r-- | uitest/demo_ui/checkbox.py | 37 | ||||
-rw-r--r-- | uitest/demo_ui/combobox.py | 28 | ||||
-rw-r--r-- | uitest/demo_ui/edit.py | 47 | ||||
-rw-r--r-- | uitest/demo_ui/listbox.py | 45 | ||||
-rw-r--r-- | uitest/helper.py | 93 | ||||
-rw-r--r-- | uitest/main.py | 66 | ||||
-rwxr-xr-x | uitest/screenshot.py | 12 | ||||
-rw-r--r-- | uitest/uitest_helper.py | 77 |
13 files changed, 642 insertions, 0 deletions
diff --git a/uitest/__init__.py b/uitest/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/uitest/__init__.py diff --git a/uitest/calc_tests/__init__.py b/uitest/calc_tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/uitest/calc_tests/__init__.py diff --git a/uitest/calc_tests/about_test.py b/uitest/calc_tests/about_test.py new file mode 100644 index 000000000000..d90ee06c5a75 --- /dev/null +++ b/uitest/calc_tests/about_test.py @@ -0,0 +1,48 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-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 time + +from uitest_helper import UITest +from helper import Screenshot + +def get_bounding_box(props): + size_str = None + pos_str = None + for prop in props: + if prop.Name == "AbsPosition": + pos_str = prop.Value + elif prop.Name == "Size": + size_str = prop.Value + + x1, y1 = pos_str.split("x") + dx, dy = size_str.split("x") + return x1, y1, str(int(x1) + int(dx)), str(int(y1) + int(dy)) + +def test_about_dlg_with_screenshot(xContext): + xUITest = xContext.ServiceManager.createInstanceWithContext( + "org.libreoffice.uitest.UITest", xContext) + + ui_test = UITest(xUITest, xContext) + + ui_test.create_doc_in_start_center("calc") + + ui_test.execute_dialog_through_command(".uno:About") + + xAboutDlg = xUITest.getTopFocusWindow() + + # take the screenshot + time.sleep(1) + aboutDlgState = xAboutDlg.getState() + x1, y1, x2, y2 = get_bounding_box(aboutDlgState) + screenshot = Screenshot() + screenshot.take_screenshot(x1, y1, x2, y2) + + xCloseBtn = xAboutDlg.getChild("close") + xCloseBtn.executeAction("CLICK", tuple()) + +# vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/uitest/calc_tests/create_range_name.py b/uitest/calc_tests/create_range_name.py new file mode 100644 index 000000000000..901975396ad3 --- /dev/null +++ b/uitest/calc_tests/create_range_name.py @@ -0,0 +1,42 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-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/. +# + +from uitest_helper import UITest + +from helper import mkPropertyValues + +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 + +def create_range_name(xContext): + xUITest = xContext.ServiceManager.createInstanceWithContext( + "org.libreoffice.uitest.UITest", xContext) + + ui_test = UITest(xUITest, xContext) + + ui_test.create_doc_in_start_center("calc") + + ui_test.execute_modeless_dialog_through_command(".uno:AddName") + + xAddNameDlg = xUITest.getTopFocusWindow() + + props = {"TEXT": "simpleRangeName"} + actionProps = mkPropertyValues(props) + + xEdit = xAddNameDlg.getChild("edit") + xEdit.executeAction("TYPE", actionProps) + xAddBtn = xAddNameDlg.getChild("add") + xAddBtn.executeAction("CLICK", tuple()) + +# vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/uitest/connection.py b/uitest/connection.py new file mode 100644 index 000000000000..0c87f30c2541 --- /dev/null +++ b/uitest/connection.py @@ -0,0 +1,147 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-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 os +import subprocess +import sys +import time +import uuid +import datetime + +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): + (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) + self.xContext = self.connect(socket) + + def bootstrap(self, soffice, userdir, socket): + argv = [ soffice, "--accept=" + socket + ";urp", + "-env:UserInstallation=" + userdir, + "--quickstart=no", "--nofirststartwizard", + "--norestore", "--nologo" ] + if "--valgrind" in self.args: + argv.append("--valgrind") + self.pro = subprocess.Popen(argv) + return self.pro + + def connect(self, 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: + try: + xContext = xUnoResolver.resolve(url) + return xContext +# except com.sun.star.connection.NoConnectException + except pyuno.getClass("com.sun.star.connection.NoConnectException"): + print("NoConnectException: sleeping...") + time.sleep(1) + + def tearDown(self): + 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 com.sun.star.lang.DisposedException: + 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() + 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 kill(self): + command = "kill " + str(self.pro.pid) + print(command) + os.system(command) + + @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): + return self.connection.xContext + def setUp(self): + assert(not self.connection) + conn = OfficeConnection(self.args) + conn.setUp() + self.connection = conn + def preTest(self): + assert(self.connection) + def postTest(self): + assert(self.connection) + def tearDown(self): + if self.connection: + try: + self.connection.tearDown() + finally: + self.connection = None + def kill(self): + if self.connection: + self.connection.kill() + +# vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/uitest/demo_ui/checkbox.py b/uitest/demo_ui/checkbox.py new file mode 100644 index 000000000000..b85565251951 --- /dev/null +++ b/uitest/demo_ui/checkbox.py @@ -0,0 +1,37 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-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/. +# + +from uitest_helper import UITest + +from helper import mkPropertyValues + +import time + +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 + +def toggle_checkbox(xContext): + xUITest = xContext.ServiceManager.createInstanceWithContext( + "org.libreoffice.uitest.UITest", xContext) + + ui_test = UITest(xUITest, xContext) + + ui_test.create_doc_in_start_center("calc") + + ui_test.execute_dialog_through_command(".uno:FormatCellDialog") + xCellsDlg = xUITest.getTopFocusWindow() + xNegativeNumRedCB = xCellsDlg.getChild("negnumred") + xNegativeNumRedCB.executeAction("CLICK",tuple()) + +# vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/uitest/demo_ui/combobox.py b/uitest/demo_ui/combobox.py new file mode 100644 index 000000000000..8cb7ac8a96fa --- /dev/null +++ b/uitest/demo_ui/combobox.py @@ -0,0 +1,28 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-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/. +# + +from uitest_helper import UITest + +from helper import mkPropertyValues + +def select_entry_pos(xContext): + xUITest = xContext.ServiceManager.createInstanceWithContext( + "org.libreoffice.uitest.UITest", xContext) + + ui_test = UITest(xUITest, xContext) + + ui_test.create_doc_in_start_center("calc") + + ui_test.execute_modeless_dialog_through_command(".uno:AddName") + xAddNameDlg = xUITest.getTopFocusWindow() + + scopeCB = xAddNameDlg.getChild("scope") + props = {"POS": "1"} + actionProps = mkPropertyValues(props) + scopeCB.executeAction("SELECT", actionProps) + +# vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/uitest/demo_ui/edit.py b/uitest/demo_ui/edit.py new file mode 100644 index 000000000000..dab55c52a7a0 --- /dev/null +++ b/uitest/demo_ui/edit.py @@ -0,0 +1,47 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-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/. +# + +from uitest_helper import UITest + +from helper import mkPropertyValues + +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 + +def type_text(xContext): + xUITest = xContext.ServiceManager.createInstanceWithContext( + "org.libreoffice.uitest.UITest", xContext) + + ui_test = UITest(xUITest, xContext) + + ui_test.create_doc_in_start_center("calc") + + ui_test.execute_modeless_dialog_through_command(".uno:AddName") + xAddNameDlg = xUITest.getTopFocusWindow() + + xEdit = xAddNameDlg.getChild("edit") + + props = {"TEXT": "simpleRangeName"} + actionProps = mkPropertyValues(props) + xEdit.executeAction("TYPE", actionProps) + + xAddBtn = xAddNameDlg.getChild("cancel") + xAddBtn.executeAction("CLICK", tuple()) + + xUITest.executeCommand(".uno:CloseDoc") + + ui_test.close_doc() + + +# vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/uitest/demo_ui/listbox.py b/uitest/demo_ui/listbox.py new file mode 100644 index 000000000000..41c6e4d2191b --- /dev/null +++ b/uitest/demo_ui/listbox.py @@ -0,0 +1,45 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-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/. +# + +from uitest_helper import UITest + +from helper import mkPropertyValues + +def select_entry_pos(xContext): + xUITest = xContext.ServiceManager.createInstanceWithContext( + "org.libreoffice.uitest.UITest", xContext) + + ui_test = UITest(xUITest, xContext) + + ui_test.create_doc_in_start_center("calc") + + ui_test.execute_dialog_through_command(".uno:FormatCellDialog") + xCellsDlg = xUITest.getTopFocusWindow() + + categoryLB = xCellsDlg.getChild("categorylb") + props = {"POS": "4"} + actionProps = mkPropertyValues(props) + categoryLB.executeAction("SELECT", actionProps) + +def select_entry_text(xContext): + xUITest = xContext.ServiceManager.createInstanceWithContext( + "org.libreoffice.uitest.UITest", xContext) + + ui_test = UITest(xUITest, xContext) + + ui_test.create_doc_in_start_center("calc") + + ui_test.execute_dialog_through_command(".uno:FormatCellDialog") + xCellsDlg = xUITest.getTopFocusWindow() + + categoryLB = xCellsDlg.getChild("categorylb") + props = {"TEXT": "Time"} + + actionProps = mkPropertyValues(props) + categoryLB.executeAction("SELECT", actionProps) + +# vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/uitest/helper.py b/uitest/helper.py new file mode 100644 index 000000000000..e660739fb470 --- /dev/null +++ b/uitest/helper.py @@ -0,0 +1,93 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-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 os +import subprocess + +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 + +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 + +class EventListener(XDocumentEventListener,unohelper.Base): + + def __init__(self, xContext, eventName): + self.xGEB = xContext.ServiceManager.createInstanceWithContext( + "com.sun.star.frame.GlobalEventBroadcaster", xContext) + self.xContext = xContext + self.executed = False + self.eventName = eventName + + def __enter__(self): + self.xGEB.addDocumentEventListener(self) + return self + + def __exit__(self, type, value, traceback): + self.xGEB.removeDocumentEventListener(self) + + def documentEventOccured(self, event): + print(str(event.EventName)) + if event.EventName == self.eventName: + self.executed = True + + def disposing(event): + pass + +class Screenshot(object): + + def __init__(self): + pass + + def _create_python_path(self): + """ creates a clean PATH env variable + + We need to avoid picking the LibreOffice python and + the corresponding PYTHONPATH, PYTHONHOME variables + """ + env = os.environ.copy() + + # remove any python properties pointing to soffice internal python + del env['PYTHONPATH'] + del env['PYTHONHOME'] + + # remove path pointing to instdir to avoid picking up the soffice provided python + path = env['PATH'] + path_parts = path.split(':') + cleaned_path = (path for path in path_parts if path.find("instdir") == -1) + new_path = ":".join( cleaned_path ) + env['PATH'] = new_path + return env + + def take_screenshot(self, x1, y1, x2, y2): + env = self._create_python_path() + popen = subprocess.Popen(" ".join(["./screenshot.py", x1, y1, x2, y2]), shell=True, env=env) + popen.wait() + +def mkPropertyValue(name, value): + """ Create a UNO ProertyValue from two input values. + """ + return uno.createUnoStruct("com.sun.star.beans.PropertyValue", + name, 0, value, 0) + +def mkPropertyValues(vals): + """ Create UNO property values from a map. + """ + return tuple([mkPropertyValue(name, value) for (name, value) in vals.items()]) + +# vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/uitest/main.py b/uitest/main.py new file mode 100644 index 000000000000..11e92ee677db --- /dev/null +++ b/uitest/main.py @@ -0,0 +1,66 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-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 sys +import getopt +import os +import importlib + +from connection import PersistentConnection, OfficeConnection + +def load_test(name): + module_name, obj_name = name.rsplit(".", 1) + module = importlib.import_module(module_name) + obj = getattr(module, obj_name) + return obj + +def generic_test(opts, test_name): + connection = PersistentConnection(opts) + connection.setUp() + xContext = connection.getContext() + func = load_test(test_name) + func(xContext) + connection.tearDown() + +def parseArgs(argv): + (optlist,args) = getopt.getopt(argv[1:], "hr", + ["help", "soffice=", "userdir=", "calc-demo", "file="]) + return (dict(optlist), args) + +def usage(): + message = """usage: {program} [option]... [task_file]..." + -h | --help: print usage information + {connection_params} + the 'task_file' parameters should be + full absolute pathnames, not URLs.""" + print(message.format(program = os.path.basename(sys.argv[0]), \ + connection_params = OfficeConnection.getHelpText())) + +if __name__ == "__main__": + (opts,args) = parseArgs(sys.argv) + if "-h" in opts or "--help" in opts: + usage() + sys.exit() + elif not "--soffice" in opts: + usage() + sys.exit(1) + elif "--file" in opts: + file_name = opts["--file"] + with open(file_name) as f: + lines = f.readlines() + for line in lines: + line = line.strip() + generic_test(opts, line) + + elif "--calc-demo" in opts: + generic_test(opts, "calc_tests.about_test.test_about_dlg_with_screenshot") + generic_test(opts, "calc_tests.create_range_name.create_range_name") + else: + usage() + sys.exit(1) + +# vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/uitest/screenshot.py b/uitest/screenshot.py new file mode 100755 index 000000000000..af3cd44b4992 --- /dev/null +++ b/uitest/screenshot.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +import sys + +import pyscreenshot as ImageGrab + +if __name__ == "__main__": + # part of the screen + if len(sys.argv) != 5: + sys.exit(1) + im=ImageGrab.grab(bbox=(int(sys.argv[1]),int(sys.argv[2]),int(sys.argv[3]),int(sys.argv[4]))) # X1,Y1,X2,Y2 + im.show() diff --git a/uitest/uitest_helper.py b/uitest/uitest_helper.py new file mode 100644 index 000000000000..3f46618e6aa6 --- /dev/null +++ b/uitest/uitest_helper.py @@ -0,0 +1,77 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-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 time + +from helper import EventListener + +class UITest(object): + + def __init__(self, xUITest, xContext): + self._xUITest = xUITest + self._xContext = xContext + + def execute_dialog_through_command(self, command): + with EventListener(self._xContext, "DialogExecute") as event: + self._xUITest.executeCommand(command) + time_ = 0 + while time_ < 30: + if event.executed: + time.sleep(1) + return + time_ += 1 + time.sleep(1) + + # report a failure here + print("failure execute modal dialog") + + def execute_modeless_dialog_through_command(self, command): + with EventListener(self._xContext, "ModelessDialogVisible") as event: + self._xUITest.executeCommand(command) + time_ = 0 + while time_ < 30: + if event.executed: + time.sleep(1) + return + time_ += 1 + time.sleep(1) + + # report a failure here + print("failure execute modeless dialog") + + def create_doc_in_start_center(self, app): + xStartCenter = self._xUITest.getTopFocusWindow() + xBtn = xStartCenter.getChild(app + "_all") + with EventListener(self._xContext, "OnNew") as event: + xBtn.executeAction("CLICK", tuple()) + time_ = 0 + while time_ < 30: + if event.executed: + return + time_ += 1 + time.sleep(1) + + print("failure doc in start center") + + # report a failure here + + def close_doc(self): + # also need to handle "OnViewClosed" event + with EventListener(self._xContext, "DialogExecute") as event: + self._xUITest.executeCommand(".uno.CloseDoc") + time_ = 0 + while time_ < 30: + if event.executed: + xCloseDlg = self._xUITest.getTopFocusWindow() + xNoBtn = xCloseDlg.getChild("discard") + xNoBtn.executeAction("CLICK", tuple()) + return + + time_ += 1 + time.sleep(1) + +# vim:set shiftwidth=4 softtabstop=4 expandtab: */ |