project.py 18.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
processStarted = False
processExited = False

def __handleProcessStarted__(object):
    global processStarted
    processStarted = True

def __handleProcessExited__(object, exitCode):
    global processExited
    processExited = True

12 13
def openQmakeProject(projectPath):
    invokeMenuItem("File", "Open File or Project...")
14
    selectFromFileDialog(projectPath)
15 16
    waitForObject("{type='Qt4ProjectManager::Internal::ProjectLoadWizard' visible='1' windowTitle='Project Setup'}")
    selectFromCombo(":scrollArea.Create Build Configurations:_QComboBox", "For Each Qt Version One Debug And One Release")
17
    clickButton(waitForObject("{text~='(Finish|Done)' type='QPushButton'}"))
18

19
def openCmakeProject(projectPath, buildDir):
20
    invokeMenuItem("File", "Open File or Project...")
21
    selectFromFileDialog(projectPath)
22 23
    replaceEditorContent("{type='Utils::BaseValidatingLineEdit' unnamed='1' visible='1'"
                         "window=':CMake Wizard_CMakeProjectManager::Internal::CMakeOpenProjectWizard'}", buildDir)
24 25 26 27 28 29 30 31
    clickButton(waitForObject(":CMake Wizard.Next_QPushButton", 20000))
    generatorCombo = waitForObject(":Generator:_QComboBox")
    index = generatorCombo.findText("MinGW Generator (MinGW from SDK)")
    if index == -1:
        index = generatorCombo.findText("NMake Generator (Microsoft Visual C++ Compiler 9.0 (x86))")
    if index != -1:
        generatorCombo.setCurrentIndex(index)
    clickButton(waitForObject(":CMake Wizard.Run CMake_QPushButton", 20000))
Christian Stenger's avatar
Christian Stenger committed
32 33 34 35 36 37 38 39 40 41
    try:
        clickButton(waitForObject(":CMake Wizard.Finish_QPushButton", 60000))
    except LookupError:
        cmakeOutput = waitForObject("{type='QPlainTextEdit' unnamed='1' visible='1' "
                                    "window=':CMake Wizard_CMakeProjectManager::Internal::CMakeOpenProjectWizard'}")
        test.warning("Error while executing cmake - see details for cmake output.",
                     str(cmakeOutput.plainText))
        clickButton(waitForObject(":CMake Wizard.Cancel_QPushButton"))
        return False
    return True
42

43 44 45 46 47 48 49 50 51 52 53
def shadowBuildDir(path, project, qtVersion, debugVersion):
    qtVersion = qtVersion.replace(" ", "_")
    qtVersion = qtVersion.replace(".", "_")
    qtVersion = qtVersion.replace("(", "_")
    qtVersion = qtVersion.replace(")", "_")
    buildDir = path + os.sep + project + "-build-desktop-" + qtVersion
    if debugVersion:
        return buildDir + "_Debug"
    else:
        return buildDir + "_Release"

54 55 56 57 58 59 60 61
def __createProjectSelectType__(category, template):
    invokeMenuItem("File", "New File or Project...")
    categoriesView = waitForObject("{type='QTreeView' name='templateCategoryView'}", 20000)
    clickItem(categoriesView, "Projects." + category, 5, 5, 0, Qt.LeftButton)
    templatesView = waitForObject("{name='templatesView' type='QListView'}", 20000)
    clickItem(templatesView, template, 5, 5, 0, Qt.LeftButton)
    clickButton(waitForObject("{text='Choose...' type='QPushButton' unnamed='1' visible='1'}", 20000))

62
def __createProjectSetNameAndPath__(path, projectName = None, checks = True):
63 64 65 66 67 68 69 70 71 72 73 74 75
    directoryEdit = waitForObject("{type='Utils::BaseValidatingLineEdit' unnamed='1' visible='1'}", 20000)
    replaceEditorContent(directoryEdit, path)
    projectNameEdit = waitForObject("{name='nameLineEdit' visible='1' "
                                    "type='Utils::ProjectNameValidatingLineEdit'}", 20000)
    if projectName == None:
        projectName = projectNameEdit.text
    else:
        replaceEditorContent(projectNameEdit, projectName)
    if checks:
        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
76
    ensureChecked("{type='QCheckBox' name='projectsDirectoryCheckBox' visible='1'}", False)
77
    clickButton(waitForObject(":Next_QPushButton"))
78
    return str(projectName)
79

80 81 82 83
# Selects the Qt versions for a project
# param qtVersion is the name of a Qt version. In the project, build configurations will be
#                 created for this version. If it is None, all Qt versions will be used
# param checks turns tests in the function on if set to True
84 85
def __selectQtVersionDesktop__(qtVersion, checks):
    __chooseTargets__()
