project.py 33.4 KB
Newer Older
1
2
#############################################################################
##
3
## Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
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
## 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.
##
#############################################################################

30
import __builtin__
31
32
import re

Christian Stenger's avatar
Christian Stenger committed
33
def openQmakeProject(projectPath, targets=Targets.desktopTargetClasses(), fromWelcome=False):
34
    cleanUpUserFiles(projectPath)
35
36
37
    if fromWelcome:
        mouseClick(waitForObject(":OpenProject_QStyleItem"), 5, 5, 0, Qt.LeftButton)
        if not platform.system() == "Darwin":
38
            waitFor("waitForObject(':fileNameEdit_QLineEdit', 1000).focus == True", 3000)
39
40
    else:
        invokeMenuItem("File", "Open File or Project...")
41
    selectFromFileDialog(projectPath)
42
43
    try:
        # handle update generated files dialog
44
45
46
        waitForObject("{type='QLabel' name='qt_msgbox_label' visible='1' "
                      "text?='The following files are either outdated or have been modified*' "
                      "window={type='QMessageBox' unnamed='1' visible='1'}}", 3000)
47
48
49
        clickButton(waitForObject("{text='Yes' type='QPushButton' unnamed='1' visible='1'}"))
    except:
        pass
50
    checkedTargets = __chooseTargets__(targets)
51
    configureButton = waitForObject("{text='Configure Project' type='QPushButton' unnamed='1' visible='1'"
52
                                    "window=':Qt Creator_Core::Internal::MainWindow'}")
53
    clickButton(configureButton)
54
    return checkedTargets
55

56
def openCmakeProject(projectPath, buildDir):
57
    invokeMenuItem("File", "Open File or Project...")
58
    selectFromFileDialog(projectPath)
59
    replaceEditorContent("{type='Utils::FancyLineEdit' unnamed='1' visible='1'"
60
                         "window=':CMake Wizard_CMakeProjectManager::Internal::CMakeOpenProjectWizard'}", buildDir)
61
    clickButton(waitForObject(":CMake Wizard.Next_QPushButton"))
62
63
64
    return __handleCmakeWizardPage__()

def __handleCmakeWizardPage__():
65
    generatorCombo = waitForObject(":Generator:_QComboBox")
66
67
68
    mkspec = __getMkspecFromQmake__("qmake")
    test.log("Using mkspec '%s'" % mkspec)

69
    generatorText = "Unix Generator (Desktop 474 GCC)"
70
    if "win32-" in mkspec:
71
        generatorName = {"win32-g++" : "MinGW Generator (Desktop 474 GCC)",
72
                         "win32-msvc2010" : "NMake Generator (Desktop 480 MSVC2010)"}
73
        if mkspec in generatorName:
74
75
76
77
78
79
            generatorText = generatorName[mkspec]
    index = generatorCombo.findText(generatorText)
    if index == -1:
        test.warning("No matching CMake generator for mkspec '%s' found." % mkspec)
    else:
        generatorCombo.setCurrentIndex(index)
80

81
    clickButton(waitForObject(":CMake Wizard.Run CMake_QPushButton"))
Christian Stenger's avatar
Christian Stenger committed
82
83
84
85
86
87
88
89
90
91
    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
92

93
94
95
96
# this function returns a list of available targets - this is not 100% error proof
# because the Simulator target is added for some cases even when Simulator has not
# been set up inside Qt versions/Toolchains
# this list can be used in __chooseTargets__()
97
def __createProjectOrFileSelectType__(category, template, fromWelcome = False, isProject=True):
98
99
100
101
    if fromWelcome:
        mouseClick(waitForObject(":CreateProject_QStyleItem"), 5, 5, 0, Qt.LeftButton)
    else:
        invokeMenuItem("File", "New File or Project...")
102
    categoriesView = waitForObject("{type='QTreeView' name='templateCategoryView'}")
