build_utils.py 11.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
############################################################################
#
# Copyright (C) 2016 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# 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 The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
############################################################################
25

26
27
import re;

28
# this method checks the last build (if there's one) and logs the number of errors, warnings and
29
# lines within the Issues output
30
31
32
# param expectedToFail can be used to tell this function if the build was expected to fail or not
# param createTasksFileOnError whether a tasks file should be created when building ends with errors
def checkLastBuild(expectedToFail=False, createTasksFileOnError=True):
33
34
35
36
37
38
    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
39
40
41
    ensureChecked(":Qt Creator_Issues_Core::Internal::OutputPaneToggleButton")
    model = waitForObject(":Qt Creator.Issues_QListView").model()
    buildIssues = dumpBuildIssues(model)
42
43
44
    types = map(lambda i: i[5], buildIssues)
    errors = types.count("1")
    warnings = types.count("2")
45
    gotErrors = errors != 0
Robert Loehning's avatar
Robert Loehning committed
46
    test.verify(not (gotErrors ^ expectedToFail), "Errors: %s | Warnings: %s" % (errors, warnings))
47
    # additional stuff - could be removed... or improved :)
48
    test.log("Rows inside issues: %d" % model.rowCount())
49
    if gotErrors and createTasksFileOnError:
50
        createTasksFile(buildIssues)
51
52
    return not gotErrors

53
54
# helper function to check the compilation when build wasn't successful
def checkCompile():
55
    ensureChecked(":Qt Creator_CompileOutput_Core::Internal::OutputPaneToggleButton")
56
    output = waitForObject(":Qt Creator.Compile Output_Core::OutputWindow")
57
    waitFor("len(str(output.plainText))>0",5000)
58
    if compileSucceeded(output.plainText):
59
60
        if os.getenv("SYSTEST_DEBUG") == "1":
            test.log("Compile Output:\n%s" % output.plainText)
61
62
        test.passes("Compile successful")
        return True
63
    else:
64
        test.fail("Compile Output:\n%s" % output.plainText)
65
66
67
68
69
        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)
70

71
def waitForCompile(timeout=60000):
72
    progressBarWait(10000) # avoids switching to Issues pane after checking Compile Output
73
74
75
76
77
    ensureChecked(":Qt Creator_CompileOutput_Core::Internal::OutputPaneToggleButton")
    output = waitForObject(":Qt Creator.Compile Output_Core::OutputWindow")
    if not waitFor("re.match('.*Elapsed time: (\d:)?\d{2}:\d\d\.$', str(output.plainText), re.S)", timeout):
        test.warning("Waiting for compile timed out after %d s." % (timeout / 1000))

78
79
def dumpBuildIssues(listModel):
    issueDump = []
Robert Loehning's avatar
Robert Loehning committed
80
    for index in dumpIndices(listModel):
81
82
83
84
        issueDump.extend([map(lambda role: index.data(role).toString(),
                              range(Qt.UserRole, Qt.UserRole + 6))])
    return issueDump

85
86
87
# counter for written tasks files
tasksFileCount = 0

88
89
# helper method that writes a tasks file
def createTasksFile(buildIssues):
90
91
92
    # currently used directory for tasks files
    tasksFileDir = None
    global tasksFileCount
93
    if tasksFileDir == None:
94
95
96
97
98
99
100
101
102
            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()

103
104
105
106
    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)")
107
    rows = len(buildIssues)
108
109
110
111
    if os.environ.get("SYSTEST_DEBUG") == "1":
        firstrow = 0
    else:
        firstrow = max(0, rows - 100)
112
    for issue in buildIssues[firstrow:rows]:
113
        # the following is currently a bad work-around
114
115
116
117
        fData = issue[0] # file
        lData = issue[1] # line -> linenumber or empty
        tData = issue[5] # type -> 1==error 2==warning
        dData = issue[3] # description
118
119
120
121
122
123
124
125
        if lData == "":
            lData = "-1"
        if tData == "1":
            tData = "error"
        elif tData == "2":
            tData = "warning"
        else:
            tData = "unknown"
126
        if str(fData).strip() == "" and lData == "-1" and str(dData).strip() == "":
127
            test.fatal("Found empty task.")
128
129
130
131
        file.write("%s\t%s\t%s\t%s\n" % (fData, lData, tData, dData))
    file.close()
    test.log("Written tasks file %s" % outfile)

132
133
134
# 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!)
135
# param filter is a regular expression to filter the configuration by their name
136
def iterateBuildConfigs(kitCount, filter = ""):
137
    switchViewTo(ViewConstants.PROJECTS)
138
139
140
    configs = []
    for currentKit in range(kitCount):
        switchToBuildOrRunSettingsFor(kitCount, currentKit, ProjectSettings.BUILD)
141
        model = waitForObject(":scrollArea.Edit build configuration:_QComboBox").model()
142
143
144
145
146
147
        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)])
148
149
150
151
152
153
154
    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
155
# param afterSwitchTo the ViewConstant of the mode to switch to after selecting or None
156
# returns information about the selected kit, see getQtInformationForBuildSettings
157
def selectBuildConfig(targetCount, currentTarget, configName, afterSwitchTo=ViewConstants.EDIT):
158
159
    switchViewTo(ViewConstants.PROJECTS)
    switchToBuildOrRunSettingsFor(targetCount, currentTarget, ProjectSettings.BUILD)
160
161
    if selectFromCombo(":scrollArea.Edit build configuration:_QComboBox", configName) or targetCount > 1:
        progressBarWait(30000)
162
    return getQtInformationForBuildSettings(targetCount, True, afterSwitchTo)
163

164
# This will not trigger a rebuild. If needed, caller has to do this.
165
166
def verifyBuildConfig(targetCount, currentTarget, configName, shouldBeDebug=False, enableShadowBuild=False, enableQmlDebug=False):
    qtInfo = selectBuildConfig(targetCount, currentTarget, configName, None)
167
    ensureChecked(waitForObject(":scrollArea.Details_Utils::DetailsButton"))
168
169
    ensureChecked("{name='shadowBuildCheckBox' type='QCheckBox' visible='1'}", enableShadowBuild)
    buildCfCombo = waitForObject("{type='QComboBox' name='buildConfigurationComboBox' visible='1' "
170
                                 "window=':Qt Creator_Core::Internal::MainWindow'}")
171
172
173
174
    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")
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
    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))
202
    clickButton(waitForObject(":scrollArea.Details_Utils::DetailsButton"))
203
    switchViewTo(ViewConstants.EDIT)
204
    return qtInfo
205
206
207
208
209
210
211
212

# 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:
213
214
        test.verify((re.search(".* exited with code \d+", str(appOutput)) or
                     re.search("The program has unexpectedly finished\.", str(appOutput))) and
215
216
217
218
219
220
221
222
223
224
225
226
227
228
                    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)
229
230
231
        if runAndCloseApp() == None:
            checkCompile()
            continue
232
233
        verifyBuildAndRun()
        mouseClick(waitForObject(":*Qt Creator.Clear_QToolButton"))