86 87 88 89 90 91 92 93 94 95 96 97
    if qtVersion == None:
        selectFromCombo(":scrollArea.Create Build Configurations:_QComboBox_2",
                        "For Each Qt Version One Debug And One Release")
        ensureChecked(":scrollArea.Use Shadow Building_QCheckBox")
    else:
        selectFromCombo(":scrollArea.Create Build Configurations:_QComboBox_2",
                        "For One Qt Version One Debug And One Release")
        ensureChecked(":scrollArea.Use Shadow Building_QCheckBox")
        selectFromCombo(":scrollArea.Qt Version:_QComboBox", qtVersion)
        if checks:
            verifyChecked(":scrollArea.Qt 4 for Desktop - (Qt SDK) debug_QCheckBox")
            verifyChecked(":scrollArea.Qt 4 for Desktop - (Qt SDK) release_QCheckBox")
98 99
    clickButton(waitForObject(":Next_QPushButton"))

100
def __createProjectHandleLastPage__(expectedFiles = None):
101
    if expectedFiles != None:
102 103
        summary = str(waitForObject("{name='filesLabel' text?='<qt>Files to be added in<pre>*</pre>'"
                                    "type='QLabel' visible='1'}").text)
104 105 106 107 108 109 110 111
        lastIndex = 0
        for filename in expectedFiles:
            index = summary.find(filename)
            test.verify(index > lastIndex, "'" + filename + "' found at index " + str(index))
            lastIndex = index
    selectFromCombo(":addToVersionControlComboBox_QComboBox", "<None>")
    clickButton(waitForObject("{type='QPushButton' text~='(Finish|Done)' visible='1'}", 20000))

112 113 114 115 116 117
def __verifyFileCreation__(path, expectedFiles):
    for filename in expectedFiles:
        if filename != path:
            filename = os.path.join(path, filename)
        test.verify(os.path.exists(filename), "Checking if '" + filename + "' was created")

118 119 120 121 122 123 124
# Creates a Qt GUI project
# param path specifies where to create the project
# param projectName is the name for the new project
# param qtVersion is the name of a Qt version. In the project, build configurations will be
#                 created for this version. If it is None, all Qt versions will be used
# param checks turns tests in the function on if set to True
def createProject_Qt_GUI(path, projectName, qtVersion = None, checks = True):
125
    __createProjectSelectType__("Other Qt Project", "Qt Gui Application")
126
    __createProjectSetNameAndPath__(path, projectName, checks)
127
    __selectQtVersionDesktop__(qtVersion, checks)
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143

    if checks:
        exp_filename = "mainwindow"
        h_file = exp_filename + ".h"
        cpp_file = exp_filename + ".cpp"
        ui_file = exp_filename + ".ui"
        pro_file = projectName + ".pro"

        waitFor("object.exists(':headerFileLineEdit_Utils::FileNameValidatingLineEdit')", 20000)
        waitFor("object.exists(':sourceFileLineEdit_Utils::FileNameValidatingLineEdit')", 20000)
        waitFor("object.exists(':formFileLineEdit_Utils::FileNameValidatingLineEdit')", 20000)

        test.compare(findObject(":headerFileLineEdit_Utils::FileNameValidatingLineEdit").text, h_file)
        test.compare(findObject(":sourceFileLineEdit_Utils::FileNameValidatingLineEdit").text, cpp_file)
        test.compare(findObject(":formFileLineEdit_Utils::FileNameValidatingLineEdit").text, ui_file)

144
    clickButton(waitForObject(":Next_QPushButton"))
145

146
    expectedFiles = None
147
    if checks:
148 149
        if platform.system() in ('Windows', 'Microsoft'):
            path = os.path.abspath(path)
Robert Loehning's avatar
Robert Loehning committed
150 151
        path = os.path.join(path, projectName)
        expectedFiles = [path, cpp_file, h_file, ui_file, pro_file]
152
    __createProjectHandleLastPage__(expectedFiles)
153

154 155 156 157 158 159 160
    waitForSignal("{type='CppTools::Internal::CppModelManager' unnamed='1'}", "sourceFilesRefreshed(QStringList)", 20000)
    __verifyFileCreation__(path, expectedFiles)

def createProject_Qt_Console(path, projectName, qtVersion, checks):
    __createProjectSelectType__("Other Qt Project", "Qt Console Application")
    __createProjectSetNameAndPath__(path, projectName, checks)
    __selectQtVersionDesktop__(qtVersion, checks)
161

162 163 164 165 166 167 168 169 170
    expectedFiles = None
    if checks:
        if platform.system() in ('Windows', 'Microsoft'):
            path = os.path.abspath(path)
        path = os.path.join(path, projectName)
        cpp_file = "main.cpp"
        pro_file = projectName + ".pro"
        expectedFiles = [path, cpp_file, pro_file]
    __createProjectHandleLastPage__(expectedFiles)
