1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
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 http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include <QtTest/qtest.h>
38#include <QtQml/qqmlengine.h>
39#include <QtQml/qqmlcomponent.h>
40#include <QtQuickControls2/qquickstyle.h>
41#include <QtQuickControls2/private/qquickstyle_p.h>
42#include <QtQuickTemplates2/private/qquicklabel_p.h>
43#include <QtQuickTemplates2/private/qquicktheme_p.h>
44#include <QtGui/private/qguiapplication_p.h>
45
46#include "../shared/util.h"
47
48class tst_QQuickStyle : public QQmlDataTest
49{
50 Q_OBJECT
51
52private slots:
53 void cleanup();
54 void lookup();
55 void configurationFile_data();
56 void configurationFile();
57 void commandLineArgument();
58 void environmentVariables();
59 void availableStyles();
60 void qrcStylePaths_data();
61 void qrcStylePaths();
62 void qrcInQtQuickControlsStylePathEnvVar_data();
63 void qrcInQtQuickControlsStylePathEnvVar();
64
65private:
66 void loadControls();
67 void unloadControls();
68};
69
70void tst_QQuickStyle::cleanup()
71{
72 unloadControls();
73
74 QGuiApplicationPrivate::styleOverride.clear();
75 qunsetenv(varName: "QT_QUICK_CONTROLS_STYLE");
76 qunsetenv(varName: "QT_QUICK_CONTROLS_STYLE_PATH");
77 qunsetenv(varName: "QT_QUICK_CONTROLS_FALLBACK_STYLE");
78 qunsetenv(varName: "QT_QUICK_CONTROLS_CONF");
79}
80
81void tst_QQuickStyle::loadControls()
82{
83 QQmlEngine engine;
84 QQmlComponent component(&engine);
85 component.setData("import QtQuick 2.0; import QtQuick.Controls 2.1; Control { }", baseUrl: QUrl());
86
87 QScopedPointer<QObject> object(component.create());
88 QVERIFY2(!object.isNull(), qPrintable(component.errorString()));
89}
90
91void tst_QQuickStyle::unloadControls()
92{
93 qmlClearTypeRegistrations();
94}
95
96void tst_QQuickStyle::lookup()
97{
98 QVERIFY(QQuickStyle::name().isEmpty());
99 QVERIFY(!QQuickStyle::path().isEmpty());
100
101 QQuickStyle::setStyle("material");
102 QCOMPARE(QQuickStyle::name(), QString("Material"));
103 QVERIFY(!QQuickStyle::path().isEmpty());
104
105 loadControls();
106
107 // The font size for editors in the (default) Normal variant is 16.
108 // If this is wrong, the style plugin may not have been loaded.
109 QCOMPARE(QQuickTheme::instance()->font(QQuickTheme::TextArea).pixelSize(), 16);
110
111 QCOMPARE(QQuickStyle::name(), QString("Material"));
112 QVERIFY(!QQuickStyle::path().isEmpty());
113}
114
115void tst_QQuickStyle::configurationFile_data()
116{
117 QTest::addColumn<QString>(name: "fileName");
118 QTest::addColumn<QString>(name: "expectedStyle");
119 QTest::addColumn<QString>(name: "expectedPath");
120
121 QTest::newRow(dataTag: "Default") << "default.conf" << "Default" << "";
122 QTest::newRow(dataTag: "Fusion") << "fusion.conf" << "Fusion" << "";
123 QTest::newRow(dataTag: "Imagine") << "imagine.conf" << "Imagine" << "";
124 QTest::newRow(dataTag: "Material") << "material.conf" << "Material" << "";
125 QTest::newRow(dataTag: "Universal") << "universal.conf" << "Universal" << "";
126 QTest::newRow(dataTag: "Custom") << "custom.conf" << "Custom" << ":/";
127}
128
129void tst_QQuickStyle::configurationFile()
130{
131 QFETCH(QString, fileName);
132 QFETCH(QString, expectedStyle);
133 QFETCH(QString, expectedPath);
134
135 qputenv(varName: "QT_QUICK_CONTROLS_CONF", value: testFile(fileName).toLocal8Bit());
136
137 // Load a control. The import causes the configuration file to be read.
138 QQmlEngine engine;
139 QQmlComponent labelComponent(&engine);
140 labelComponent.setData("import QtQuick 2.0; import QtQuick.Controls 2.12; Label {}", baseUrl: QUrl());
141
142 QScopedPointer<QObject> object(labelComponent.create());
143 QVERIFY2(!object.isNull(), qPrintable(labelComponent.errorString()));
144
145 QCOMPARE(QQuickStyle::name(), expectedStyle);
146 if (!expectedPath.isEmpty())
147 QCOMPARE(QQuickStyle::path(), expectedPath);
148
149 // Test that fonts and palettes specified in configuration files are respected.
150 QQuickLabel *label = qobject_cast<QQuickLabel *>(object: object.data());
151 QVERIFY(label);
152 // Make it small so that there's less possibility for the default/system
153 // pixel size to match it and give us false positives.
154 QCOMPARE(label->font().pixelSize(), 3);
155 QCOMPARE(label->palette().windowText(), Qt::red);
156}
157
158void tst_QQuickStyle::commandLineArgument()
159{
160 QGuiApplicationPrivate::styleOverride = "CmdLineArgStyle";
161
162 loadControls();
163
164 QCOMPARE(QQuickStyle::name(), QString("CmdLineArgStyle"));
165}
166
167void tst_QQuickStyle::environmentVariables()
168{
169 qputenv(varName: "QT_QUICK_CONTROLS_STYLE", value: "EnvVarStyle");
170 qputenv(varName: "QT_QUICK_CONTROLS_FALLBACK_STYLE", value: "EnvVarFallbackStyle");
171 QCOMPARE(QQuickStyle::name(), QString("EnvVarStyle"));
172 QCOMPARE(QQuickStylePrivate::fallbackStyle(), QString("EnvVarFallbackStyle"));
173}
174
175void tst_QQuickStyle::availableStyles()
176{
177 QString path = QFINDTESTDATA("data");
178 QVERIFY(!path.isEmpty());
179
180 QQuickStyle::addStylePath(path);
181 QStringList paths = QQuickStylePrivate::stylePaths();
182#ifndef Q_OS_WIN
183 QVERIFY(paths.contains(path));
184#else
185 QVERIFY(paths.contains(path, Qt::CaseInsensitive));
186#endif
187
188 const QStringList styles = QQuickStyle::availableStyles();
189 QVERIFY(!styles.isEmpty());
190 QCOMPARE(styles.first(), QString("Default"));
191 QVERIFY(!styles.contains("designer"));
192
193 // QTBUG-60973
194 for (const QString &style : styles) {
195 QVERIFY2(!style.endsWith(".dSYM"), qPrintable(style));
196 }
197}
198
199void tst_QQuickStyle::qrcStylePaths_data()
200{
201 QTest::addColumn<QString>(name: "stylePath");
202 QTest::addColumn<QString>(name: "expectedStyleName");
203
204 QTest::addRow(format: "qrc:/qrcStyles1") << QString::fromLatin1(str: "qrc:/qrcStyles1") << QString::fromLatin1(str: "QrcStyle1");
205 QTest::addRow(format: ":/qrcStyles2") << QString::fromLatin1(str: ":/qrcStyles2") << QString::fromLatin1(str: "QrcStyle2");
206}
207
208void tst_QQuickStyle::qrcStylePaths()
209{
210 QFETCH(QString, stylePath);
211 QFETCH(QString, expectedStyleName);
212
213 QQuickStyle::addStylePath(path: stylePath);
214
215 const QStringList paths = QQuickStylePrivate::stylePaths();
216 QString expectedStylePath = stylePath;
217 if (expectedStylePath.startsWith(s: QLatin1String("qrc")))
218 expectedStylePath.remove(i: 0, len: 3);
219 if (!paths.contains(str: expectedStylePath)) {
220 QString message;
221 QDebug stream(&message);
222 stream.nospace() << "QQuickStylePrivate::stylePaths() doesn't contain " << expectedStylePath << ":\n" << paths;
223 QFAIL(qPrintable(message));
224 }
225
226 const QStringList styles = QQuickStyle::availableStyles();
227 QVERIFY(!styles.isEmpty());
228 if (!styles.contains(str: expectedStyleName)) {
229 QString message;
230 QDebug stream(&message);
231 stream.nospace() << "QQuickStyle::availableStyles() doesn't contain " << expectedStyleName << ":\n" << styles;
232 QFAIL(qPrintable(message));
233 }
234}
235
236void tst_QQuickStyle::qrcInQtQuickControlsStylePathEnvVar_data()
237{
238 QTest::addColumn<QString>(name: "environmentVariable");
239 QTest::addColumn<QStringList>(name: "expectedAvailableStyles");
240
241 const QChar listSeparator = QDir::listSeparator();
242 const QStringList defaultAvailableStyles = QQuickStyle::availableStyles();
243
244 {
245 QString environmentVariable;
246 QDebug stream(&environmentVariable);
247 // We use qrcStyles3 and qrcStyles4 in order to not conflict with
248 // qrcStylePaths(), since we currently have no way of clearing customStylePaths.
249 stream.noquote().nospace() << "/some/bogus/path/" << listSeparator
250 << ":/qrcStyles3";
251
252 QStringList expectedAvailableStyles = defaultAvailableStyles;
253 // We need to keep the Default style at the start of the list,
254 // as that's what availableStyles() does.
255 expectedAvailableStyles.insert(i: 1, t: QLatin1String("QrcStyle3"));
256
257 QTest::addRow(format: "%s", qPrintable(environmentVariable))
258 << environmentVariable << expectedAvailableStyles;
259 }
260
261 {
262 QString environmentVariable;
263 QDebug stream(&environmentVariable);
264 stream.noquote().nospace() << ":/qrcStyles4" << listSeparator
265 << "/some/bogus/path";
266
267 QStringList expectedAvailableStyles = defaultAvailableStyles;
268 expectedAvailableStyles.insert(i: 1, t: QLatin1String("QrcStyle4"));
269
270 QTest::addRow(format: "%s", qPrintable(environmentVariable))
271 << environmentVariable << expectedAvailableStyles;
272 }
273
274 {
275 QString environmentVariable;
276 QDebug stream(&environmentVariable);
277 stream.noquote().nospace() << ":/qrcStyles3" << listSeparator
278 << ":/qrcStyles4" << listSeparator
279 << QFINDTESTDATA("data/dummyStyles");
280
281 QStringList expectedAvailableStyles = defaultAvailableStyles;
282 expectedAvailableStyles.insert(i: 1, t: QLatin1String("DummyStyle"));
283 expectedAvailableStyles.insert(i: 1, t: QLatin1String("QrcStyle4"));
284 expectedAvailableStyles.insert(i: 1, t: QLatin1String("QrcStyle3"));
285
286 QTest::addRow(format: "%s", qPrintable(environmentVariable))
287 << environmentVariable << expectedAvailableStyles;
288 }
289
290 {
291 QString environmentVariable;
292 QDebug stream(&environmentVariable);
293 stream.noquote().nospace() << QFINDTESTDATA("data/dummyStyles") << listSeparator
294 << ":/qrcStyles3" << listSeparator
295 << ":/qrcStyles4";
296
297 QStringList expectedAvailableStyles = defaultAvailableStyles;
298 expectedAvailableStyles.insert(i: 1, t: QLatin1String("QrcStyle4"));
299 expectedAvailableStyles.insert(i: 1, t: QLatin1String("QrcStyle3"));
300 expectedAvailableStyles.insert(i: 1, t: QLatin1String("DummyStyle"));
301
302 QTest::addRow(format: "%s", qPrintable(environmentVariable))
303 << environmentVariable << expectedAvailableStyles;
304 }
305
306 {
307 QString environmentVariable;
308 QDebug stream(&environmentVariable);
309 // Same as the last row, except it adds a superfluous separator
310 // to ensure that it handles it gracefully rather than failing an assertion.
311 stream.noquote().nospace() << QFINDTESTDATA("data/dummyStyles") << listSeparator
312 << ":/qrcStyles3" << listSeparator
313 << ":/qrcStyles4" << listSeparator;
314
315 QStringList expectedAvailableStyles = defaultAvailableStyles;
316 expectedAvailableStyles.insert(i: 1, t: QLatin1String("QrcStyle4"));
317 expectedAvailableStyles.insert(i: 1, t: QLatin1String("QrcStyle3"));
318 expectedAvailableStyles.insert(i: 1, t: QLatin1String("DummyStyle"));
319
320 QTest::addRow(format: "%s", qPrintable(environmentVariable))
321 << environmentVariable << expectedAvailableStyles;
322 }
323}
324
325/*
326 Tests that qrc paths work with QT_QUICK_CONTROLS_STYLE_PATH.
327*/
328void tst_QQuickStyle::qrcInQtQuickControlsStylePathEnvVar()
329{
330 QFETCH(QString, environmentVariable);
331 QFETCH(QStringList, expectedAvailableStyles);
332
333 qputenv(varName: "QT_QUICK_CONTROLS_STYLE_PATH", value: environmentVariable.toLocal8Bit());
334
335 const QStringList availableStyles = QQuickStyle::availableStyles();
336 if (availableStyles != expectedAvailableStyles) {
337 QString failureMessage;
338 QDebug stream(&failureMessage);
339 stream << "Mismatch in actual vs expected available styles:"
340 << "\n Expected:" << expectedAvailableStyles
341 << "\n Actual:" << availableStyles;
342 QFAIL(qPrintable(failureMessage));
343 }
344}
345
346QTEST_MAIN(tst_QQuickStyle)
347
348#include "tst_qquickstyle.moc"
349

source code of qtquickcontrols2/tests/auto/qquickstyle/tst_qquickstyle.cpp