summaryrefslogtreecommitdiffstats
path: root/scripts/packagetesting/testpackage.py
blob: cc6b61ef7a465a32dccdc840f6ccf2bbb22d3f3d (plain)
1
2
3
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
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
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/env python3
# ###########################################################################
#
# Copyright (C) 2019 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# This file is part of the Quality Assurance module of the Qt Toolkit.
#
# $QT_BEGIN_LICENSE:GPL-EXCEPT$
# 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.
#
# $QT_END_LICENSE$
#
# ############################################################################


from enum import Enum
import os
import subprocess
import sys
import shutil
import tempfile

desc = """
Qt Package testing script for testing examples of an installed package.

Run it with the environment for building (compiler and qtenv.bat on Windows)
set up. It will build a selection of examples with console logging enabled
and run them.

Supported platforms: Linux, Windows (MSVC/MinGW), Windows UWP
"""

qt_version = (0, 0, 0)
qt_mkspec = ''
qt_examples_path = ''
make_command = ''


class Deployment(Enum):
    NO_DEPLOYMENT = 1
    """The platform supports deployment, for example Win32, macOS"""
    DEPLOYMENT_SUPPORTED = 2
    """The platform requires deployment, for example WinRT"""
    DEPLOYMENT_REQUIRED = 3


def deployment():
    """Returns whether the platform requires/supports deployment"""
    if qt_mkspec.startswith('winrt'):
        return Deployment.DEPLOYMENT_REQUIRED
    if qt_mkspec.startswith('win32'):
        return Deployment.DEPLOYMENT_SUPPORTED
    return Deployment.NO_DEPLOYMENT


def deploy_tool_command(binary):
    """Returns the command to deploy an example"""
    if qt_mkspec.startswith('win32') or qt_mkspec.startswith('winrt'):
        return ['windeployqt', '--no-translations', binary]
    return []


def example_command(binary):
    """Returns the command to launch an example"""
    if qt_mkspec.startswith('winrt'):
        return ['winrtrunner', '--profile', 'appx', '--device', '0',
                '--wait', '0', '--start', binary]
    return [binary]


def qt_version_less_than(major, minor, patch):
    return qt_version < (major, minor, patch)


def examples():
    """Compile a list of examples to be tested"""
    global qt_mkspec
    result = ['widgets/mainwindows/mdi', 'charts/qmlchart',
              'multimedia/declarative-camera']
    if not qt_mkspec.startswith('winrt'):
        result.append('sensors/sensor_explorer')
    if qt_version_less_than(5, 12, 0):
        result.append('quick/demos/stocqt')
    result.extend(['location/mapviewer', 'quickcontrols/extras/gallery'])
    if not qt_mkspec.startswith('winrt') and qt_mkspec != 'win32-g++':
        result.append('webengine/quicknanobrowser')
    return result


def determine_make_command(mkspec):
    """Determine the make call (silent to emphasize warnings)"""
    if mkspec == 'win32-g++':
        return ['mingw32-make', '-s']
    if mkspec.startswith('win'):
        return ['nmake', '/s', '/l']
    return ['make',  '-s']


def query_qmake():
    """Run a qmake query to obtain version, mkspec and path"""
    global make_command, qt_examples_path, qt_mkspec, qt_version
    for line in run_process_output(['qmake', '-query']):
        print_line = True
        if line.startswith('QMAKE_XSPEC:'):
            qt_mkspec = line[12:]
        elif line.startswith('QT_VERSION:'):
            qt_version = tuple(int(v) for v in line[11:].split('.'))
        elif line.startswith('QT_INSTALL_EXAMPLES:'):
            qt_examples_path = line[20:]
        else:
            print_line = False
        if print_line:
            print(line)

    make_command = determine_make_command(qt_mkspec)


def execute(args):
    """Execute a command and print output"""
    log_string = '[{}] {}'.format(os.path.basename(os.getcwd()),
                                  ' '.join(args))
    print(log_string)
    exit_code = subprocess.call(args)
    if exit_code != 0:
        raise RuntimeError('FAIL({}): {}'.format(exit_code, log_string))


def run_process_output(args):
    """Execute a command and capture stdout"""
    std_out = subprocess.Popen(args, universal_newlines=1,
                               stdout=subprocess.PIPE).stdout
    result = [line.rstrip() for line in std_out.readlines()]
    std_out.close()
    return result


def run_example(example, test_deployment):
    """Build and run an example"""
    global qt_mkspec
    name = os.path.basename(example)
    result = False
    print('#### Running {} #####'.format(name))
    os.mkdir(name)
    os.chdir(name)
    try:
        execute(['qmake', 'CONFIG+=console',
                 os.path.join(qt_examples_path, example)])
        execute(make_command)

        binary = name if not name == 'mapviewer' else 'qml_location_mapviewer'
        if qt_mkspec.startswith('win'):
            binary += '.exe'
            # Note: After QTBUG-78445, MinGW has release/debug folders only
            # up to 5.14 and sensor_explorer has none
            if not os.path.exists(binary):
                binary = os.path.join('release', binary)
        binary = os.path.join(os.getcwd(), binary)

        do_deploy = (deployment() == Deployment.DEPLOYMENT_REQUIRED
                     or (test_deployment and deployment() != Deployment.NO_DEPLOYMENT))

        if do_deploy:
            execute(deploy_tool_command(binary))

        execute(example_command(binary))
        result = True
        print('#### ok {} #####'.format(name))
    except Exception as e:
        print('#### FAIL {} #####'.format(name), e)

    os.chdir('..')
    return result


if __name__ == "__main__":
    if sys.version_info[0] < 3:
        raise Exception('This script requires Python 3')

    query_qmake()
    print('#### Found Qt {}.{}.{}, "{}", examples at {}'.format(
          qt_version[0], qt_version[1], qt_version[2],
          qt_mkspec, qt_examples_path))
    if qt_version[0] != 5 or not qt_mkspec or not qt_examples_path:
        print('No suitable Qt version could be found')
        sys.exit(1)

    current_dir = os.getcwd()
    temp_dir = tempfile.mkdtemp(prefix='qtpkgtest{}{}{}'.format(
                                qt_version[0], qt_version[1], qt_version[2]))
    os.chdir(temp_dir)
    error_count = 0
    for index, example in enumerate(examples()):
        if not run_example(example, index == 0):
            error_count = error_count + 1
    os.chdir(current_dir)
    shutil.rmtree(temp_dir)
    print('#### Done ({} errors) #####'.format(error_count))
    sys.exit(error_count)