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 | |
48 | class tst_QQuickStyle : public QQmlDataTest |
49 | { |
50 | Q_OBJECT |
51 | |
52 | private 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 | |
65 | private: |
66 | void loadControls(); |
67 | void unloadControls(); |
68 | }; |
69 | |
70 | void 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 | |
81 | void 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 | |
91 | void tst_QQuickStyle::unloadControls() |
92 | { |
93 | qmlClearTypeRegistrations(); |
94 | } |
95 | |
96 | void 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 | |
115 | void 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 | |
129 | void 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 | |
158 | void tst_QQuickStyle::commandLineArgument() |
159 | { |
160 | QGuiApplicationPrivate::styleOverride = "CmdLineArgStyle" ; |
161 | |
162 | loadControls(); |
163 | |
164 | QCOMPARE(QQuickStyle::name(), QString("CmdLineArgStyle" )); |
165 | } |
166 | |
167 | void 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 | |
175 | void 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 | |
199 | void 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 | |
208 | void 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 | |
236 | void 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 | */ |
328 | void 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 | |
346 | QTEST_MAIN(tst_QQuickStyle) |
347 | |
348 | #include "tst_qquickstyle.moc" |
349 | |