103
104
105
106
    if isProject:
        clickItem(categoriesView, "Projects." + category, 5, 5, 0, Qt.LeftButton)
    else:
        clickItem(categoriesView, "Files and Classes." + category, 5, 5, 0, Qt.LeftButton)
107
    templatesView = waitForObject("{name='templatesView' type='QListView'}")
108
    clickItem(templatesView, template, 5, 5, 0, Qt.LeftButton)
109
    text = waitForObject("{type='QTextBrowser' name='templateDescription' visible='1'}").plainText
110
    clickButton(waitForObject("{text='Choose...' type='QPushButton' unnamed='1' visible='1'}"))
111
    return __getSupportedPlatforms__(str(text), template)[0]
112

113
def __createProjectSetNameAndPath__(path, projectName = None, checks = True):
114
115
    directoryEdit = waitForObject("{type='Utils::FancyLineEdit' unnamed='1' visible='1' "
                                  "toolTip?='Full path: *'}")
116
117
    replaceEditorContent(directoryEdit, path)
    projectNameEdit = waitForObject("{name='nameLineEdit' visible='1' "
118
                                    "type='Utils::ProjectNameValidatingLineEdit'}")
119
120
121
122
123
124
125
126
127
    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
128
    ensureChecked("{type='QCheckBox' name='projectsDirectoryCheckBox' visible='1'}", False)
129
    clickButton(waitForObject(":Next_QPushButton"))
130
    return str(projectName)
131

132
def __createProjectHandleQtQuickSelection__(qtQuickVersion, controlsVersion):
133
134
135
    comboBox = waitForObject("{type='QComboBox' unnamed='1' visible='1' "
                             "leftWidget={text='Qt Quick component set:' type='QLabel' unnamed='1' "
                             "visible='1'}}")
136
    if qtQuickVersion == "1.1":
137
        selectFromCombo(comboBox, "Qt Quick 1.1")
138
        if controlsVersion:
139
            test.warning("Controls are not available for Quick 1.")
140
141
142
143
144
145
146
    elif qtQuickVersion[:2] == "2.":
        if controlsVersion:
            if controlsVersion in ("1.0", "1.1"):
                selectFromCombo(comboBox, "Qt Quick Controls %s" % controlsVersion)
            else:
                test.fatal("Got unknown Qt Quick Controls version: %s - trying to continue."
                           % str(controlsVersion))
147
        else:
148
            selectFromCombo(comboBox, "Qt Quick %s" % qtQuickVersion)
149
150
    else:
        test.fatal("Got unknown Qt Quick version: %s - trying to continue." % str(qtQuickVersion))
151
152
153
154
    label = waitForObject("{type='QLabel' unnamed='1' visible='1' text?='Creates a *' }")
    requires = re.match(".*Requires Qt (\d\.\d).*", str(label.text))
    if requires:
        requires = requires.group(1)
155
    clickButton(waitForObject(":Next_QPushButton"))
156
    return requires
157

158
159
# Selects the Qt versions for a project
# param checks turns tests in the function on if set to True
160
# param available a list holding the available targets
161
def __selectQtVersionDesktop__(checks, available=None):
Christian Stenger's avatar
Christian Stenger committed
162
    checkedTargets = __chooseTargets__(Targets.desktopTargetClasses(), available)
163
    if checks:
164
165
166
167
168
169
170
171
172
173
174
        for target in checkedTargets:
            detailsWidget = waitForObject("{type='Utils::DetailsWidget' unnamed='1' visible='1' "
                                          "summaryText='%s'}" % Targets.getStringForTarget(target))
            detailsButton = getChildByClass(detailsWidget, "Utils::DetailsButton")
            if test.verify(detailsButton != None, "Verifying if 'Details' button could be found"):
                clickButton(detailsButton)
                cbObject = ("{type='QCheckBox' text='%s' unnamed='1' visible='1' "
                            "container=%s}")
                verifyChecked(cbObject % ("Debug", objectMap.realName(detailsWidget)))
                verifyChecked(cbObject % ("Release", objectMap.realName(detailsWidget)))
                clickButton(detailsButton)