171

172 173
    waitForSignal("{type='CppTools::Internal::CppModelManager' unnamed='1'}", "sourceFilesRefreshed(QStringList)", 10000)
    __verifyFileCreation__(path, expectedFiles)
174

175
def createNewQtQuickApplication(workingDir, projectName = None, templateFile = None, targets = QtQuickConstants.Targets.DESKTOP):
176
    __createProjectSelectType__("Qt Quick Project", "Qt Quick Application")
177
    projectName = __createProjectSetNameAndPath__(workingDir, projectName)
178
    if (templateFile==None):
179
        __chooseComponents__()
180
    else:
181
        __chooseComponents__(QtQuickConstants.Components.EXISTING_QML)
182 183 184
        # define the existing qml file to import
        baseLineEd = waitForObject("{type='Utils::BaseValidatingLineEdit' unnamed='1' visible='1'}", 20000)
        type(baseLineEd, templateFile)
185
    nextButton = waitForObject(":Next_QPushButton", 20000)
186
    clickButton(nextButton)
187
    __chooseTargets__(targets)
188 189
    snooze(1)
    clickButton(nextButton)
190
    __createProjectHandleLastPage__()
191
    return projectName
192 193

def createNewQtQuickUI(workingDir):
194
    __createProjectSelectType__("Qt Quick Project", "Qt Quick UI")
195 196
    if workingDir == None:
        workingDir = tempDir()
197
    projectName = __createProjectSetNameAndPath__(workingDir)
198
    __createProjectHandleLastPage__()
199
    return projectName
200 201

def createNewQmlExtension(workingDir):
202
    __createProjectSelectType__("Qt Quick Project", "Custom QML Extension Plugin")
203 204
    if workingDir == None:
        workingDir = tempDir()
205 206
    __createProjectSetNameAndPath__(workingDir)
    __chooseTargets__()
207
    nextButton = waitForObject(":Next_QPushButton")
208 209 210 211 212 213 214 215
    clickButton(nextButton)
    nameLineEd = waitForObject("{buddy={type='QLabel' text='Object Class-name:' unnamed='1' visible='1'} "
                               "type='QLineEdit' unnamed='1' visible='1'}", 20000)
    replaceEditorContent(nameLineEd, "TestItem")
    uriLineEd = waitForObject("{buddy={type='QLabel' text='URI:' unnamed='1' visible='1'} "
                              "type='QLineEdit' unnamed='1' visible='1'}", 20000)
    replaceEditorContent(uriLineEd, "com.nokia.test.qmlcomponents")
    clickButton(nextButton)
216
    __createProjectHandleLastPage__()
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

# parameter components can only be one of the Constants defined in QtQuickConstants.Components
def __chooseComponents__(components=QtQuickConstants.Components.BUILTIN):
    rbComponentToChoose = waitForObject("{type='QRadioButton' text='%s' visible='1'}"
                              % QtQuickConstants.getStringForComponents(components), 20000)
    if rbComponentToChoose.checked:
        test.passes("Selected QRadioButton is '%s'" % QtQuickConstants.getStringForComponents(components))
    else:
        clickButton(rbComponentToChoose)
        test.verify(rbComponentToChoose.checked, "Selected QRadioButton is '%s'"
                % QtQuickConstants.getStringForComponents(components))

# parameter target can be an OR'd value of QtQuickConstants.Targets
def __chooseTargets__(targets=QtQuickConstants.Targets.DESKTOP):
     # DESKTOP should be always accessible
    ensureChecked("{type='QCheckBox' text='%s' visible='1'}"
                  % QtQuickConstants.getStringForTarget(QtQuickConstants.Targets.DESKTOP),
                  targets & QtQuickConstants.Targets.DESKTOP)
    # following targets depend on the build environment - added for further/later tests
    available = [QtQuickConstants.Targets.MAEMO5,
                 QtQuickConstants.Targets.SIMULATOR, QtQuickConstants.Targets.HARMATTAN]
    if platform.system() in ('Windows', 'Microsoft'):
        available += [QtQuickConstants.Targets.SYMBIAN]
    for current in available:
        mustCheck = targets & current == current
        try:
            ensureChecked("{type='QCheckBox' text='%s' visible='1'}" % QtQuickConstants.getStringForTarget(current),
                          mustCheck)
        except LookupError:
            if mustCheck:
                test.fail("Failed to check target '%s'" % QtQuickConstants.getStringForTarget(current))

