uitest: move uitest python part from dev-tools

Change-Id: I5a6464cb4e110d1da48b5f525f63e0a7de6dea58
This commit is contained in:
Markus Mohrhard 2016-03-24 00:35:18 +01:00
parent 24d5e156cb
commit 29cd5a2f5e
13 changed files with 642 additions and 0 deletions

0
uitest/__init__.py Normal file
View file

View file

View file

@ -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: */

View file

@ -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: */

147
uitest/connection.py Normal file
View file

@ -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: */

View file

@ -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: */

View file

@ -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: */

47
uitest/demo_ui/edit.py Normal file
View file

@ -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: */

45
uitest/demo_ui/listbox.py Normal file
View file

@ -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: */

93
uitest/helper.py Normal file
View file

@ -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: */

66
uitest/main.py Normal file
View file

@ -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: */

12
uitest/screenshot.py Executable file
View file

@ -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()

77
uitest/uitest_helper.py Normal file
View file

@ -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: */