175
    clickButton(waitForObject(":Next_QPushButton"))
176
    return checkedTargets
177

178
def __createProjectHandleLastPage__(expectedFiles = None, addToVersionControl = "<None>", addToProject = None):
179
    if expectedFiles != None:
180
181
182
183
184
185
        summary = waitForObject("{name='filesLabel' text?='<qt>Files to be added in<pre>*</pre>' "
                                "type='QLabel' visible='1'}").text
        verifyItemOrder(expectedFiles, summary)
    if addToProject:
        selectFromCombo(":projectComboBox_QComboBox", addToProject)
    selectFromCombo(":addToVersionControlComboBox_QComboBox", addToVersionControl)
186
    clickButton(waitForObject("{type='QPushButton' text~='(Finish|Done)' visible='1'}"))
187

188
189
190
191
192
193
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")

194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
def __modifyAvailableTargets__(available, requiredQt, asStrings=False):
    threeDigits = re.compile("\d{3}")
    requiredQtVersion = requiredQt.replace(".", "") + "0"
    tmp = list(available) # we need a deep copy
    for currentItem in tmp:
        if asStrings:
            item = currentItem
        else:
            item = Targets.getStringForTarget(currentItem)
        found = threeDigits.search(item)
        if found:
            if found.group(0) < requiredQtVersion:
                # Quick 1.1 supports 4.7.4 only for running, debugging is unsupported
                # so the least required version is 4.8, but 4.7.4 will be still listed
                if not (requiredQtVersion == "480" and found.group(0) == "474"):
                    available.remove(currentItem)
            if requiredQtVersion > "480":
                toBeRemoved = [Targets.EMBEDDED_LINUX, Targets.SIMULATOR]
                if asStrings:
                    toBeRemoved = Targets.getTargetsAsStrings(toBeRemoved)
                for t in toBeRemoved:
                    if t in available:
                        available.remove(t)

218
219
220
221
# Creates a Qt GUI project
# param path specifies where to create the project
# param projectName is the name for the new project
# param checks turns tests in the function on if set to True
222
def createProject_Qt_GUI(path, projectName, checks = True, addToVersionControl = "<None>"):
223
    template = "Qt Widgets Application"
224
    available = __createProjectOrFileSelectType__("  Applications", template)
225
    __createProjectSetNameAndPath__(path, projectName, checks)
226
    checkedTargets = __selectQtVersionDesktop__(checks, available)
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242

    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)

243
    clickButton(waitForObject(":Next_QPushButton"))
244

245
    expectedFiles = None
246
    if checks:
247
248
        if platform.system() in ('Windows', 'Microsoft'):
            path = os.path.abspath(path)
Robert Loehning's avatar
Robert Loehning committed
249
        path = os.path.join(path, projectName)
250
        expectedFiles = [path]
251
        expectedFiles.extend(__sortFilenamesOSDependent__(["main.cpp", cpp_file, h_file, ui_file, pro_file]))
252
    __createProjectHandleLastPage__(expectedFiles, addToVersionControl)
253

254
    progressBarWait(20000)
255
    __verifyFileCreation__(path, expectedFiles)
256
    return checkedTargets
257

258
259
260
261
# Creates a Qt Console project
# param path specifies where to create the project
# param projectName is the name for the new project
# param checks turns tests in the function on if set to True
262
def createProject_Qt_Console(path, projectName, checks = True):
263
    available = __createProjectOrFileSelectType__("  Applications", "Qt Console Application")
264
    __createProjectSetNameAndPath__(path, projectName, checks)
265
    checkedTargets = __selectQtVersionDesktop__(checks, available)
266

267
268
269
270
271
272
273
    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"
274
275
        expectedFiles = [path]
        expectedFiles.extend(__sortFilenamesOSDependent__([cpp_file, pro_file]))
276
    __createProjectHandleLastPage__(expectedFiles)
