/usr/share/pyshared/mago/application.py is in mago 0.3+bzr20-0ubuntu3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 | # Copyright (C) 2005-2010 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
""" Test applcation object
It implements the application under test
"""
from sys import exit
try:
import ldtp, ooldtp
except ImportError:
print "ldtp is required. Install the package python-ldtp.\nExiting..."
exit(1)
import mago.mouse
import mago.xlib
from os import kill
from time import sleep
import re
class TestApplication(object):
""" TestApplication class
This class present an absctraction layer to manage the application under
test
"""
appModule = None
appClass = None
isRunning = False
context = None # LDTP Context
pid = -1
xid = 0
window = None
window_manager = None
def __init__(self, launcher = None, launcher_args = [], window_name = None):
""" Construct a TestApplication instance
:param launcher: Name of the binary to launch the application. If this
argument is not specificied, the application is not
launched during init and must be launched with the
method 'launch'.
:param launcher_args: Optional arguments to launch the application
:param window_name: Name of the main window of the application. The
name respect the LDTP naming convention
"""
self.launcher = launcher
self.launcher_args = launcher_args
self.window_name = window_name
self.window_manager = mago.xlib.WM
self.isRunning = False
self.dlgAboutName = None
if launcher:
self.launch()
def launch(self, launcher = None, launcher_args = [], window_name = None):
"""Launch the application
:param launcher: Name of the binary to launch the application. If this
argument is not specificied.
:param launcher_args: Optional arguments to launch the application
:param window_name: Name of the main window of the application. The
name respect the LDTP naming convention
"""
if self.isRunning:
return self.pid
# Sync args and class properties with a priority for method args
if not launcher: launcher = self.launcher
if not launcher_args: launcher_args = self.launcher_args
if not window_name: window_name = self.window_name
self.launcher = launcher
self.launcher_args = launcher_args
self.window_name = window_name
self.pid = ldtp.launchapp(launcher, launcher_args)
self.window = mago.xlib.window_from_pid(self.pid, launcher)
if not window_name:
window_name = self.get_windowname( discover = True )
# Raise LdtpExecutionError if not found
self.window_name = window_name
ldtp.waittillguiexist(window_name)
self.context = ooldtp.context(window_name)
# Get XID, WindowManager and Application Window object
self.isRunning = True
return self.pid
def close(self, timeout = 30):
"""Closes the application
# If the pid still exists:
# 1. Close the app using ldtp
# 2. If the window is not closed close it using SIGTERM
# 3. If the window is still not closed close it with SIGKILL
:param timout: Kill the application after timeout (in seconds)"""
# Force sync to avoid closing the app before its really there
# But this may cause problem if another window exists with the
# same name
# Pb if the window has already been closed
# TODO
# - Check if window still exists and kill it if it is
# - Add a waiter and kill the process after a timeout
try:
kill(self.pid, 0)
#print "closing with ldtp"
rc = ldtp.closewindow(self.window_name)
if rc == 1:
rc = ldtp.waittillguinotexist(self.window_name, guiTimeOut = timeout)
if rc == 0:
#print "closing with SIGTERM"
kill(self.pid, 15)
rc = ldtp.waittillguinotexist(self.window_name, guiTimeOut = 5)
if rc == 0:
#print "closing with SIGKILL"
kill(self.pid, 9)
rc = ldtp.waittillguinotexist(self.window_name, guiTimeOut = 5)
except OSError:
# We are here if the process is not there anymore
pass
self.pid = -1
self.isRunning = False
# Lets wait a few seconds to be sure that the WM has time to run all
# the pending events
ldtp.wait(5)
def __del__(self):
"""Destructor
Closes the application
"""
if self.isRunning:
self.close()
# def __guess_window_name(self):
# """Tries to guess the name of the main window of the application
#
# It does this by checking the last window opened in the window list and
# returns the name of the window following LDTP naming conventions.
#
# This neeeds to be called immediatly after the ldtp.launchapp() to avoid
# catching another window that would be opened during the test
# """
# winlist=ldtp.getwindowlist()
# timeout=60
#
# # Any way to get the window name from the app name ?
# # appundertest is not implemented in LDTPv2
# wincount=len(winlist)
# wincount_orig = wincount
# while wincount == wincount_orig and timeout > 0:
# winlist = ldtp.getwindowlist()
# wincount = len(winlist)
# timeout -= 1
# sleep(1)
#
# return(winlist[-1])
#
#
# return False
def get_windowname(self, discover = False):
"""Returns the name of the window for the current running application
following the LDTP convention.
It gets it from the _WM_ICON_NAME Atom for the XID of the main window
:param discover: if True or window_name is not set already, it forces
the discovery of the window using X Atoms. Otherwise
it returns the window_name
"""
if not self.window:
return False
if not discover and self.window_name:
return self.window_name
title = re.sub('\s', '', self.window.name)
wm_type = self.window.type
type = "*"
if wm_type[0] == self.window.TYPE_NORMAL:
type = 'frm'
elif wm_type[0] == self.window.TYPE_DIALOG:
type = 'dlg'
ldtpid= type + title
return ldtpid
def openDocument(self, path, actionOpen = "mnuOpen*",
dlgOpen = "dlgOpenDocument", btnOk = "btnOpen",
defaultTimeout = 2, opts = {}):
""" Helper method to manage the open dialog.
All the names in argument follow the LDTP naming convention
:param parentWindow: Name of the main window
:param path: Path to the document
:param actionOpen: Name of the widget to open the Open dialog
:param dlgOpen: Name of the Open dialog
:param btnOk: Name of the button to open the document
:param defaultTimeout: Timeout between actions
:param opts: Additional arguments to provide to the dialog. This is a
dict of the form {'componenent_name':'value'}
"""
txtLocation = 'txtLocation'
if not ldtp.guiexist(self.window_name, actionOpen):
return False
ldtp.click(self.window_name, actionOpen)
ldtp.wait(defaultTimeout)
if not ldtp.guiexist(dlgOpen):
return False
if not ldtp.guiexist(dlgOpen, txtLocation):
ldtp.generatekeyevent('<ctrl>l')
ldtp.settextvalue(dlgOpen, txtLocation, path)
ldtp.wait(defaultTimeout)
ldtp.click(dlgOpen,btnOk)
ldtp.wait(defaultTimeout)
return True
def saveDocument(self, path, actionSave = "mnuSaveAs*",
dlgSave = "dlgSave*", btnOk = "btnSave",
defaultTimeout = 2, opts = {}, replace = True):
""" Helper method to manage the save dialog.
All the names in argument follow the LDTP naming convention
:param parentWindow: Name of the main window
:param path: Path to the document
:param actionOpen: Name of the widget to open the Save dialog
:param dlgSave: Name of the Save dialog
:param btnOk: Name of the button to validate the action
:param defaultTimeout: Timeout between actions
:param opts: Additional arguments to provide to the dialog. This is a
dict of the form {'componenent_name':'value'}
:param replace: Set to true (default) to overwrite an existing file
"""
# TODO: Put all of this in a resource file
txtLocation = 'txtName'
# Confirmation dialog
dlgQuestion = 'dlgQuestion'
btnCancel = 'btnCancel'
btnReplace = 'btnReplace'
if not ldtp.guiexist(self.window_name, actionSave):
return False
ldtp.click(self.window_name, actionSave)
ldtp.wait(defaultTimeout)
if not ldtp.guiexist(dlgSave):
return False
if not ldtp.guiexist(dlgSave, txtLocation):
ldtp.generatekeyevent('<ctrl>l')
ldtp.settextvalue(dlgSave, txtLocation, path)
ldtp.wait(defaultTimeout)
ldtp.click(dlgSave, btnOk)
ldtp.wait(defaultTimeout)
if ldtp.guiexist(dlgQuestion):
ldtp.click(dlgQuestion, btnReplace if replace else btnCancel)
ldtp.wait(defaultTimeout)
return True
def authenticate(self, password = "", cancel = False):
"""Manages the authentication dialog
:param password: User password. Defaults to an empty string
:param cancel: If set to True, it cancels the dialog
TODO:
- Manage it automatically with an onwindowcreate event
"""
dlgName = "dlgAuthenticate"
btnCancel = "btnCancel"
btnOk = "btnAuthenticate"
txtPassword = 'txtPassword'
lblFailed = 'lbl*unsuccessful*'
# Wait for the dialog to appear
for i in range(0, 2):
if not ldtp.guiexist(dlgName):
if i == 1:
return False
ldtp.waittillguiexist(dlgName)
ldtp.wait(1)
ldtp.settextvalue(dlgName, txtPassword, password)
ldtp.click(dlgName, btnCancel if cancel else btnOk)
# The dialog takes some time to respond when you enter a wrong password
ldtp.wait(5)
# Wrong password ?
if ldtp.guiexist(dlgName, lblFailed):
return False
return True
def about_open(self, cmdOpen = None, dlgAboutName = None):
"""Opens the about dialog
:param cmdOpen: Name of the component to open the about dialog. If None
it tries to guess it and returns false if it can't
:param dlgAboutName: Name of the about dialog. Try to guess it if None
"""
if not cmdOpen or not self.context.guiexist(cmdOpen):
cmds = filter(lambda x: "about" in x.lower(),
self.context.getobjectlist())
if len(cmds) == 0:
return False
elif len(cmds) == 1:
cmdOpen = cmds[0]
else:
# Check the properties and select the first menu which matches
for c in cmds:
component_class = self.context.getobjectproperty(c, 'class')
if component_class == 'menu_item':
cmdOpen = c
break
self.context.click(cmdOpen)
self.context.waittillguiexist()
ldtp.wait(1)
# Find the name of the dialog
if not dlgAboutName:
try:
dlgAboutName = filter(lambda x: "dlgabout" in x.lower(),
ldtp.getwindowlist())[-1]
except IndexError:
# Cannot find the name
return False
self.dlgAboutName = dlgAboutName
return ldtp.guiexist(self.dlgAboutName)
def about_close(self, cmdClose = "btnClose", dlgAboutName = None):
"""Opens the about dialog
:param cmdClose: Name of the component to close the about dialog. If
None it tries to guess it and returns false if it can't
:param dlgAboutName: Name of the about dialog. If None it uses the name
found in about_open
"""
if not (dlgAboutName or self.dlgAboutName):
return False
if not dlgAboutName:
dlgAboutName = self.dlgAboutName
# About not found or already closed ?
# State is undefined return False by default
if not ldtp.guiexist(dlgAboutName):
return False
# Try to find the close button name
if not cmdClose:
cmds = filter(lambda x: "close" in x.lower(),
ldtp.getobjectlist(dlgAboutName))
if len(cmds) == 0:
return False
elif len(cmds) == 1:
cmdClose = cmds[0]
else:
# Check the properties and select the first button which matches
for c in cmds:
component_class = ldtp.getobjectproperty(dlgAboutName, c, 'class')
if component_class == 'push_button':
cmdClose = c
break
ldtp.click(dlgAboutName, cmdClose)
ldtp.waittillguinotexist(dlgAboutName)
ldtp.wait(1)
self.dlgAboutName = None
return not ldtp.guiexist(dlgAboutName)
|