# run and close an application
# withHookInto - if set to True the function tries to attach to the sub-process instead of simply pressing Stop inside Creator
# executable - must be defined when using hook-into
# port - must be defined when using hook-into
# function - can be a string holding a function name or a reference to the function itself - this function will be called on
# the sub-process when hooking-into has been successful - if its missing simply closing the Qt Quick app will be done
# sType the SubprocessType - is nearly mandatory - except when using the function parameter
# userDefinedType - if you set sType to SubprocessType.USER_DEFINED you must(!) specify the WindowType for hooking into
# by yourself (or use the function parameter)
# ATTENTION! Make sure this function won't fail and the sub-process will end when the function returns
def runAndCloseApp(withHookInto=False, executable=None, port=None, function=None, sType=None, userDefinedType=None):
    global processStarted, processExited
    processStarted = processExited = False
    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)
    if sType != SubprocessType.QT_QUICK_UI:
        waitForSignal("{type='ProjectExplorer::BuildManager' unnamed='1'}", "buildQueueFinished(bool)", 300000)
        buildSucceeded = checkLastBuild()
        if not buildSucceeded:
            test.fatal("Build inside run wasn't successful - leaving test")
            invokeMenuItem("File", "Exit")
            return False
    waitFor("processStarted==True", 10000)
    if not processStarted:
        test.fatal("Couldn't start application - leaving test")
        invokeMenuItem("File", "Exit")
        return False
278 279
    if os.getenv("SYSTEST_QMLVIEWER_NO_HOOK_INTO", "0") == "1":
        withHookInto = False
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
    if withHookInto and not validType(sType, userDefinedType):
        if function != None:
            test.warning("You did not provide a valid value for the SubprocessType value - sType, but you have "
                         "provided a function to execute on the subprocess. Please ensure that your function "
                         "closes the subprocess before exiting, or this test will not complete.")
        else:
            test.warning("You did not provide a valid value for the SubprocessType value - sType, nor a "
                         "function to execute on the subprocess. Falling back to pushing the STOP button "
                         "inside creator to terminate execution of the subprocess.")
            withHookInto = False
    if withHookInto and not executable in ("", None):
        __closeSubprocessByHookingInto__(executable, port, function, sType, userDefinedType)
    else:
        __closeSubprocessByPushingStop__(sType)
    return True

def validType(sType, userDef):
    if sType == None:
        return False
    ty = SubprocessType.getWindowType(sType)
    return ty != None and not (ty == "user-defined" and (userDef == None or userDef.strip() == ""))

def __closeSubprocessByPushingStop__(sType):
303
    ensureChecked(":Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton")
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
    playButton = verifyEnabled(":Qt Creator.ReRun_QToolButton", False)
    stopButton = verifyEnabled(":Qt Creator.Stop_QToolButton")
    if stopButton.enabled:
        clickButton(stopButton)
        test.verify(playButton.enabled)
        test.compare(stopButton.enabled, False)
        if sType == SubprocessType.QT_QUICK_UI and platform.system() == "Darwin":
            waitFor("stop.enabled==False")
            snooze(2)
            nativeType("<Escape>")
    else:
        test.fatal("Subprocess does not seem to have been started.")

def __closeSubprocessByHookingInto__(executable, port, function, sType, userDefType):
    global processExited
319
    ensureChecked(":Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton")
320 321 322 323 324 325 326 327
    output = waitForObject("{type='Core::OutputWindow' visible='1' windowTitle='Application Output Window'}", 20000)
    if port == None:
        test.warning("I need a port number or attaching might fail.")
    else:
        waitFor("'Listening on port %d for incoming connectionsdone' in str(output.plainText)" % port, 5000)
    try:
        attachToApplication(executable)
    except:
328
        test.warning("Could not attach to '%s' - using fallback of pushing STOP inside Creator." % executable)
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
        resetApplicationContextToCreator()
        __closeSubprocessByPushingStop__(sType)
        return False
    if function == None:
        if sType==SubprocessType.USER_DEFINED:
            sendEvent("QCloseEvent", "{type='%s' unnamed='1' visible='1'}" % userDefType)
        else:
            sendEvent("QCloseEvent", "{type='%s' unnamed='1' visible='1'}" % SubprocessType.getWindowType(sType))
        resetApplicationContextToCreator()
    else:
        try:
            if isinstance(function, (str, unicode)):
                globals()[function]()
            else:
                function()
        except:
            test.fatal("Function to execute on sub-process could not be found.",
                       "Using fallback of pushing STOP inside Creator.")
            resetApplicationContextToCreator()
            __closeSubprocessByPushingStop__(sType)
    waitFor("processExited==True", 10000)
    if not processExited:
        test.warning("Sub-process seems not to have closed properly.")
        try:
            resetApplicationContextToCreator()
            __closeSubprocessByPushingStop__(sType)
        except:
            pass
    return True

# this helper tries to reset the current application context back
# to creator - this strange work-around is needed _sometimes_ on MacOS
def resetApplicationContextToCreator():
    appCtxt = applicationContext("qtcreator")
    if appCtxt.name == "":
        appCtxt = applicationContext("Qt Creator")
    setApplicationContext(appCtxt)