277

278
    progressBarWait(10000)
279
    __verifyFileCreation__(path, expectedFiles)
280
    return checkedTargets
281

282
def createNewQtQuickApplication(workingDir, projectName = None,
283
284
                                targets=Targets.desktopTargetClasses(), qtQuickVersion="1.1",
                                fromWelcome=False, controlsVersion=None):
285
    available = __createProjectOrFileSelectType__("  Applications", "Qt Quick Application", fromWelcome)
286
    projectName = __createProjectSetNameAndPath__(workingDir, projectName)
287
    requiredQt = __createProjectHandleQtQuickSelection__(qtQuickVersion, controlsVersion)
288
    __modifyAvailableTargets__(available, requiredQt)
289
    checkedTargets = __chooseTargets__(targets, available)
290
    snooze(1)
291
    clickButton(waitForObject(":Next_QPushButton"))
292
    __createProjectHandleLastPage__()
293

294
    progressBarWait(10000)
295
    return checkedTargets, projectName
296

297
def createNewQtQuickUI(workingDir, qtQuickVersion="1.1", controlsVersion=None):
298
    __createProjectOrFileSelectType__("  Applications", "Qt Quick UI")
299
300
    if workingDir == None:
        workingDir = tempDir()
301
    projectName = __createProjectSetNameAndPath__(workingDir)
302
    __createProjectHandleQtQuickSelection__(qtQuickVersion, controlsVersion)
303
    __createProjectHandleLastPage__()
304
    return projectName
305

306
307
308
def createNewQmlExtension(workingDir, targets=Targets.DESKTOP_474_GCC, qtQuickVersion=1):
    available = __createProjectOrFileSelectType__("  Libraries", "Qt Quick %d Extension Plugin"
                                                  % qtQuickVersion)
309
310
    if workingDir == None:
        workingDir = tempDir()
311
    __createProjectSetNameAndPath__(workingDir)
312
    checkedTargets = __chooseTargets__(targets, available)
313
    nextButton = waitForObject(":Next_QPushButton")
314
    clickButton(nextButton)
315
    nameLineEd = waitForObject("{buddy={type='QLabel' text='Object class-name:' unnamed='1' visible='1'} "
316
                               "type='QLineEdit' unnamed='1' visible='1'}")
317
318
    replaceEditorContent(nameLineEd, "TestItem")
    uriLineEd = waitForObject("{buddy={type='QLabel' text='URI:' unnamed='1' visible='1'} "
319
                              "type='QLineEdit' unnamed='1' visible='1'}")
Robert Loehning's avatar
Robert Loehning committed
320
    replaceEditorContent(uriLineEd, "org.qt-project.test.qmlcomponents")
321
    clickButton(nextButton)
322
    __createProjectHandleLastPage__()
323
    return checkedTargets
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
def createEmptyQtProject(workingDir=None, projectName=None, targets=Targets.desktopTargetClasses()):
    __createProjectOrFileSelectType__("  Other Project", "Empty Qt Project")
    if workingDir == None:
        workingDir = tempDir()
    projectName = __createProjectSetNameAndPath__(workingDir, projectName)
    checkedTargets = __chooseTargets__(targets)
    snooze(1)
    clickButton(waitForObject(":Next_QPushButton"))
    __createProjectHandleLastPage__()
    return projectName, checkedTargets

def createNewNonQtProject(workingDir=None, projectName=None, target=Targets.DESKTOP_474_GCC,
                          plainC=False, cmake=False):
    if plainC:
        template = "Plain C Project"
    else:
        template = "Plain C++ Project"
    if cmake:
        template += " (CMake Build)"
    available = __createProjectOrFileSelectType__("  Non-Qt Project", template)
    if workingDir == None:
        workingDir = tempDir()
    projectName = __createProjectSetNameAndPath__(workingDir, projectName)
    if cmake:
        __createProjectHandleLastPage__()
        clickButton(waitForObject(":Next_QPushButton"))
        if not __handleCmakeWizardPage__():
            return None
    else:
        __chooseTargets__(target, availableTargets=available)
        clickButton(waitForObject(":Next_QPushButton"))
        __createProjectHandleLastPage__()
    return projectName

