Commit 11f7dbda authored by Christian Stenger's avatar Christian Stenger Committed by Bill King

Added new qml test and continue refactoring

Refactoring of all helper functions to make it easier to
recognize them as such. All helper functions in global
shared scripts now follow the scheme __NAME__
Helper functions normally should not get called from outside.

Change-Id: I0d02028d3f9de1ad251af9226c0460655bd9c9bd
Reviewed-on: http://codereview.qt-project.org/5331Reviewed-by: default avatarQt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: default avatarBill King <bill.king@nokia.com>
parent ddacec3e
......@@ -27,3 +27,5 @@
:projects_QModelIndex {column='0' container=':Qt Creator_Utils::NavigationTreeView' text='projects' type='QModelIndex'}
:scrollArea.Create Build Configurations:_QComboBox {container=':Project Setup.scrollArea_QScrollArea' leftWidget=':scrollArea.Create Build Configurations:_QLabel' type='QComboBox' unnamed='1' visible='1'}
:scrollArea.Create Build Configurations:_QLabel {container=':Project Setup.scrollArea_QScrollArea' text='Create Build Configurations:' type='QLabel' unnamed='1' visible='1'}
:Qt Creator.Stop_QToolButton {text='Stop' type='QToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
:Qt Creator_Core::Internal::OutputPaneToggleButton {occurrence='3' type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
......@@ -16,11 +16,11 @@ def overrideInstallLazySignalHandler():
return
overridenInstallLazySignalHandlers = True
global installLazySignalHandler
installLazySignalHandler = addSignalHandlerDict(installLazySignalHandler)
installLazySignalHandler = __addSignalHandlerDict__(installLazySignalHandler)
# avoids adding a handler to a signal twice or more often
# do not call this function directly - use overrideInstallLazySignalHandler() instead
def addSignalHandlerDict(lazySignalHandlerFunction):
def __addSignalHandlerDict__(lazySignalHandlerFunction):
global installedSignalHandlers
def wrappedFunction(name, signalSignature, handlerFunctionName):
handlers = installedSignalHandlers.get("%s____%s" % (name,signalSignature))
......
import re;
# places the cursor inside the given editor into the given line
# (leading and trailing whitespaces are ignored!)
# and goes to the end of the line
# line can be a regex - but if so, remember to set isRegex to True
# the function returns True if this went fine, False on error
def placeCursorToLine(editor,line,isRegex=False):
cursor = editor.textCursor()
oldPosition = 0
cursor.setPosition(oldPosition)
found = False
if isRegex:
regex = re.compile(line)
while not found:
currentLine = str(lineUnderCursor(editor)).strip()
if isRegex:
if regex.match(currentLine):
found = True
else:
type(editor, "<Down>")
if oldPosition==editor.textCursor().position():
break
oldPosition = editor.textCursor().position()
else:
if currentLine==line:
found = True
else:
type(editor, "<Down>")
if oldPosition==editor.textCursor().position():
break
oldPosition = editor.textCursor().position()
if not found:
test.fatal("Couldn't find line matching\n\n%s\n\nLeaving test..." % line)
return False
cursor=editor.textCursor()
cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.MoveAnchor)
editor.setTextCursor(cursor)
return True
# this function simply opens the context menu inside the given editor
# at the same position where the text cursor is located at
def openContextMenuOnTextCursorPosition(editor):
rect = editor.cursorRect(editor.textCursor())
openContextMenu(editor, rect.x+rect.width/2, rect.y+rect.height/2, 0)
# this function marks/selects the text inside the given editor from position
# startPosition to endPosition (both inclusive)
def markText(editor, startPosition, endPosition):
cursor = editor.textCursor()
cursor.setPosition(startPosition)
cursor.movePosition(QTextCursor.StartOfLine)
editor.setTextCursor(cursor)
cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, endPosition-startPosition)
cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
cursor.setPosition(endPosition, QTextCursor.KeepAnchor)
editor.setTextCursor(cursor)
......@@ -4,6 +4,9 @@ import os;
import glob;
import atexit;
import codecs;
import subprocess;
import errno;
from datetime import datetime,timedelta;
SDKPath = ''
SettingsPath = ''
......@@ -14,10 +17,36 @@ source("../../shared/utils.py")
source("../../shared/build_utils.py")
source("../../shared/mainwin.py")
source("../../shared/qtquick.py")
source("../../shared/editor_utils.py")
def removeTmpSettingsDir():
def waitForCleanShutdown(timeOut=10):
appCtxt = currentApplicationContext()
waitFor("appCtxt.isRunning==False")
shutdownDone = False
if platform.system() in ('Windows','Microsoft'):
endtime = datetime.utcnow() + timedelta(seconds=timeOut)
while not shutdownDone:
# following work-around because os.kill() works for win not until python 2.7
tasks = subprocess.Popen("tasklist /FI \"PID eq %d\"" % appCtxt.pid, shell=True,stdout=subprocess.PIPE)
output = tasks.communicate()[0]
tasks.stdout.close()
if (output=="INFO: No tasks are running which match the specified criteria."
or output=="" or output.find("ERROR")==0):
shutdownDone=True
if not shutdownDone and datetime.utcnow() > endtime:
break
else:
endtime = datetime.utcnow() + timedelta(seconds=timeOut)
while not shutdownDone:
try:
os.kill(appCtxt.pid,0)
except OSError, err:
if err.errno == errno.EPERM or err.errno == errno.ESRCH:
shutdownDone=True
if not shutdownDone and datetime.utcnow() > endtime:
break
def __removeTmpSettingsDir__():
waitForCleanShutdown()
deleteDirIfExists(os.path.dirname(tmpSettingsDir))
if platform.system() in ('Windows', 'Microsoft'):
......@@ -34,6 +63,6 @@ tmpSettingsDir = tempDir()
tmpSettingsDir = os.path.abspath(tmpSettingsDir+"/settings")
shutil.copytree(cwd, tmpSettingsDir)
# the following only doesn't work if the test ends in an exception
atexit.register(removeTmpSettingsDir)
atexit.register(__removeTmpSettingsDir__)
SettingsPath = " -settingspath %s" % tmpSettingsDir
......@@ -44,11 +44,11 @@ class QtQuickConstants:
else:
return None
def handleProcessStarted(object):
def __handleProcessStarted__(object):
global processStarted
processStarted = True
def handleProcessExited(object, exitCode):
def __handleProcessExited__(object, exitCode):
global processExited
processExited = True
......@@ -87,8 +87,8 @@ def chooseDestination(destination=QtQuickConstants.Destinations.DESKTOP):
def runAndCloseApp():
global processStarted, processExited
processStarted = processExited = False
installLazySignalHandler("{type='ProjectExplorer::ApplicationLaucher'}", "processStarted()", "handleProcessStarted")
installLazySignalHandler("{type='ProjectExplorer::ApplicationLaucher'}", "processExited(int)", "handleProcessExited")
installLazySignalHandler("{type='ProjectExplorer::ApplicationLaucher'}", "processStarted()", "__handleProcessStarted__")
installLazySignalHandler("{type='ProjectExplorer::ApplicationLaucher'}", "processExited(int)", "__handleProcessExited__")
runButton = waitForObject("{type='Core::Internal::FancyToolButton' text='Run' visible='1'}", 20000)
clickButton(runButton)
waitForSignal("{type='ProjectExplorer::BuildManager' unnamed='1'}", "buildQueueFinished(bool)", 300000)
......@@ -103,24 +103,22 @@ def runAndCloseApp():
invokeMenuItem("File", "Exit")
return False
# the following is currently a work-around for not using hooking into subprocesses
setWindowState(waitForObject(":Qt Creator_Core::Internal::MainWindow", 20000), WindowState.Minimize)
nativeType("<Alt+F4>")
waitFor("processExited==True",10000)
setWindowState(waitForObject(":Qt Creator_Core::Internal::MainWindow", 20000), WindowState.Normal)
if (waitForObject(":Qt Creator_Core::Internal::OutputPaneToggleButton").checked!=True):
clickButton(":Qt Creator_Core::Internal::OutputPaneToggleButton")
clickButton(":Qt Creator.Stop_QToolButton")
return True
def runAndCloseQtQuickUI():
global processStarted, processExited
processStarted = processExited = False
installLazySignalHandler("{type='ProjectExplorer::ApplicationLaucher'}", "processStarted()", "handleProcessStarted")
installLazySignalHandler("{type='ProjectExplorer::ApplicationLaucher'}", "processExited(int)", "handleProcessExited")
installLazySignalHandler("{type='ProjectExplorer::ApplicationLaucher'}", "processStarted()", "__handleProcessStarted__")
installLazySignalHandler("{type='ProjectExplorer::ApplicationLaucher'}", "processExited(int)", "__handleProcessExited__")
runButton = waitForObject("{type='Core::Internal::FancyToolButton' text='Run' visible='1'}", 20000)
clickButton(runButton)
waitFor("processStarted==True", 10000)
# the following is currently a work-around for not using hooking into subprocesses
setWindowState(waitForObject(":Qt Creator_Core::Internal::MainWindow", 20000), WindowState.Minimize)
nativeType("<Alt+F4>")
waitFor("processExited==True", 10000)
setWindowState(waitForObject(":Qt Creator_Core::Internal::MainWindow", 20000), WindowState.Normal)
if (waitForObject(":Qt Creator_Core::Internal::OutputPaneToggleButton").checked!=True):
clickButton(":Qt Creator_Core::Internal::OutputPaneToggleButton")
clickButton(":Qt Creator.Stop_QToolButton")
return True
......@@ -72,29 +72,24 @@ def replaceLineEditorContent(lineEditor, newcontent):
signalObjects = {}
def callbackFunction(object, *args):
def __callbackFunction__(object, *args):
global signalObjects
# test.log("callbackFunction: "+objectMap.realName(object))
# test.log("__callbackFunction__: "+objectMap.realName(object))
signalObjects[objectMap.realName(object)] += 1
def waitForSignal(object, signal, timeout=30000):
global signalObjects
realName = prepareForSignal(object, signal)
beforeCount = signalObjects[realName]
waitFor("signalObjects[realName] > beforeCount", timeout)
def prepareForSignal(object, signal):
global signalObjects
overrideInstallLazySignalHandler()
realName = objectMap.realName(object)
# test.log("waitForSignal: "+realName)
if not (realName in signalObjects):
signalObjects[realName] = 0
beforeCount = signalObjects[realName]
installLazySignalHandler(object, signal, "callbackFunction")
waitFor("signalObjects[realName] > beforeCount", timeout)
def markText(editor, startPosition, endPosition):
cursor = editor.textCursor()
cursor.setPosition(startPosition)
cursor.movePosition(QTextCursor.StartOfLine)
editor.setTextCursor(cursor)
cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, endPosition-startPosition)
cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
cursor.setPosition(endPosition, QTextCursor.KeepAnchor)
editor.setTextCursor(cursor)
installLazySignalHandler(object, signal, "__callbackFunction__")
return realName
source("../../shared/qtcreator.py")
refreshFinishedCount = 0
def handleRefreshFinished(object, fileList):
global refreshFinishedCount
refreshFinishedCount += 1
def main():
test.verify(os.path.exists(SDKPath + "/creator/tests/manual/cplusplus-tools/cplusplus-tools.pro"))
startApplication("qtcreator" + SettingsPath)
## leave this one like this, it's too fast for delayed installation of signal handler
installLazySignalHandler("{type='CppTools::Internal::CppModelManager'}", "sourceFilesRefreshed(QStringList)", "handleRefreshFinished")
prepareForSignal("{type='CppTools::Internal::CppModelManager' unnamed='1'}", "sourceFilesRefreshed(QStringList)")
openQmakeProject(SDKPath + "/creator/tests/manual/cplusplus-tools/cplusplus-tools.pro")
waitFor("refreshFinishedCount == 1", 20000)
test.compare(refreshFinishedCount, 1)
waitForSignal("{type='CppTools::Internal::CppModelManager' unnamed='1'}", "sourceFilesRefreshed(QStringList)", 20000)
mouseClick(waitForObject(":*Qt Creator_Utils::FilterLineEdit", 20000), 5, 5, 0, Qt.LeftButton)
type(waitForObject(":*Qt Creator_Utils::FilterLineEdit"), "dummy.cpp")
......
......@@ -7,6 +7,6 @@ HOOK_SUB_PROCESSES=true
IMPLICITAUTSTART=0
LANGUAGE=Python
OBJECTMAP=../objects.map
TEST_CASES=tst_qtquick_creation tst_qtquick_creation2 tst_qtquick_creation3 tst_qtquick_creation4
TEST_CASES=tst_qtquick_creation tst_qtquick_creation2 tst_qtquick_creation3 tst_qtquick_creation4 tst_qml_indent tst_qml_editor
VERSION=2
WRAPPERS=Qt
source("../../shared/qtcreator.py")
workingDir = None
templateDir = None
def main():
global workingDir,templateDir
startApplication("qtcreator" + SettingsPath)
# using a temporary directory won't mess up an eventually exisiting
workingDir = tempDir()
prepareTemplate()
createNewQtQuickApplication()
# wait for parsing to complete
waitForSignal("{type='CppTools::Internal::CppModelManager' unnamed='1'}", "sourceFilesRefreshed(QStringList)", 30000)
testRenameId()
invokeMenuItem("File", "Exit")
def prepareTemplate():
global templateDir
templateDir = tempDir()
templateDir = os.path.abspath(templateDir + "/template")
sourceExample = os.path.abspath(SDKPath + "/../Examples/4.7/declarative/text/textselection")
shutil.copytree(sourceExample, templateDir)
def createNewQtQuickApplication():
global workingDir,templateDir
invokeMenuItem("File", "New File or Project...")
clickItem(waitForObject("{type='QTreeView' name='templateCategoryView'}", 20000),
"Projects.Qt Quick Project", 5, 5, 0, Qt.LeftButton)
clickItem(waitForObject("{name='templatesView' type='QListView'}", 20000),
"Qt Quick Application", 5, 5, 0, Qt.LeftButton)
clickButton(waitForObject("{text='Choose...' type='QPushButton' unnamed='1' visible='1'}", 20000))
baseLineEd = waitForObject("{name='nameLineEdit' visible='1' "
"type='Utils::ProjectNameValidatingLineEdit'}", 20000)
replaceLineEditorContent(baseLineEd, "untitled")
baseLineEd = waitForObject("{type='Utils::BaseValidatingLineEdit' unnamed='1' visible='1'}", 20000)
replaceLineEditorContent(baseLineEd, workingDir)
stateLabel = findObject("{type='QLabel' name='stateLabel'}")
labelCheck = stateLabel.text=="" and stateLabel.styleSheet == ""
test.verify(labelCheck, "Project name and base directory without warning or error")
# make sure this is not set as default location
cbDefaultLocation = waitForObject("{type='QCheckBox' name='projectsDirectoryCheckBox' visible='1'}", 20000)
if cbDefaultLocation.checked:
clickButton(cbDefaultLocation)
# now there's the 'untitled' project inside a temporary directory - step forward...!
nextButton = waitForObject("{text?='Next*' type='QPushButton' visible='1'}", 20000)
clickButton(nextButton)
chooseComponents(QtQuickConstants.Components.EXISTING_QML)
# define the existing qml file to import
baseLineEd = waitForObject("{type='Utils::BaseValidatingLineEdit' unnamed='1' visible='1'}", 20000)
type(baseLineEd, templateDir+"/qml/textselection.qml")
clickButton(nextButton)
chooseDestination()
snooze(1)
clickButton(nextButton)
clickButton(waitForObject("{type='QPushButton' text='Finish' visible='1'}", 20000))
def testRenameId():
test.log("Testing rename of id")
navTree = waitForObject("{type='Utils::NavigationTreeView' unnamed='1' visible='1' "
"window=':Qt Creator_Core::Internal::MainWindow'}", 20000)
model = navTree.model()
treeElement = ("untitled.QML.%s/qml.textselection\\.qml" %
templateDir.replace("\\", "/").replace("_", "\\_"))
waitForObjectItem(navTree, treeElement)
doubleClickItem(navTree, treeElement, 5, 5, 0, Qt.LeftButton)
editor = waitForObject("{type='QmlJSEditor::QmlJSTextEditorWidget' unnamed='1' visible='1' "
"window=':Qt Creator_Core::Internal::MainWindow'}", 20000)
originalText = "%s" % editor.plainText
line = "TextEdit\s*\{"
if not placeCursorToLine(editor, line, True):
test.fatal("File seems to have changed... Canceling current test")
return False
type(editor, "<Down>")
openContextMenuOnTextCursorPosition(editor)
activateItem(waitForObjectItem("{type='QMenu' visible='1' unnamed='1'}", "Rename Symbol Under Cursor"))
type(waitForObject("{leftWidget={text='Replace with:' type='QLabel' unnamed='1' visible='1'} "
"type='Find::Internal::WideEnoughLineEdit' unnamed='1' visible='1' "
"window=':Qt Creator_Core::Internal::MainWindow'}"), "halloballo")
clickButton(waitForObject("{text='Replace' type='QToolButton' unnamed='1' visible='1' "
"window=':Qt Creator_Core::Internal::MainWindow'}"))
modifiedText = "%s" % editor.plainText
originalText = "%s" % (originalText.replace("editor", "__EDITOR__").replace("edit", "halloballo")
.replace("__EDITOR__", "editor"))
test.compare(originalText,modifiedText)
type(editor, "<Ctrl+S>")
def cleanup():
global workingDir, templateDir
waitForCleanShutdown()
if workingDir!=None:
deleteDirIfExists(workingDir)
if templateDir!=None:
deleteDirIfExists(os.path.dirname(templateDir))
......@@ -37,7 +37,7 @@ def createNewQtQuickApplication():
if cbDefaultLocation.checked:
clickButton(cbDefaultLocation)
# now there's the 'untitled' project inside a temporary directory - step forward...!
nextButton = waitForObject("{text='Next' type='QPushButton' visible='1'}", 20000)
nextButton = waitForObject("{text?='Next*' type='QPushButton' visible='1'}", 20000)
clickButton(nextButton)
chooseComponents()
clickButton(nextButton)
......@@ -99,8 +99,7 @@ def testReIndent():
if originalText==textAfterReIndent:
test.passes("Text successfully reindented...")
else:
# remove the 2nd parameter of the following???
# (it's huge output that takes long time to finish & screenshot is taken either!)
# shrink the texts - it's huge output that takes long time to finish & screenshot is taken as well
originalText = shrinkText(originalText, 20)
textAfterReIndent = shrinkText(textAfterReIndent, 20)
test.fail("Re-indent of text unsuccessful...",
......@@ -120,8 +119,7 @@ def shrinkText(txt, lines=10):
def cleanup():
global workingDir
# waiting for a clean exit - for a full-remove of the temp directory
appCtxt = currentApplicationContext()
waitFor("appCtxt.isRunning==False")
waitForCleanShutdown()
if workingDir!=None:
deleteDirIfExists(workingDir)
......@@ -36,7 +36,7 @@ def createNewQtQuickApplication():
if cbDefaultLocation.checked:
clickButton(cbDefaultLocation)
# now there's the 'untitled' project inside a temporary directory - step forward...!
nextButton = waitForObject("{text='Next' type='QPushButton' visible='1'}", 20000)
nextButton = waitForObject("{text?='Next*' type='QPushButton' visible='1'}", 20000)
clickButton(nextButton)
chooseComponents()
clickButton(nextButton)
......@@ -48,8 +48,7 @@ def createNewQtQuickApplication():
def cleanup():
global workingDir
# waiting for a clean exit - for a full-remove of the temp directory
appCtxt = currentApplicationContext()
waitFor("appCtxt.isRunning==False")
waitForCleanShutdown()
if workingDir!=None:
deleteDirIfExists(workingDir)
......@@ -45,7 +45,7 @@ def createNewQtQuickApplication():
if cbDefaultLocation.checked:
clickButton(cbDefaultLocation)
# now there's the 'untitled' project inside a temporary directory - step forward...!
nextButton = waitForObject("{text='Next' type='QPushButton' visible='1'}", 20000)
nextButton = waitForObject("{text?='Next*' type='QPushButton' visible='1'}", 20000)
clickButton(nextButton)
chooseComponents(QtQuickConstants.Components.EXISTING_QML)
# define the existing qml file to import
......@@ -60,8 +60,7 @@ def createNewQtQuickApplication():
def cleanup():
global workingDir,templateDir
# waiting for a clean exit - for a full-remove of the temp directory
appCtxt = currentApplicationContext()
waitFor("appCtxt.isRunning==False")
waitForCleanShutdown()
if workingDir!=None:
deleteDirIfExists(workingDir)
if templateDir!=None:
......
......@@ -29,14 +29,13 @@ def createNewQtQuickUI():
if cbDefaultLocation.checked:
clickButton(cbDefaultLocation)
# now there's the 'untitled' project inside a temporary directory - step forward...!
clickButton(waitForObject("{text='Next' type='QPushButton' visible='1'}", 20000))
clickButton(waitForObject("{text?='Next*' type='QPushButton' visible='1'}", 20000))
clickButton(waitForObject("{type='QPushButton' text='Finish' visible='1'}", 20000))
def cleanup():
global workingDir
# waiting for a clean exit - for a full-remove of the temp directory
appCtxt = currentApplicationContext()
waitFor("appCtxt.isRunning==False")
waitForCleanShutdown()
if workingDir!=None:
deleteDirIfExists(workingDir)
......@@ -33,7 +33,7 @@ def createNewQmlExtension():
if cbDefaultLocation.checked:
clickButton(cbDefaultLocation)
# now there's the 'untitled' project inside a temporary directory - step forward...!
nextButton = waitForObject("{text='Next' type='QPushButton' visible='1'}", 20000)
nextButton = waitForObject("{text?='Next*' type='QPushButton' visible='1'}", 20000)
clickButton(nextButton)
chooseDestination()
clickButton(nextButton)
......@@ -50,8 +50,7 @@ def createNewQmlExtension():
def cleanup():
global workingDir
# waiting for a clean exit - for a full-remove of the temp directory
appCtxt = currentApplicationContext()
waitFor("appCtxt.isRunning==False")
waitForCleanShutdown()
if workingDir!=None:
deleteDirIfExists(workingDir)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment