forked from qt-creator/qt-creator
		
	Change-Id: Ib5423fdd064e4546f848c0b640b0ed0514c26d3a Reviewed-by: Leena Miettinen <riitta-leena.miettinen@digia.com> Reviewed-by: Kai Koehne <kai.koehne@digia.com>
		
			
				
	
	
		
			261 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#############################################################################
 | 
						|
##
 | 
						|
## Copyright (C) 2014 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 re;
 | 
						|
 | 
						|
# dictionary to hold a list of all installed handler functions for all object-signalSignature pairs
 | 
						|
installedSignalHandlers = {}
 | 
						|
# flag to indicate whether overrideInstallLazySignalHandler() has been called already
 | 
						|
overridenInstallLazySignalHandlers = False
 | 
						|
# flag to indicate whether a tasks file should be created when building ends with errors
 | 
						|
createTasksFileOnError = True
 | 
						|
 | 
						|
# call this function to override installLazySignalHandler()
 | 
						|
def overrideInstallLazySignalHandler():
 | 
						|
    global overridenInstallLazySignalHandlers
 | 
						|
    if overridenInstallLazySignalHandlers:
 | 
						|
        return
 | 
						|
    overridenInstallLazySignalHandlers = True
 | 
						|
    global 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):
 | 
						|
    global installedSignalHandlers
 | 
						|
    def wrappedFunction(name, signalSignature, handlerFunctionName):
 | 
						|
        handlers = installedSignalHandlers.get("%s____%s" % (name,signalSignature))
 | 
						|
        if handlers == None:
 | 
						|
            lazySignalHandlerFunction(name, signalSignature, handlerFunctionName)
 | 
						|
            installedSignalHandlers.setdefault("%s____%s" % (name,signalSignature), [handlerFunctionName])
 | 
						|
        else:
 | 
						|
            if not handlerFunctionName in handlers:
 | 
						|
                lazySignalHandlerFunction(name, signalSignature, handlerFunctionName)
 | 
						|
                handlers.append(handlerFunctionName)
 | 
						|
                installedSignalHandlers.setdefault("%s____%s" % (name,signalSignature), handlers)
 | 
						|
    return wrappedFunction
 | 
						|
 | 
						|
# this method checks the last build (if there's one) and logs the number of errors, warnings and
 | 
						|
# lines within the Issues output
 | 
						|
# optional parameter can be used to tell this function if the build was expected to fail or not
 | 
						|
def checkLastBuild(expectedToFail=False):
 | 
						|
    try:
 | 
						|
        # can't use waitForObject() 'cause visible is always 0
 | 
						|
        buildProg = findObject("{type='ProjectExplorer::Internal::BuildProgress' unnamed='1' }")
 | 
						|
    except LookupError:
 | 
						|
        test.log("checkLastBuild called without a build")
 | 
						|
        return
 | 
						|
    ensureChecked(":Qt Creator_Issues_Core::Internal::OutputPaneToggleButton")
 | 
						|
    model = waitForObject(":Qt Creator.Issues_QListView").model()
 | 
						|
    buildIssues = dumpBuildIssues(model)
 | 
						|
    errors = len(filter(lambda i: i[5] == "1", buildIssues))
 | 
						|
    warnings = len(filter(lambda i: i[5] == "2", buildIssues))
 | 
						|
    gotErrors = errors != 0
 | 
						|
    if not (gotErrors ^ expectedToFail):
 | 
						|
        test.passes("Errors: %s | Warnings: %s" % (errors, warnings))
 | 
						|
    else:
 | 
						|
        test.fail("Errors: %s | Warnings: %s" % (errors, warnings))
 | 
						|
    # additional stuff - could be removed... or improved :)
 | 
						|
    test.log("Rows inside issues: %d" % model.rowCount())
 | 
						|
    if gotErrors and createTasksFileOnError:
 | 
						|
        createTasksFile(buildIssues)
 | 
						|
    return not gotErrors
 | 
						|
 | 
						|
# helper function to check the compilation when build wasn't successful
 | 
						|
def checkCompile():
 | 
						|
    ensureChecked(":Qt Creator_CompileOutput_Core::Internal::OutputPaneToggleButton")
 | 
						|
    output = waitForObject(":Qt Creator.Compile Output_Core::OutputWindow")
 | 
						|
    waitFor("len(str(output.plainText))>0",5000)
 | 
						|
    if compileSucceeded(output.plainText):
 | 
						|
        if os.getenv("SYSTEST_DEBUG") == "1":
 | 
						|
            test.log("Compile Output:\n%s" % output.plainText)
 | 
						|
        test.passes("Compile successful")
 | 
						|
        return True
 | 
						|
    else:
 | 
						|
        test.fail("Compile Output:\n%s" % output.plainText)
 | 
						|
        return False
 | 
						|
 | 
						|
def compileSucceeded(compileOutput):
 | 
						|
    return None != re.match(".*exited normally\.\n\d\d:\d\d:\d\d: Elapsed time: "
 | 
						|
                            "(\d:)?\d{2}:\d\d\.$", str(compileOutput), re.S)
 | 
						|
 | 
						|
def dumpBuildIssues(listModel):
 | 
						|
    issueDump = []
 | 
						|
    for row in range(listModel.rowCount()):
 | 
						|
        index = listModel.index(row, 0)
 | 
						|
        issueDump.extend([map(lambda role: index.data(role).toString(),
 | 
						|
                              range(Qt.UserRole, Qt.UserRole + 6))])
 | 
						|
    return issueDump
 | 
						|
 | 
						|
# counter for written tasks files
 | 
						|
tasksFileCount = 0
 | 
						|
 | 
						|
# helper method that writes a tasks file
 | 
						|
def createTasksFile(buildIssues):
 | 
						|
    # currently used directory for tasks files
 | 
						|
    tasksFileDir = None
 | 
						|
    global tasksFileCount
 | 
						|
    if tasksFileDir == None:
 | 
						|
            tasksFileDir = os.getcwd() + "/tasks"
 | 
						|
            tasksFileDir = os.path.abspath(tasksFileDir)
 | 
						|
    if not os.path.exists(tasksFileDir):
 | 
						|
        try:
 | 
						|
            os.makedirs(tasksFileDir)
 | 
						|
        except OSError:
 | 
						|
            test.log("Could not create %s - falling back to a temporary directory" % tasksFileDir)
 | 
						|
            tasksFileDir = tempDir()
 | 
						|
 | 
						|
    tasksFileCount += 1
 | 
						|
    outfile = os.path.join(tasksFileDir, os.path.basename(squishinfo.testCase)+"_%d.tasks" % tasksFileCount)
 | 
						|
    file = codecs.open(outfile, "w", "utf-8")
 | 
						|
    test.log("Writing tasks file - can take some time (according to number of issues)")
 | 
						|
    rows = len(buildIssues)
 | 
						|
    if os.environ.get("SYSTEST_DEBUG") == "1":
 | 
						|
        firstrow = 0
 | 
						|
    else:
 | 
						|
        firstrow = max(0, rows - 100)
 | 
						|
    for issue in buildIssues[firstrow:rows]:
 | 
						|
        # the following is currently a bad work-around
 | 
						|
        fData = issue[0] # file
 | 
						|
        lData = issue[1] # line -> linenumber or empty
 | 
						|
        tData = issue[5] # type -> 1==error 2==warning
 | 
						|
        dData = issue[3] # description
 | 
						|
        if lData == "":
 | 
						|
            lData = "-1"
 | 
						|
        if tData == "1":
 | 
						|
            tData = "error"
 | 
						|
        elif tData == "2":
 | 
						|
            tData = "warning"
 | 
						|
        else:
 | 
						|
            tData = "unknown"
 | 
						|
        file.write("%s\t%s\t%s\t%s\n" % (fData, lData, tData, dData))
 | 
						|
    file.close()
 | 
						|
    test.log("Written tasks file %s" % outfile)
 | 
						|
 | 
						|
# returns a list of pairs each containing the zero based number of a kit
 | 
						|
# and the name of the matching build configuration
 | 
						|
# param kitCount specifies the number of kits currently defined (must be correct!)
 | 
						|
# param filter is a regular expression to filter the configuration by their name
 | 
						|
def iterateBuildConfigs(kitCount, filter = ""):
 | 
						|
    switchViewTo(ViewConstants.PROJECTS)
 | 
						|
    configs = []
 | 
						|
    for currentKit in range(kitCount):
 | 
						|
        switchToBuildOrRunSettingsFor(kitCount, currentKit, ProjectSettings.BUILD)
 | 
						|
        model = waitForObject(":scrollArea.Edit build configuration:_QComboBox").model()
 | 
						|
        prog = re.compile(filter)
 | 
						|
        # for each row in the model, write its data to a list
 | 
						|
        configNames = dumpItems(model)
 | 
						|
        # pick only those configuration names which pass the filter
 | 
						|
        configs += zip([currentKit] * len(configNames),
 | 
						|
                       [config for config in configNames if prog.match(config)])
 | 
						|
    switchViewTo(ViewConstants.EDIT)
 | 
						|
    return configs
 | 
						|
 | 
						|
# selects a build configuration for building the current project
 | 
						|
# param targetCount specifies the number of targets currently defined (must be correct!)
 | 
						|
# param currentTarget specifies the target for which to switch into the specified settings (zero based index)
 | 
						|
# param configName is the name of the configuration that should be selected
 | 
						|
# returns information about the selected kit, see getQtInformationForBuildSettings
 | 
						|
def selectBuildConfig(targetCount, currentTarget, configName):
 | 
						|
    switchViewTo(ViewConstants.PROJECTS)
 | 
						|
    switchToBuildOrRunSettingsFor(targetCount, currentTarget, ProjectSettings.BUILD)
 | 
						|
    selectFromCombo(":scrollArea.Edit build configuration:_QComboBox", configName)
 | 
						|
    progressBarWait(30000)
 | 
						|
    return getQtInformationForBuildSettings(targetCount, True, ViewConstants.EDIT)
 | 
						|
 | 
						|
# This will not trigger a rebuild. If needed, caller has to do this.
 | 
						|
def verifyBuildConfig(targetCount, currentTarget, shouldBeDebug=False, enableShadowBuild=False, enableQmlDebug=False):
 | 
						|
    switchViewTo(ViewConstants.PROJECTS)
 | 
						|
    switchToBuildOrRunSettingsFor(targetCount, currentTarget, ProjectSettings.BUILD)
 | 
						|
    ensureChecked(waitForObject(":scrollArea.Details_Utils::DetailsButton"))
 | 
						|
    ensureChecked("{name='shadowBuildCheckBox' type='QCheckBox' visible='1'}", enableShadowBuild)
 | 
						|
    buildCfCombo = waitForObject("{type='QComboBox' name='buildConfigurationComboBox' visible='1' "
 | 
						|
                                 "window=':Qt Creator_Core::Internal::MainWindow'}")
 | 
						|
    if shouldBeDebug:
 | 
						|
        test.compare(buildCfCombo.currentText, 'Debug', "Verifying whether it's a debug build")
 | 
						|
    else:
 | 
						|
        test.compare(buildCfCombo.currentText, 'Release', "Verifying whether it's a release build")
 | 
						|
    if enableQmlDebug:
 | 
						|
        try:
 | 
						|
            libLabel = waitForObject(":scrollArea.Library not available_QLabel", 2000)
 | 
						|
            mouseClick(libLabel, libLabel.width - 10, libLabel.height / 2, 0, Qt.LeftButton)
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
        # Since waitForObject waits for the object to be enabled,
 | 
						|
        # it will wait here until compilation of the debug libraries has finished.
 | 
						|
        qmlDebugCheckbox = waitForObject(":scrollArea.qmlDebuggingLibraryCheckBox_QCheckBox", 150000)
 | 
						|
        if qmlDebugCheckbox.checked != enableQmlDebug:
 | 
						|
            clickButton(qmlDebugCheckbox)
 | 
						|
            # Don't rebuild now
 | 
						|
            clickButton(waitForObject(":QML Debugging.No_QPushButton", 5000))
 | 
						|
        try:
 | 
						|
            problemFound = waitForObject("{window=':Qt Creator_Core::Internal::MainWindow' "
 | 
						|
                                         "type='QLabel' name='problemLabel' visible='1'}", 1000)
 | 
						|
            if problemFound:
 | 
						|
                test.warning('%s' % problemFound.text)
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
    else:
 | 
						|
        qmlDebugCheckbox = findObject(":scrollArea.qmlDebuggingLibraryCheckBox_QCheckBox")
 | 
						|
        if qmlDebugCheckbox.enabled and qmlDebugCheckbox.checked:
 | 
						|
            test.log("Qml debugging libraries are available - unchecking qml debugging.")
 | 
						|
            clickButton(qmlDebugCheckbox)
 | 
						|
            # Don't rebuild now
 | 
						|
            clickButton(waitForObject(":QML Debugging.No_QPushButton", 5000))
 | 
						|
    clickButton(waitForObject(":scrollArea.Details_Utils::DetailsButton"))
 | 
						|
    switchViewTo(ViewConstants.EDIT)
 | 
						|
 | 
						|
# verify if building and running of project was successful
 | 
						|
def verifyBuildAndRun():
 | 
						|
    # check compile output if build successful
 | 
						|
    checkCompile()
 | 
						|
    # check application output log
 | 
						|
    appOutput = logApplicationOutput()
 | 
						|
    if appOutput:
 | 
						|
        test.verify((re.search(".* exited with code \d+", str(appOutput)) or
 | 
						|
                     re.search("The program has unexpectedly finished\.", str(appOutput))) and
 | 
						|
                    re.search('[Ss]tarting.*', str(appOutput)),
 | 
						|
                    "Verifying if built app started and closed successfully.")
 | 
						|
 | 
						|
# run project for debug and release
 | 
						|
def runVerify(checkedTargets):
 | 
						|
    availableConfigs = iterateBuildConfigs(len(checkedTargets))
 | 
						|
    if not availableConfigs:
 | 
						|
        test.fatal("Haven't found build configurations, quitting")
 | 
						|
        invokeMenuItem("File", "Save All")
 | 
						|
        invokeMenuItem("File", "Exit")
 | 
						|
    # select debug configuration
 | 
						|
    for kit, config in availableConfigs:
 | 
						|
        selectBuildConfig(len(checkedTargets), kit, config)
 | 
						|
        test.log("Using build config '%s'" % config)
 | 
						|
        if not runAndCloseApp():
 | 
						|
            return
 | 
						|
        verifyBuildAndRun()
 | 
						|
        mouseClick(waitForObject(":*Qt Creator.Clear_QToolButton"))
 |