Christian Stenger's avatar
Christian Stenger committed
359
# parameter target can be an OR'd value of Targets
360
# parameter availableTargets should be the result of __createProjectOrFileSelectType__()
361
#           or use None as a fallback
362
def __chooseTargets__(targets=Targets.DESKTOP_474_GCC, availableTargets=None):
363
364
365
366
    if availableTargets != None:
        available = availableTargets
    else:
        # following targets depend on the build environment - added for further/later tests
367
        available = [Targets.DESKTOP_474_GCC, Targets.DESKTOP_480_GCC, Targets.DESKTOP_501_DEFAULT,
368
369
                     Targets.DESKTOP_521_DEFAULT, Targets.MAEMO5, Targets.EMBEDDED_LINUX,
                     Targets.SIMULATOR, Targets.HARMATTAN]
370
        if platform.system() in ('Windows', 'Microsoft'):
Christian Stenger's avatar
Christian Stenger committed
371
            available.remove(Targets.EMBEDDED_LINUX)
372
            available.append(Targets.DESKTOP_480_MSVC2010)
373
374
375
    for target in filter(lambda x: x in available,
                         (Targets.MAEMO5, Targets.HARMATTAN)):
        available.remove(target)
376
    checkedTargets = []
377
378
379
    for current in available:
        mustCheck = targets & current == current
        try:
Christian Stenger's avatar
Christian Stenger committed
380
            ensureChecked("{type='QCheckBox' text='%s' visible='1'}" % Targets.getStringForTarget(current),
381
                          mustCheck, 3000)
382
383
            if (mustCheck):
                checkedTargets.append(current)
384
385
        except LookupError:
            if mustCheck:
Christian Stenger's avatar
Christian Stenger committed
386
                test.fail("Failed to check target '%s'." % Targets.getStringForTarget(current))
387
            else:
388
                # Simulator has been added without knowing whether configured or not - so skip warning here?
389
                if current != Targets.SIMULATOR:
Christian Stenger's avatar
Christian Stenger committed
390
                    test.warning("Target '%s' is not set up correctly." % Targets.getStringForTarget(current))
391
    return checkedTargets
392

393
def waitForProcessRunning(running=True):
394
395
396
397
398
399
400
    outputButton = waitForObject(":Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton")
    if not waitFor("outputButton.checked", 10000):
        ensureChecked(outputButton)
    waitFor("object.exists(':Qt Creator.ReRun_QToolButton')", 20000)
    reRunButton = findObject(":Qt Creator.ReRun_QToolButton")
    waitFor("object.exists(':Qt Creator.Stop_QToolButton')", 20000)
    stopButton = findObject(":Qt Creator.Stop_QToolButton")
401
    return waitFor("(reRunButton.enabled != running) and (stopButton.enabled == running)", 10000)
402

403
404
405
406
407
408
409
410
411
412
# 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
413
# returns None if the build failed, False if the subprocess did not start, and True otherwise
414
def runAndCloseApp(withHookInto=False, executable=None, port=None, function=None, sType=None, userDefinedType=None, quickVersion="1.1"):
415
    runButton = waitForObject(":*Qt Creator.Run_Core::Internal::FancyToolButton")
416
417
    clickButton(runButton)
    if sType != SubprocessType.QT_QUICK_UI:
418
        waitForCompile(300000)
419
        buildSucceeded = checkLastBuild()
420
        ensureChecked(waitForObject(":Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton"))
421
422
        if not buildSucceeded:
            test.fatal("Build inside run wasn't successful - leaving test")
423
            return None
424
    if not waitForProcessRunning():
425
426
        test.fatal("Couldn't start application - leaving test")
        return False
