1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtTest module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39#include "qtestblacklist_p.h"
40#include "qtestresult_p.h"
41
42#include <QtTest/qtestcase.h>
43#include <QtCore/qbytearray.h>
44#include <QtCore/qfile.h>
45#include <QtCore/qset.h>
46#include <QtCore/qcoreapplication.h>
47#include <QtCore/qvariant.h>
48#include <QtCore/QSysInfo>
49
50#include <set>
51
52QT_BEGIN_NAMESPACE
53
54/*
55 The BLACKLIST file format is a grouped listing of keywords.
56
57 Blank lines and everything after # is simply ignored. An initial #-line
58 referring to this documentation is kind to readers. Comments can also be used
59 to indicate the reasons for ignoring particular cases.
60
61 The key "ci" applies only when run by COIN. Other keys name platforms,
62 operating systems, distributions, tool-chains or architectures; a ! prefix
63 reverses what it checks. A version, joined to a key (at present, only for
64 distributions and for msvc) with a hyphen, limits the key to the specific
65 version. A keyword line matches if every key on it applies to the present
66 run. Successive lines are alternate conditions for ignoring a test.
67
68 Ungrouped lines at the beginning of a file apply to the whole testcase.
69 A group starts with a [square-bracketed] identification of a test function,
70 optionally with (after a colon, the name of) a specific data set, to ignore.
71 Subsequent lines give conditions for ignoring this test.
72
73 # See qtbase/src/testlib/qtestblacklist.cpp for format
74 # Test doesn't work on QNX at all
75 qnx
76
77 # QTBUG-12345
78 [testFunction]
79 linux
80 windows 64bit
81
82 # Flaky in COIN on macOS, not reproducible by developers
83 [testSlowly]
84 ci osx
85
86 # Needs basic C++11 support
87 [testfunction2:testData]
88 msvc-2010
89
90 Keys are lower-case. Distribution name and version are supported if
91 QSysInfo's productType() and productVersion() return them. Keys can be
92 added via the space-separated QTEST_ENVIRONMENT environment variable.
93
94 The other known keys are listed below:
95*/
96
97static QSet<QByteArray> keywords()
98{
99 // this list can be extended with new keywords as required
100 QSet<QByteArray> set = QSet<QByteArray>()
101 << "*"
102#ifdef Q_OS_LINUX
103 << "linux"
104#endif
105#ifdef Q_OS_OSX
106 << "osx"
107 << "macos"
108#endif
109#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
110 << "windows"
111#endif
112#ifdef Q_OS_IOS
113 << "ios"
114#endif
115#ifdef Q_OS_TVOS
116 << "tvos"
117#endif
118#ifdef Q_OS_WATCHOS
119 << "watchos"
120#endif
121#ifdef Q_OS_ANDROID
122 << "android"
123#endif
124#ifdef Q_OS_QNX
125 << "qnx"
126#endif
127#ifdef Q_OS_WINRT
128 << "winrt"
129#endif
130
131#if QT_POINTER_SIZE == 8
132 << "64bit"
133#else
134 << "32bit"
135#endif
136
137#ifdef Q_CC_GNU
138 << "gcc"
139#endif
140#ifdef Q_CC_CLANG
141 << "clang"
142#endif
143#ifdef Q_CC_MSVC
144 << "msvc"
145# if _MSC_VER <= 1600
146 << "msvc-2010"
147# elif _MSC_VER <= 1700
148 << "msvc-2012"
149# elif _MSC_VER <= 1800
150 << "msvc-2013"
151# elif _MSC_VER <= 1900
152 << "msvc-2015"
153# elif _MSC_VER <= 1916
154 << "msvc-2017"
155# else
156 << "msvc-2019"
157# endif
158#endif
159
160#ifdef Q_PROCESSOR_X86
161 << "x86"
162#endif
163#ifdef Q_PROCESSOR_ARM
164 << "arm"
165#endif
166
167#ifdef QT_BUILD_INTERNAL
168 << "developer-build"
169#endif
170 ;
171
172#if QT_CONFIG(properties)
173 QCoreApplication *app = QCoreApplication::instance();
174 if (app) {
175 const QVariant platformName = app->property("platformName");
176 if (platformName.isValid())
177 set << platformName.toByteArray();
178 }
179#endif
180
181 return set;
182}
183
184static QSet<QByteArray> activeConditions()
185{
186 QSet<QByteArray> result = keywords();
187
188 QByteArray distributionName = QSysInfo::productType().toLower().toUtf8();
189 QByteArray distributionRelease = QSysInfo::productVersion().toLower().toUtf8();
190 if (!distributionName.isEmpty()) {
191 if (result.find(distributionName) == result.end())
192 result.insert(distributionName);
193 if (!distributionRelease.isEmpty()) {
194 QByteArray versioned = distributionName + "-" + distributionRelease;
195 if (result.find(versioned) == result.end())
196 result.insert(versioned);
197 }
198 }
199
200 if (qEnvironmentVariableIsSet("QTEST_ENVIRONMENT")) {
201 for (const QByteArray &k : qgetenv("QTEST_ENVIRONMENT").split(' '))
202 result.insert(k);
203 }
204
205 return result;
206}
207
208static bool checkCondition(const QByteArray &condition)
209{
210 static const QSet<QByteArray> matchedConditions = activeConditions();
211 QList<QByteArray> conds = condition.split(' ');
212
213 for (QByteArray c : conds) {
214 bool result = c.startsWith('!');
215 if (result)
216 c.remove(0, 1);
217
218 result ^= matchedConditions.contains(c);
219 if (!result)
220 return false;
221 }
222 return true;
223}
224
225static bool ignoreAll = false;
226static std::set<QByteArray> *ignoredTests = nullptr;
227
228namespace QTestPrivate {
229
230void parseBlackList()
231{
232 QString filename = QTest::qFindTestData(QStringLiteral("BLACKLIST"));
233 if (filename.isEmpty())
234 return;
235 QFile ignored(filename);
236 if (!ignored.open(QIODevice::ReadOnly))
237 return;
238
239 QByteArray function;
240
241 while (!ignored.atEnd()) {
242 QByteArray line = ignored.readLine();
243 const int commentPosition = line.indexOf('#');
244 if (commentPosition >= 0)
245 line.truncate(commentPosition);
246 line = line.simplified();
247 if (line.isEmpty())
248 continue;
249 if (line.startsWith('[')) {
250 function = line.mid(1, line.length() - 2);
251 continue;
252 }
253 bool condition = checkCondition(line);
254 if (condition) {
255 if (!function.size()) {
256 ignoreAll = true;
257 } else {
258 if (!ignoredTests)
259 ignoredTests = new std::set<QByteArray>;
260 ignoredTests->insert(function);
261 }
262 }
263 }
264}
265
266void checkBlackLists(const char *slot, const char *data)
267{
268 bool ignore = ignoreAll;
269
270 if (!ignore && ignoredTests) {
271 QByteArray s = slot;
272 ignore = (ignoredTests->find(s) != ignoredTests->end());
273 if (!ignore && data) {
274 s += ':';
275 s += data;
276 ignore = (ignoredTests->find(s) != ignoredTests->end());
277 }
278 }
279
280 QTestResult::setBlacklistCurrentTest(ignore);
281}
282
283} // QTestPrivate
284
285QT_END_NAMESPACE
286