forked from qt-creator/qt-creator
Squish: Added test for checking locals
Change-Id: I8c676982d335c772a5911cab23a52acb2a094ed1 Reviewed-by: Robert Loehning <robert.loehning@digia.com>
This commit is contained in:
@@ -43,6 +43,7 @@
|
||||
:DebugModeWidget.Breakpoints_QDockWidget {container=':Qt Creator.DebugModeWidget_QSplitter' name='Debugger.Docks.Break' type='QDockWidget' visible='1' windowTitle='Breakpoints'}
|
||||
:DebugModeWidget.Debugger Log_QDockWidget {container=':Qt Creator.DebugModeWidget_QSplitter' name='Debugger.Docks.Output' type='QDockWidget' visible='1' windowTitle='Debugger Log'}
|
||||
:DebugModeWidget.Debugger Toolbar_QDockWidget {container=':Qt Creator.DebugModeWidget_QSplitter' name='Debugger Toolbar' type='QDockWidget' visible='1' windowTitle='Debugger Toolbar'}
|
||||
:DebugModeWidget.Locals and Expressions_QDockWidget {container=':Qt Creator.DebugModeWidget_QSplitter' name='Debugger.Docks.LocalsAndWatchers' type='QDockWidget' visible='1' windowTitle='Locals and Expressions'}
|
||||
:DebugModeWidget.OK_QPushButton {container=':Qt Creator.DebugModeWidget_QSplitter' text='OK' type='QPushButton' unnamed='1' visible='1'}
|
||||
:DebugModeWidget_QComboBox {container=':Qt Creator.DebugModeWidget_QSplitter' occurrence='2' type='QComboBox' unnamed='1' visible='1'}
|
||||
:Debugger Toolbar.Continue_QToolButton {container=':DebugModeWidget.Debugger Toolbar_QDockWidget' text='Continue' type='QToolButton' unnamed='1' visible='1'}
|
||||
@@ -69,6 +70,7 @@
|
||||
:JavaScript.QmlProfilerV8ProfileTable_QmlProfiler::Internal::QmlProfilerEventsMainView {container=':*Qt Creator.JavaScript_QDockWidget' name='QmlProfilerV8ProfileTable' type='QmlProfiler::Internal::QmlProfilerEventsMainView' visible='1'}
|
||||
:Kits_Or_Compilers_QTreeView {container=':qt_tabwidget_stackedwidget_QWidget' type='QTreeView' unnamed='1' visible='1'}
|
||||
:Kits_QtVersion_QComboBox {container=':qt_tabwidget_stackedwidget_QWidget' occurrence='4' type='QComboBox' unnamed='1' visible='1'}
|
||||
:Locals and Expressions_Debugger::Internal::WatchTreeView {container=':DebugModeWidget.Locals and Expressions_QDockWidget' name='WatchWindow' type='Debugger::Internal::WatchTreeView' visible='1' windowTitle='Locals and Expressions'}
|
||||
:New.frame_QFrame {name='frame' type='QFrame' visible='1' window=':New_Core::Internal::NewDialog'}
|
||||
:New.templateCategoryView_QTreeView {name='templateCategoryView' type='QTreeView' visible='1' window=':New_Core::Internal::NewDialog'}
|
||||
:New_Core::Internal::NewDialog {name='Core__Internal__NewDialog' type='Core::Internal::NewDialog' visible='1' windowTitle='New'}
|
||||
|
||||
@@ -242,6 +242,18 @@ def __getTargetFromToolTip__(toolTip):
|
||||
return None
|
||||
return target.group(1).split("<br/>")[0].strip()
|
||||
|
||||
def getExecutableAndTargetFromToolTip(toolTip):
|
||||
target = __getTargetFromToolTip__(toolTip)
|
||||
if toolTip == None or not isinstance(toolTip, (str, unicode)):
|
||||
return None, target
|
||||
pattern = re.compile('.*<b>Run:</b>(.*)</.*')
|
||||
exe = pattern.match(toolTip)
|
||||
if exe == None:
|
||||
test.fatal("UI seems to have changed - expected ToolTip does not match.",
|
||||
"ToolTip: '%s'" % toolTip)
|
||||
return None, target
|
||||
return exe.group(1).strip(), target
|
||||
|
||||
def __getMkspecFromQMakeCall__(qmakeCall):
|
||||
qCall = qmakeCall.split("</b>")[1].strip()
|
||||
tmp = qCall.split()
|
||||
|
||||
@@ -667,3 +667,12 @@ def clickOnTab(tabBarStr, tabText, timeout=5000):
|
||||
test.log("Using workaround for Mac.")
|
||||
setWindowState(tabBar, WindowState.Normal)
|
||||
clickTab(waitForObject(tabBarStr, timeout), tabText)
|
||||
|
||||
# constructs a string holding the properties for a QModelIndex
|
||||
# param property a string holding additional properties including their values
|
||||
# ATTENTION! use single quotes for values (e.g. "text='Text'", "text='Text' occurrence='2'")
|
||||
# param container the container (str) to be used for this QModelIndex
|
||||
def getQModelIndexStr(property, container):
|
||||
if (container.startswith(":")):
|
||||
container = "'%s'" % container
|
||||
return ("{column='0' container=%s %s type='QModelIndex'}" % (container, property))
|
||||
|
||||
@@ -44,12 +44,6 @@ def invokeContextMenuItemOnBookmarkFolder(view, item, menuItem):
|
||||
activateItem(waitForObject("{aboveWidget=%s type='QMenu' unnamed='1' visible='1' "
|
||||
"window=':Add Bookmark_BookmarkDialog'}" % aboveWidget), menuItem)
|
||||
|
||||
def getQModelIndexStr(textProperty, container):
|
||||
if (container.startswith(":")):
|
||||
container = "'%s'" % container
|
||||
return ("{column='0' container=%s %s type='QModelIndex'}"
|
||||
% (container, textProperty))
|
||||
|
||||
def main():
|
||||
startApplication("qtcreator" + SettingsPath)
|
||||
if not startedWithoutPluginError():
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.0
|
||||
|
||||
Rectangle {
|
||||
width: 360
|
||||
height: 360
|
||||
Rectangle {
|
||||
width: 100; height: 100
|
||||
anchors.centerIn: parent
|
||||
color: "red"
|
||||
}
|
||||
Rectangle {
|
||||
width: 50; height: 50
|
||||
anchors.centerIn: parent
|
||||
color: "green"
|
||||
}
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "Check"
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import QmlProject 1.1
|
||||
|
||||
Project {
|
||||
mainFile: "simpleQuickUI2.qml"
|
||||
|
||||
QmlFiles {
|
||||
directory: "."
|
||||
}
|
||||
JavaScriptFiles {
|
||||
directory: "."
|
||||
}
|
||||
ImageFiles {
|
||||
directory: "."
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,6 @@ HOOK_SUB_PROCESSES=false
|
||||
IMPLICITAUTSTART=0
|
||||
LANGUAGE=Python
|
||||
OBJECTMAP=../objects.map
|
||||
TEST_CASES=tst_build_new_project tst_cli_output_console tst_simple_analyze tst_simple_debug
|
||||
TEST_CASES=tst_build_new_project tst_cli_output_console tst_qml_locals tst_simple_analyze tst_simple_debug
|
||||
VERSION=2
|
||||
WRAPPERS=Qt
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
#############################################################################
|
||||
##
|
||||
## Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
## Contact: http://www.qt-project.org/legal
|
||||
##
|
||||
## This file is part of Qt Creator.
|
||||
##
|
||||
## Commercial License Usage
|
||||
## Licensees holding valid commercial Qt licenses may use this file in
|
||||
## accordance with the commercial license agreement provided with the
|
||||
## Software or, alternatively, in accordance with the terms contained in
|
||||
## a written agreement between you and Digia. For licensing terms and
|
||||
## conditions see http://qt.digia.com/licensing. For further information
|
||||
## use the contact form at http://qt.digia.com/contact-us.
|
||||
##
|
||||
## GNU Lesser General Public License Usage
|
||||
## Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
## General Public License version 2.1 as published by the Free Software
|
||||
## Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
## packaging of this file. Please review the following information to
|
||||
## ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
##
|
||||
## In addition, as a special exception, Digia gives you certain additional
|
||||
## rights. These rights are described in the Digia Qt LGPL Exception
|
||||
## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
##
|
||||
#############################################################################
|
||||
|
||||
import os
|
||||
|
||||
# Helper class to create a tree structure
|
||||
class Tree:
|
||||
def __init__(self, name=None, value=None, children=None):
|
||||
self.__name__ = name
|
||||
self.__value__ = value
|
||||
self.__children__ = children
|
||||
|
||||
# getter functions
|
||||
def getChild(self, name, occurrence=1):
|
||||
if self.__children__:
|
||||
for ch in self.__children__:
|
||||
if ch.__name__ == name:
|
||||
if occurrence == 1:
|
||||
return ch
|
||||
occurrence -= 1
|
||||
return None
|
||||
|
||||
def getChildren(self):
|
||||
return self.__children__
|
||||
|
||||
def getName(self):
|
||||
return self.__name__
|
||||
|
||||
def getValue(self):
|
||||
return self.__value__
|
||||
|
||||
# setter function
|
||||
def setChildren(self, children):
|
||||
self.__children__ = children
|
||||
|
||||
def setName(self, name):
|
||||
self.__name__ = name
|
||||
|
||||
def setValue(self, value):
|
||||
self.__value__ = value
|
||||
|
||||
# other functions
|
||||
def addChild(self, child):
|
||||
if self.__children__ == None:
|
||||
self.__children__ = []
|
||||
self.__children__.append(child)
|
||||
|
||||
def childrenCount(self):
|
||||
if self.__children__:
|
||||
return len(self.__children__)
|
||||
return 0
|
||||
|
||||
def countChildOccurrences(self, name):
|
||||
if not self.__children__:
|
||||
return 0
|
||||
return len(filter(lambda x: x.getName() == name, self.__children__))
|
||||
|
||||
# internal functions
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
def __strIndented__(self, indent):
|
||||
result = "%s%s (%s)" % (" " * indent, self.__name__, self.__value__)
|
||||
if self.__children__:
|
||||
for ch in self.__children__:
|
||||
if isinstance(ch, Tree):
|
||||
result += os.linesep + ch.__strIndented__(indent + 2)
|
||||
else:
|
||||
result += os.linesep + " " * (indent + 2) + str(ch)
|
||||
return result
|
||||
|
||||
def __str__(self):
|
||||
return self.__strIndented__(1)
|
||||
@@ -0,0 +1,164 @@
|
||||
#############################################################################
|
||||
##
|
||||
## Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
## Contact: http://www.qt-project.org/legal
|
||||
##
|
||||
## This file is part of Qt Creator.
|
||||
##
|
||||
## Commercial License Usage
|
||||
## Licensees holding valid commercial Qt licenses may use this file in
|
||||
## accordance with the commercial license agreement provided with the
|
||||
## Software or, alternatively, in accordance with the terms contained in
|
||||
## a written agreement between you and Digia. For licensing terms and
|
||||
## conditions see http://qt.digia.com/licensing. For further information
|
||||
## use the contact form at http://qt.digia.com/contact-us.
|
||||
##
|
||||
## GNU Lesser General Public License Usage
|
||||
## Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
## General Public License version 2.1 as published by the Free Software
|
||||
## Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
## packaging of this file. Please review the following information to
|
||||
## ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
##
|
||||
## In addition, as a special exception, Digia gives you certain additional
|
||||
## rights. These rights are described in the Digia Qt LGPL Exception
|
||||
## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
##
|
||||
#############################################################################
|
||||
|
||||
source("../../shared/qtcreator.py")
|
||||
source("Tree.py")
|
||||
|
||||
def main():
|
||||
projName = "simpleQuickUI2.qmlproject"
|
||||
projFolder = os.path.dirname(findFile("testdata", "simpleQuickUI2/%s" % projName))
|
||||
if not neededFilePresent(os.path.join(projFolder, projName)):
|
||||
return
|
||||
qmlProjDir = prepareTemplate(projFolder)
|
||||
if qmlProjDir == None:
|
||||
test.fatal("Could not prepare test files - leaving test")
|
||||
return
|
||||
qmlProjFile = os.path.join(qmlProjDir, projName)
|
||||
# start Creator by passing a .qmlproject file
|
||||
startApplication('qtcreator' + SettingsPath + ' "%s"' % qmlProjFile)
|
||||
if not startedWithoutPluginError():
|
||||
return
|
||||
fancyConfButton = findObject(":*Qt Creator_Core::Internal::FancyToolButton")
|
||||
fancyRunButton = findObject(":*Qt Creator.Run_Core::Internal::FancyToolButton")
|
||||
fancyDebugButton = findObject(":*Qt Creator.Start Debugging_Core::Internal::FancyToolButton")
|
||||
exe, target = getExecutableAndTargetFromToolTip(str(waitForObject(fancyConfButton).toolTip))
|
||||
if not (test.verify(fancyRunButton.enabled and fancyDebugButton.enabled,
|
||||
"Verifying Run and Debug are enabled (Qt5 is available).")
|
||||
and test.compare(target, Targets.getStringForTarget(Targets.DESKTOP_501_DEFAULT),
|
||||
"Verifying selected Target is Qt5.")
|
||||
and test.compare(exe, "QML Scene", "Verifying selected executable is QML Scene.")):
|
||||
earlyExit("Something went wrong opening Qml project - probably missing Qt5.")
|
||||
return
|
||||
switchViewTo(ViewConstants.PROJECTS)
|
||||
switchToBuildOrRunSettingsFor(1, 0, ProjectSettings.RUN, True)
|
||||
ensureChecked("{container=':Qt Creator.scrollArea_QScrollArea' text='Enable QML' "
|
||||
"type='QCheckBox' unnamed='1' visible='1'}")
|
||||
switchViewTo(ViewConstants.EDIT)
|
||||
if platform.system() in ('Microsoft', 'Windows'):
|
||||
qmake = getQtInformationForQmlProject()[3]
|
||||
if qmake == None:
|
||||
earlyExit("Could not figure out which qmake is used.")
|
||||
return
|
||||
qmlScenePath = os.path.abspath(os.path.dirname(qmake))
|
||||
qmlScene = "qmlscene.exe"
|
||||
allowAppThroughWinFW(qmlScenePath, qmlScene, None)
|
||||
clickButton(fancyDebugButton)
|
||||
locAndExprTV = waitForObject(":Locals and Expressions_Debugger::Internal::WatchTreeView")
|
||||
# Locals and Expressions populates treeview only on demand - so the tree must be expanded
|
||||
__unfoldTree__()
|
||||
items = fetchItems(QModelIndex(), QModelIndex(), locAndExprTV)
|
||||
# reduce items to Locals (invisible object)
|
||||
items = items.getChild("Inspector")
|
||||
if items == None:
|
||||
earlyExit("Could not find expected Inspector tree inside Locals and Expressions.")
|
||||
return
|
||||
# reduce items to outer Rectangle object
|
||||
items = items.getChild("Rectangle")
|
||||
if items == None:
|
||||
earlyExit("Could not find expected Rectangle tree inside Locals and Expressions.")
|
||||
return
|
||||
checkForEmptyRows(items)
|
||||
check = [[None, 0, {"Properties":1, "Rectangle":2, "Text":1}, {"width":"360", "height":"360"}],
|
||||
["Text", 1, {"Properties":1}, {"text":"Check"}],
|
||||
["Rectangle", 1, {"Properties":1}, {"width":"50", "height":"50", "color":"#008000"}],
|
||||
["Rectangle", 2, {"Properties":1}, {"width":"100", "height":"100", "color":"#ff0000"}]
|
||||
]
|
||||
for current in check:
|
||||
if current[0]:
|
||||
subItem = items.getChild(current[0], current[1])
|
||||
else:
|
||||
subItem = items
|
||||
checkForExpectedValues(subItem, current[2], current[3])
|
||||
clickButton(waitForObject(':Debugger Toolbar.Exit Debugger_QToolButton', 5000))
|
||||
if platform.system() in ('Microsoft', 'Windows'):
|
||||
deleteAppFromWinFW(qmlScenePath, qmlScene)
|
||||
invokeMenuItem("File", "Exit")
|
||||
|
||||
def __unfoldTree__():
|
||||
rootIndex = getQModelIndexStr("text='Rectangle'",
|
||||
':Locals and Expressions_Debugger::Internal::WatchTreeView')
|
||||
unfoldQModelIndexIncludingProperties(rootIndex)
|
||||
subItems = ["text='Rectangle' occurrence='2'", "text='Rectangle'", "text='Text'"]
|
||||
for item in subItems:
|
||||
unfoldQModelIndexIncludingProperties(getQModelIndexStr(item, rootIndex))
|
||||
|
||||
def unfoldQModelIndexIncludingProperties(indexStr):
|
||||
doubleClick(waitForObject(indexStr))
|
||||
propIndex = getQModelIndexStr("text='Properties'", indexStr)
|
||||
doubleClick(waitForObject(propIndex))
|
||||
|
||||
def fetchItems(index, valIndex, treeView):
|
||||
tree = Tree()
|
||||
model = treeView.model()
|
||||
if index.isValid():
|
||||
name = str(model.data(index).toString())
|
||||
value = str(model.data(valIndex).toString())
|
||||
tree.setName(name)
|
||||
tree.setValue(value)
|
||||
for row in range(model.rowCount(index)):
|
||||
tree.addChild(fetchItems(model.index(row, 0, index), model.index(row, 1, index), treeView))
|
||||
return tree
|
||||
|
||||
def checkForEmptyRows(items, isRootCheck=True):
|
||||
# check for QTCREATORBUG-9069
|
||||
noEmptyRowsFound = True
|
||||
if items.getName().strip() == "":
|
||||
noEmptyRowsFound = False
|
||||
test.fail('Found empty row inside Locals and Expressions', '%s' % items)
|
||||
if items.childrenCount():
|
||||
for item in items.getChildren():
|
||||
noEmptyRowsFound &= checkForEmptyRows(item, False)
|
||||
if isRootCheck and noEmptyRowsFound:
|
||||
test.passes("No empty rows inside Locals and Expressions found.")
|
||||
return noEmptyRowsFound
|
||||
|
||||
def checkForExpectedValues(items, expectedChildren, expectedProperties):
|
||||
if items == None:
|
||||
test.fatal("Got a None object to inspect")
|
||||
return
|
||||
for subItemName in expectedChildren.keys():
|
||||
test.compare(items.countChildOccurrences(subItemName), expectedChildren[subItemName],
|
||||
"Verify number of children named %s for %s" % (subItemName, items.getName()))
|
||||
properties = items.getChild("Properties")
|
||||
if properties:
|
||||
children = properties.getChildren()
|
||||
for property,value in expectedProperties.iteritems():
|
||||
foundProperty = getProperty(property, children)
|
||||
if foundProperty:
|
||||
test.compare(foundProperty.getValue(), value, "Verifying value for %s" % property)
|
||||
else:
|
||||
test.fail("Could not find property %s for object %s" % (property, items.getName()))
|
||||
else:
|
||||
test.fail("Missing properties for %s" % items.getName())
|
||||
|
||||
def getProperty(property, propertyList):
|
||||
for prop in propertyList:
|
||||
if prop.getName() == property:
|
||||
return prop
|
||||
return None
|
||||
Reference in New Issue
Block a user