427
    if sType == SubprocessType.QT_QUICK_UI and os.getenv("SYSTEST_QMLVIEWER_NO_HOOK_INTO", "0") == "1":
428
        withHookInto = False
429
    if withHookInto and not validType(sType, userDefinedType, quickVersion):
430
431
432
433
434
435
436
437
438
439
        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):
440
        __closeSubprocessByHookingInto__(executable, port, function, sType, userDefinedType, quickVersion)
441
442
443
444
    else:
        __closeSubprocessByPushingStop__(sType)
    return True

445
def validType(sType, userDef, quickVersion):
446
447
    if sType == None:
        return False
448
    ty = SubprocessType.getWindowType(sType, quickVersion)
449
450
451
    return ty != None and not (ty == "user-defined" and (userDef == None or userDef.strip() == ""))

def __closeSubprocessByPushingStop__(sType):
452
    ensureChecked(":Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton")
453
454
455
456
    try:
        waitForObject(":Qt Creator.Stop_QToolButton", 5000)
    except:
        pass
457
458
459
460
    playButton = verifyEnabled(":Qt Creator.ReRun_QToolButton", False)
    stopButton = verifyEnabled(":Qt Creator.Stop_QToolButton")
    if stopButton.enabled:
        clickButton(stopButton)
461
462
        test.verify(waitFor("playButton.enabled", 5000), "Play button should be enabled")
        test.compare(stopButton.enabled, False, "Stop button should be disabled")
463
        if sType == SubprocessType.QT_QUICK_UI and platform.system() == "Darwin":
464
            waitFor("stopButton.enabled==False")
465
466
467
468
469
            snooze(2)
            nativeType("<Escape>")
    else:
        test.fatal("Subprocess does not seem to have been started.")

470
def __closeSubprocessByHookingInto__(executable, port, function, sType, userDefType, quickVersion):
471
    ensureChecked(":Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton")
472
    output = waitForObject("{type='Core::OutputWindow' visible='1' windowTitle='Application Output Window'}")
473
474
475
476
477
478
479
480
    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:
        resetApplicationContextToCreator()
481
482
        if ("Loading Qt Wrapper failed" in str(output.plainText)
            or "Failed to assign process to job object" in str(output.plainText)):
483
484
485
486
487
            test.warning("Loading of Qt Wrapper failed - probably different Qt versions.",
                         "Resetting hook-into settings to continue.")
            # assuming we're still on the build settings of the current project (TODO)
            switchViewTo(ViewConstants.PROJECTS)
            if sType == SubprocessType.QT_QUICK_UI:
488
489
490
491
                if "qmlscene" in executable:
                    selectConfig = "QML Scene"
                else:
                    selectConfig = "QML Viewer"
492
493
494
495
496
497
            else:
                selectConfig = executable
            selectFromCombo(waitForObject("{buddy={text='Run configuration:' type='QLabel' "
                                          "unnamed='1' visible='1'} type='QComboBox' unnamed='1' "
                                          "visible='1'}"), selectConfig)
            switchViewTo(ViewConstants.EDIT)
498
            runButton = waitForObject(":*Qt Creator.Run_Core::Internal::FancyToolButton")
499
            clickButton(runButton)
500
            if not waitForProcessRunning():
501
502
503
504
505
506
507
                test.fatal("Something seems to be really wrong.", "Application output:"
                           % str(output.plainText))
                return False
            else:
                test.log("Application seems to be started without hooking-into.")
        else:
            test.warning("Could not attach to '%s' - using fallback of pushing STOP inside Creator." % executable)
508
509
510
511
512
513
        __closeSubprocessByPushingStop__(sType)
        return False
    if function == None:
        if sType==SubprocessType.USER_DEFINED:
            sendEvent("QCloseEvent", "{type='%s' unnamed='1' visible='1'}" % userDefType)
        else:
514
            sendEvent("QCloseEvent", "{type='%s' unnamed='1' visible='1'}" % SubprocessType.getWindowType(sType, quickVersion))
515
516
517
518
519
520
521
522
523
524
525
526
        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)
527
    resetApplicationContextToCreator()
528
    if not (waitForProcessRunning(False) and waitFor("'exited with code' in str(output.plainText)", 10000)):
529
530
531
532
533
        test.warning("Sub-process seems not to have closed properly.")
        try:
            __closeSubprocessByPushingStop__(sType)
        except:
            pass
534
535
536
        if (platform.system() in ('Microsoft', 'Windows') and
            'Listening on port %d for incoming connectionsdone' % port not in str(output.plainText)):
            checkForStillRunningQmlExecutable([executable + ".exe"])
537
538
539
540
541
542
543
544
545
    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)
546
547
548
549
550

# helper that examines the text (coming from the create project wizard)
# to figure out which available targets we have
# Simulator must be handled in a special way, because this depends on the
# configured Qt versions and Toolchains and cannot be looked up the same way
551
# if you set getAsStrings to True this function returns a list of strings instead
Christian Stenger's avatar
Christian Stenger committed
552
# of the constants defined in Targets
553
def __getSupportedPlatforms__(text, templateName, getAsStrings=False):
554
555
556
557
558
559
    reqPattern = re.compile("requires qt (?P<version>\d+\.\d+(\.\d+)?)", re.IGNORECASE)
    res = reqPattern.search(text)
    if res:
        version = res.group("version")
    else:
        version = None
560
561
562
563
    if 'Supported Platforms' in text:
        supports = text[text.find('Supported Platforms'):].split(":")[1].strip().split(" ")
        result = []
        if 'Desktop' in supports:
564
565
566
567
568
569
570
            if version == None or version < "5.0":
                result.append(Targets.DESKTOP_474_GCC)
                result.append(Targets.DESKTOP_480_GCC)
                if platform.system() in ("Linux", "Darwin"):
                    result.append(Targets.EMBEDDED_LINUX)
                elif platform.system() in ('Windows', 'Microsoft'):
                    result.append(Targets.DESKTOP_480_MSVC2010)
571
            result.extend([Targets.DESKTOP_501_DEFAULT, Targets.DESKTOP_521_DEFAULT])
572
        if 'MeeGo/Harmattan' in supports:
Christian Stenger's avatar
Christian Stenger committed
573
            result.append(Targets.HARMATTAN)
574
        if 'Maemo/Fremantle' in supports:
Christian Stenger's avatar
Christian Stenger committed
575
            result.append(Targets.MAEMO5)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
576
        if not ("BlackBerry" in templateName or re.search("custom Qt Creator plugin", text)) and (version == None or version < "5.0"):
Christian Stenger's avatar
Christian Stenger committed
577
            result.append(Targets.SIMULATOR)
578
    elif 'Platform independent' in text:
579
        # MAEMO5 and HARMATTAN could be wrong here - depends on having Madde plugin enabled or not
580
        result = [Targets.DESKTOP_474_GCC, Targets.DESKTOP_480_GCC, Targets.DESKTOP_501_DEFAULT,
581
                  Targets.DESKTOP_521_DEFAULT, Targets.MAEMO5, Targets.SIMULATOR, Targets.HARMATTAN]
Robert Loehning's avatar
Robert Loehning committed
582
        if platform.system() in ('Windows', 'Microsoft'):
583
            result.append(Targets.DESKTOP_480_MSVC2010)
584
585
586
    else:
        test.warning("Returning None (__getSupportedPlatforms__())",
                     "Parsed text: '%s'" % text)
587
588
        return None, None
    if getAsStrings:
Christian Stenger's avatar
Christian Stenger committed
589
        result = Targets.getTargetsAsStrings(result)
590
    return result, version
591
592
593
594

# copy example project (sourceExample is path to project) to temporary directory inside repository
def prepareTemplate(sourceExample):
    templateDir = os.path.abspath(tempDir() + "/template")
595
596
597
598
599
    try:
        shutil.copytree(sourceExample, templateDir)
    except:
        test.fatal("Error while copying '%s' to '%s'" % (sourceExample, templateDir))
        return None
600
    return templateDir
601
602
603
604
605
606
607

def __sortFilenamesOSDependent__(filenames):
    if platform.system() in ('Windows', 'Microsoft'):
        filenames.sort(key=str.lower)
    else:
        filenames.sort()
    return filenames
608

609
610
def __iterateChildren__(model, parent, nestingLevel=0):
    children = []
611
    for currentIndex in dumpIndices(model, parent):
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
        children.append([str(currentIndex.text), nestingLevel])
        if model.hasChildren(currentIndex):
            children.extend(__iterateChildren__(model, currentIndex, nestingLevel + 1))
    return children

# This will write the data to a file which can then be used for comparing
def __writeProjectTreeFile__(projectTree, filename):
    f = open(filename, "w+")
    f.write('"text"\t"nestinglevel"\n')
    for elem in projectTree:
        f.write('"%s"\t"%s"\n' % (elem[0], elem[1]))
    f.close()

def __getTestData__(record):
    return [testData.field(record, "text"),
            __builtin__.int(testData.field(record, "nestinglevel"))]

def compareProjectTree(rootObject, dataset):
    root = waitForObject(rootObject)
    tree = __iterateChildren__(root.model(), root)

    # __writeProjectTreeFile__(tree, dataset)

    for i, current in enumerate(map(__getTestData__, testData.dataset(dataset))):
        try:
            # Just removing everything up to the found item
            # Writing a pass would result in truly massive logs
            tree = tree[tree.index(current) + 1:]
        except ValueError:
            test.fail('Could not find "%s" with nesting level %s' % tuple(current),
                      'Line %s in dataset' % str(i + 1))
            return
    test.passes("No errors found in project tree")
645

646
def addCPlusPlusFileToCurrentProject(name, template, forceOverwrite=False, addToVCS = "<None>"):
647
648
649
650
651
    if name == None:
        test.fatal("File must have a name - got None.")
        return
    __createProjectOrFileSelectType__("  C++", template, isProject=False)
    window = "{type='Utils::FileWizardDialog' unnamed='1' visible='1'}"
652
    basePath = str(waitForObject("{type='Utils::FancyLineEdit' unnamed='1' visible='1' "
653
654
655
656
657
                                 "window=%s}" % window).text)
    lineEdit = waitForObject("{name='nameLineEdit' type='Utils::FileNameValidatingLineEdit' "
                             "visible='1' window=%s}" % window)
    replaceEditorContent(lineEdit, name)
    clickButton(waitForObject(":Next_QPushButton"))
658
    fileExistedBefore = os.path.exists(os.path.join(basePath, name))
659
    __createProjectHandleLastPage__(addToVersionControl = addToVCS)
660
    if (fileExistedBefore):
661
662
663
664
665
666
667
668
        overwriteDialog = "{type='Core::Internal::PromptOverwriteDialog' unnamed='1' visible='1'}"
        waitForObject(overwriteDialog)
        if forceOverwrite:
            buttonToClick = 'OK'
        else:
            buttonToClick = 'Cancel'
        clickButton("{text='%s' type='QPushButton' unnamed='1' visible='1' window=%s}"
                    % (buttonToClick, overwriteDialog))
669
670
671
672
673
674
675
676
677
678

def qt5SDKPath():
    if platform.system() in ('Microsoft', 'Windows'):
        return os.path.abspath("C:/Qt/Qt5.0.1/5.0.1/msvc2010")
    elif platform.system() == 'Linux':
        if __is64BitOS__():
            return os.path.expanduser("~/Qt5.0.1/5.0.1/gcc_64")
        return os.path.expanduser("~/Qt5.0.1/5.0.1/gcc")
    else:
        return os.path.expanduser("~/Qt5.0.1/5.0.1/clang_64")