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 test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
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 General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | |
30 | #include <QtTest/QtTest> |
31 | #include <QImageReader> |
32 | #include <qicon.h> |
33 | #include <qiconengine.h> |
34 | #include <QtCore/QStandardPaths> |
35 | |
36 | #include <algorithm> |
37 | |
38 | |
39 | class tst_QIcon : public QObject |
40 | { |
41 | Q_OBJECT |
42 | public: |
43 | tst_QIcon(); |
44 | |
45 | private slots: |
46 | void initTestCase(); |
47 | void actualSize_data(); // test with 1 pixmap |
48 | void actualSize(); |
49 | void actualSize2_data(); // test with 2 pixmaps with different aspect ratio |
50 | void actualSize2(); |
51 | void isNull(); |
52 | void isMask(); |
53 | void swap(); |
54 | void bestMatch(); |
55 | void cacheKey(); |
56 | void detach(); |
57 | void addFile(); |
58 | void availableSizes(); |
59 | void name(); |
60 | void streamAvailableSizes_data(); |
61 | void streamAvailableSizes(); |
62 | void fromTheme(); |
63 | void fromThemeCache(); |
64 | |
65 | #ifndef QT_NO_WIDGETS |
66 | void task184901_badCache(); |
67 | #endif |
68 | void task223279_inconsistentAddFile(); |
69 | |
70 | private: |
71 | bool haveImageFormat(QByteArray const&); |
72 | |
73 | const QString m_pngImageFileName; |
74 | const QString m_pngRectFileName; |
75 | const QString m_sourceFileName; |
76 | }; |
77 | |
78 | bool tst_QIcon::haveImageFormat(QByteArray const& desiredFormat) |
79 | { |
80 | return QImageReader::supportedImageFormats().contains(t: desiredFormat); |
81 | } |
82 | |
83 | tst_QIcon::tst_QIcon() |
84 | : m_pngImageFileName(QFINDTESTDATA("image.png" )) |
85 | , m_pngRectFileName(QFINDTESTDATA("rect.png" )) |
86 | , m_sourceFileName(":/tst_qicon.cpp" ) |
87 | { |
88 | } |
89 | |
90 | void tst_QIcon::initTestCase() |
91 | { |
92 | QVERIFY(!m_pngImageFileName.isEmpty()); |
93 | QVERIFY(!m_pngRectFileName.isEmpty()); |
94 | QVERIFY(!m_sourceFileName.isEmpty()); |
95 | } |
96 | |
97 | void tst_QIcon::actualSize_data() |
98 | { |
99 | QTest::addColumn<QString>(name: "source" ); |
100 | QTest::addColumn<QSize>(name: "argument" ); |
101 | QTest::addColumn<QSize>(name: "result" ); |
102 | |
103 | // square image |
104 | QTest::newRow(dataTag: "resource0" ) << ":/image.png" << QSize(128, 128) << QSize(128, 128); |
105 | QTest::newRow(dataTag: "resource1" ) << ":/image.png" << QSize( 64, 64) << QSize( 64, 64); |
106 | QTest::newRow(dataTag: "resource2" ) << ":/image.png" << QSize( 32, 64) << QSize( 32, 32); |
107 | QTest::newRow(dataTag: "resource3" ) << ":/image.png" << QSize( 16, 64) << QSize( 16, 16); |
108 | QTest::newRow(dataTag: "resource4" ) << ":/image.png" << QSize( 16, 128) << QSize( 16, 16); |
109 | QTest::newRow(dataTag: "resource5" ) << ":/image.png" << QSize( 128, 16) << QSize( 16, 16); |
110 | QTest::newRow(dataTag: "resource6" ) << ":/image.png" << QSize( 150, 150) << QSize( 128, 128); |
111 | // rect image |
112 | QTest::newRow(dataTag: "resource7" ) << ":/rect.png" << QSize( 20, 40) << QSize( 20, 40); |
113 | QTest::newRow(dataTag: "resource8" ) << ":/rect.png" << QSize( 10, 20) << QSize( 10, 20); |
114 | QTest::newRow(dataTag: "resource9" ) << ":/rect.png" << QSize( 15, 50) << QSize( 15, 30); |
115 | QTest::newRow(dataTag: "resource10" ) << ":/rect.png" << QSize( 25, 50) << QSize( 20, 40); |
116 | |
117 | QTest::newRow(dataTag: "external0" ) << m_pngImageFileName << QSize(128, 128) << QSize(128, 128); |
118 | QTest::newRow(dataTag: "external1" ) << m_pngImageFileName << QSize( 64, 64) << QSize( 64, 64); |
119 | QTest::newRow(dataTag: "external2" ) << m_pngImageFileName << QSize( 32, 64) << QSize( 32, 32); |
120 | QTest::newRow(dataTag: "external3" ) << m_pngImageFileName << QSize( 16, 64) << QSize( 16, 16); |
121 | QTest::newRow(dataTag: "external4" ) << m_pngImageFileName << QSize( 16, 128) << QSize( 16, 16); |
122 | QTest::newRow(dataTag: "external5" ) << m_pngImageFileName << QSize(128, 16) << QSize( 16, 16); |
123 | QTest::newRow(dataTag: "external6" ) << m_pngImageFileName << QSize(150, 150) << QSize(128, 128); |
124 | // rect image |
125 | QTest::newRow(dataTag: "external7" ) << ":/rect.png" << QSize( 20, 40) << QSize( 20, 40); |
126 | QTest::newRow(dataTag: "external8" ) << ":/rect.png" << QSize( 10, 20) << QSize( 10, 20); |
127 | QTest::newRow(dataTag: "external9" ) << ":/rect.png" << QSize( 15, 50) << QSize( 15, 30); |
128 | QTest::newRow(dataTag: "external10" ) << ":/rect.png" << QSize( 25, 50) << QSize( 20, 40); |
129 | } |
130 | |
131 | void tst_QIcon::actualSize() |
132 | { |
133 | QFETCH(QString, source); |
134 | QFETCH(QSize, argument); |
135 | QFETCH(QSize, result); |
136 | |
137 | { |
138 | QPixmap pixmap(source); |
139 | QIcon icon(pixmap); |
140 | QCOMPARE(icon.actualSize(argument), result); |
141 | QCOMPARE(icon.pixmap(argument).size(), result); |
142 | } |
143 | |
144 | { |
145 | QIcon icon(source); |
146 | QCOMPARE(icon.actualSize(argument), result); |
147 | QCOMPARE(icon.pixmap(argument).size(), result); |
148 | } |
149 | } |
150 | |
151 | void tst_QIcon::actualSize2_data() |
152 | { |
153 | QTest::addColumn<QSize>(name: "argument" ); |
154 | QTest::addColumn<QSize>(name: "result" ); |
155 | |
156 | // two images - 128x128 and 20x40. Let the games begin |
157 | QTest::newRow(dataTag: "trivial1" ) << QSize( 128, 128) << QSize( 128, 128); |
158 | QTest::newRow(dataTag: "trivial2" ) << QSize( 20, 40) << QSize( 20, 40); |
159 | |
160 | // QIcon chooses the one with the smallest area to choose the pixmap |
161 | QTest::newRow(dataTag: "best1" ) << QSize( 100, 100) << QSize( 100, 100); |
162 | QTest::newRow(dataTag: "best2" ) << QSize( 20, 20) << QSize( 10, 20); |
163 | QTest::newRow(dataTag: "best3" ) << QSize( 15, 30) << QSize( 15, 30); |
164 | QTest::newRow(dataTag: "best4" ) << QSize( 5, 5) << QSize( 2, 5); |
165 | QTest::newRow(dataTag: "best5" ) << QSize( 10, 15) << QSize( 7, 15); |
166 | } |
167 | |
168 | void tst_QIcon::actualSize2() |
169 | { |
170 | QIcon icon; |
171 | icon.addPixmap(pixmap: m_pngImageFileName); |
172 | icon.addPixmap(pixmap: m_pngRectFileName); |
173 | |
174 | QFETCH(QSize, argument); |
175 | QFETCH(QSize, result); |
176 | |
177 | QCOMPARE(icon.actualSize(argument), result); |
178 | QCOMPARE(icon.pixmap(argument).size(), result); |
179 | } |
180 | |
181 | void tst_QIcon::isNull() { |
182 | // test default constructor |
183 | QIcon defaultConstructor; |
184 | QVERIFY(defaultConstructor.isNull()); |
185 | |
186 | // test copy constructor |
187 | QVERIFY(QIcon(defaultConstructor).isNull()); |
188 | |
189 | // test pixmap constructor |
190 | QPixmap nullPixmap; |
191 | QVERIFY(QIcon(nullPixmap).isNull()); |
192 | |
193 | // test string constructor with empty string |
194 | QIcon iconEmptyString = QIcon(QString()); |
195 | QVERIFY(iconEmptyString.isNull()); |
196 | QVERIFY(!iconEmptyString.actualSize(QSize(32, 32)).isValid());; |
197 | |
198 | // test string constructor with non-existing file |
199 | QIcon iconNoFile = QIcon("imagedoesnotexist" ); |
200 | QVERIFY(!iconNoFile.isNull()); |
201 | QVERIFY(!iconNoFile.actualSize(QSize(32, 32)).isValid()); |
202 | |
203 | // test string constructor with non-existing file with suffix |
204 | QIcon iconNoFileSuffix = QIcon("imagedoesnotexist.png" ); |
205 | QVERIFY(!iconNoFileSuffix.isNull()); |
206 | QVERIFY(!iconNoFileSuffix.actualSize(QSize(32, 32)).isValid()); |
207 | |
208 | // test string constructor with existing file but unsupported format |
209 | QIcon iconUnsupportedFormat = QIcon(m_sourceFileName); |
210 | QVERIFY(!iconUnsupportedFormat.isNull()); |
211 | QVERIFY(!iconUnsupportedFormat.actualSize(QSize(32, 32)).isValid()); |
212 | |
213 | // test string constructor with existing file and supported format |
214 | QIcon iconSupportedFormat = QIcon(m_pngImageFileName); |
215 | QVERIFY(!iconSupportedFormat.isNull()); |
216 | QVERIFY(iconSupportedFormat.actualSize(QSize(32, 32)).isValid()); |
217 | } |
218 | |
219 | void tst_QIcon::isMask() |
220 | { |
221 | QIcon icon; |
222 | icon.setIsMask(true); |
223 | icon.addPixmap(pixmap: QPixmap()); |
224 | QVERIFY(icon.isMask()); |
225 | |
226 | QIcon icon2; |
227 | icon2.setIsMask(true); |
228 | QVERIFY(icon2.isMask()); |
229 | icon2.setIsMask(false); |
230 | QVERIFY(!icon2.isMask()); |
231 | } |
232 | |
233 | void tst_QIcon::swap() |
234 | { |
235 | QPixmap p1(1, 1), p2(2, 2); |
236 | p1.fill(fillColor: Qt::black); |
237 | p2.fill(fillColor: Qt::black); |
238 | |
239 | QIcon i1(p1), i2(p2); |
240 | const qint64 i1k = i1.cacheKey(); |
241 | const qint64 i2k = i2.cacheKey(); |
242 | QVERIFY(i1k != i2k); |
243 | i1.swap(other&: i2); |
244 | QCOMPARE(i1.cacheKey(), i2k); |
245 | QCOMPARE(i2.cacheKey(), i1k); |
246 | } |
247 | |
248 | void tst_QIcon::bestMatch() |
249 | { |
250 | QPixmap p1(1, 1); |
251 | QPixmap p2(2, 2); |
252 | QPixmap p3(3, 3); |
253 | QPixmap p4(4, 4); |
254 | QPixmap p5(5, 5); |
255 | QPixmap p6(6, 6); |
256 | QPixmap p7(7, 7); |
257 | QPixmap p8(8, 8); |
258 | |
259 | p1.fill(fillColor: Qt::black); |
260 | p2.fill(fillColor: Qt::black); |
261 | p3.fill(fillColor: Qt::black); |
262 | p4.fill(fillColor: Qt::black); |
263 | p5.fill(fillColor: Qt::black); |
264 | p6.fill(fillColor: Qt::black); |
265 | p7.fill(fillColor: Qt::black); |
266 | p8.fill(fillColor: Qt::black); |
267 | |
268 | for (int i = 0; i < 4; ++i) { |
269 | for (int j = 0; j < 2; ++j) { |
270 | QIcon::State state = (j == 0) ? QIcon::On : QIcon::Off; |
271 | QIcon::State oppositeState = (state == QIcon::On) ? QIcon::Off |
272 | : QIcon::On; |
273 | QIcon::Mode mode; |
274 | QIcon::Mode oppositeMode; |
275 | |
276 | QIcon icon; |
277 | |
278 | switch (i) { |
279 | case 0: |
280 | default: |
281 | mode = QIcon::Normal; |
282 | oppositeMode = QIcon::Active; |
283 | break; |
284 | case 1: |
285 | mode = QIcon::Active; |
286 | oppositeMode = QIcon::Normal; |
287 | break; |
288 | case 2: |
289 | mode = QIcon::Disabled; |
290 | oppositeMode = QIcon::Selected; |
291 | break; |
292 | case 3: |
293 | mode = QIcon::Selected; |
294 | oppositeMode = QIcon::Disabled; |
295 | } |
296 | |
297 | /* |
298 | The test mirrors the code in |
299 | QPixmapIconEngine::bestMatch(), to make sure that |
300 | nobody breaks QPixmapIconEngine by mistake. Before |
301 | you change this test or the code that it tests, |
302 | please talk to the maintainer if possible. |
303 | */ |
304 | if (mode == QIcon::Disabled || mode == QIcon::Selected) { |
305 | icon.addPixmap(pixmap: p1, mode: oppositeMode, state: oppositeState); |
306 | QVERIFY(icon.pixmap(100, mode, state).size() == p1.size()); |
307 | |
308 | icon.addPixmap(pixmap: p2, mode: oppositeMode, state); |
309 | QVERIFY(icon.pixmap(100, mode, state).size() == p2.size()); |
310 | |
311 | icon.addPixmap(pixmap: p3, mode: QIcon::Active, state: oppositeState); |
312 | QVERIFY(icon.pixmap(100, mode, state).size() == p3.size()); |
313 | |
314 | icon.addPixmap(pixmap: p4, mode: QIcon::Normal, state: oppositeState); |
315 | QVERIFY(icon.pixmap(100, mode, state).size() == p4.size()); |
316 | |
317 | icon.addPixmap(pixmap: p5, mode, state: oppositeState); |
318 | QVERIFY(icon.pixmap(100, mode, state).size() == p5.size()); |
319 | |
320 | icon.addPixmap(pixmap: p6, mode: QIcon::Active, state); |
321 | QVERIFY(icon.pixmap(100, mode, state).size() == p6.size()); |
322 | |
323 | icon.addPixmap(pixmap: p7, mode: QIcon::Normal, state); |
324 | QVERIFY(icon.pixmap(100, mode, state).size() == p7.size()); |
325 | |
326 | icon.addPixmap(pixmap: p8, mode, state); |
327 | QVERIFY(icon.pixmap(100, mode, state).size() == p8.size()); |
328 | } else { |
329 | icon.addPixmap(pixmap: p1, mode: QIcon::Selected, state: oppositeState); |
330 | QVERIFY(icon.pixmap(100, mode, state).size() == p1.size()); |
331 | |
332 | icon.addPixmap(pixmap: p2, mode: QIcon::Disabled, state: oppositeState); |
333 | QVERIFY(icon.pixmap(100, mode, state).size() == p2.size()); |
334 | |
335 | icon.addPixmap(pixmap: p3, mode: QIcon::Selected, state); |
336 | QVERIFY(icon.pixmap(100, mode, state).size() == p3.size()); |
337 | |
338 | icon.addPixmap(pixmap: p4, mode: QIcon::Disabled, state); |
339 | QVERIFY(icon.pixmap(100, mode, state).size() == p4.size()); |
340 | |
341 | icon.addPixmap(pixmap: p5, mode: oppositeMode, state: oppositeState); |
342 | QVERIFY(icon.pixmap(100, mode, state).size() == p5.size()); |
343 | |
344 | icon.addPixmap(pixmap: p6, mode, state: oppositeState); |
345 | QVERIFY(icon.pixmap(100, mode, state).size() == p6.size()); |
346 | |
347 | icon.addPixmap(pixmap: p7, mode: oppositeMode, state); |
348 | QVERIFY(icon.pixmap(100, mode, state).size() == p7.size()); |
349 | |
350 | icon.addPixmap(pixmap: p8, mode, state); |
351 | QVERIFY(icon.pixmap(100, mode, state).size() == p8.size()); |
352 | } |
353 | } |
354 | } |
355 | } |
356 | |
357 | void tst_QIcon::cacheKey() |
358 | { |
359 | QIcon icon1(m_pngImageFileName); |
360 | qint64 icon1_key = icon1.cacheKey(); |
361 | QIcon icon2 = icon1; |
362 | |
363 | QCOMPARE(icon2.cacheKey(), icon1.cacheKey()); |
364 | icon2.detach(); |
365 | QVERIFY(icon2.cacheKey() != icon1.cacheKey()); |
366 | QCOMPARE(icon1.cacheKey(), icon1_key); |
367 | } |
368 | |
369 | void tst_QIcon::detach() |
370 | { |
371 | QImage img(32, 32, QImage::Format_ARGB32_Premultiplied); |
372 | img.fill(pixel: 0xffff0000); |
373 | QIcon icon1(QPixmap::fromImage(image: img)); |
374 | QIcon icon2 = icon1; |
375 | icon2.addFile(fileName: m_pngImageFileName, size: QSize(64, 64)); |
376 | |
377 | QImage img1 = icon1.pixmap(w: 64, h: 64).toImage(); |
378 | QImage img2 = icon2.pixmap(w: 64, h: 64).toImage(); |
379 | QVERIFY(img1 != img2); |
380 | |
381 | img1 = icon1.pixmap(w: 32, h: 32).toImage(); |
382 | img2 = icon2.pixmap(w: 32, h: 32).toImage(); |
383 | QCOMPARE(img1, img2); |
384 | } |
385 | |
386 | void tst_QIcon::addFile() |
387 | { |
388 | QIcon icon; |
389 | icon.addFile(fileName: QLatin1String(":/styles/commonstyle/images/standardbutton-open-16.png" )); |
390 | icon.addFile(fileName: QLatin1String(":/styles/commonstyle/images/standardbutton-open-32.png" )); |
391 | icon.addFile(fileName: QLatin1String(":/styles/commonstyle/images/standardbutton-open-128.png" )); |
392 | icon.addFile(fileName: QLatin1String(":/styles/commonstyle/images/standardbutton-save-16.png" ), size: QSize(), mode: QIcon::Selected); |
393 | icon.addFile(fileName: QLatin1String(":/styles/commonstyle/images/standardbutton-save-32.png" ), size: QSize(), mode: QIcon::Selected); |
394 | icon.addFile(fileName: QLatin1String(":/styles/commonstyle/images/standardbutton-save-128.png" ), size: QSize(), mode: QIcon::Selected); |
395 | |
396 | QVERIFY(icon.pixmap(16, QIcon::Normal).toImage() == |
397 | QPixmap(QLatin1String(":/styles/commonstyle/images/standardbutton-open-16.png" )).toImage()); |
398 | QVERIFY(icon.pixmap(32, QIcon::Normal).toImage() == |
399 | QPixmap(QLatin1String(":/styles/commonstyle/images/standardbutton-open-32.png" )).toImage()); |
400 | QVERIFY(icon.pixmap(128, QIcon::Normal).toImage() == |
401 | QPixmap(QLatin1String(":/styles/commonstyle/images/standardbutton-open-128.png" )).toImage()); |
402 | QVERIFY(icon.pixmap(16, QIcon::Selected).toImage() == |
403 | QPixmap(QLatin1String(":/styles/commonstyle/images/standardbutton-save-16.png" )).toImage()); |
404 | QVERIFY(icon.pixmap(32, QIcon::Selected).toImage() == |
405 | QPixmap(QLatin1String(":/styles/commonstyle/images/standardbutton-save-32.png" )).toImage()); |
406 | QVERIFY(icon.pixmap(128, QIcon::Selected).toImage() == |
407 | QPixmap(QLatin1String(":/styles/commonstyle/images/standardbutton-save-128.png" )).toImage()); |
408 | } |
409 | |
410 | static bool sizeLess(const QSize &a, const QSize &b) |
411 | { |
412 | return a.width() < b.width(); |
413 | } |
414 | |
415 | void tst_QIcon::availableSizes() |
416 | { |
417 | { |
418 | QIcon icon; |
419 | icon.addFile(fileName: m_pngImageFileName, size: QSize(32,32)); |
420 | icon.addFile(fileName: m_pngImageFileName, size: QSize(64,64)); |
421 | icon.addFile(fileName: m_pngImageFileName, size: QSize(128,128)); |
422 | icon.addFile(fileName: m_pngImageFileName, size: QSize(256,256), mode: QIcon::Disabled); |
423 | icon.addFile(fileName: m_pngImageFileName, size: QSize(16,16), mode: QIcon::Normal, state: QIcon::On); |
424 | |
425 | QList<QSize> availableSizes = icon.availableSizes(); |
426 | QCOMPARE(availableSizes.size(), 3); |
427 | std::sort(first: availableSizes.begin(), last: availableSizes.end(), comp: sizeLess); |
428 | QCOMPARE(availableSizes.at(0), QSize(32,32)); |
429 | QCOMPARE(availableSizes.at(1), QSize(64,64)); |
430 | QCOMPARE(availableSizes.at(2), QSize(128,128)); |
431 | |
432 | availableSizes = icon.availableSizes(mode: QIcon::Disabled); |
433 | QCOMPARE(availableSizes.size(), 1); |
434 | QCOMPARE(availableSizes.at(0), QSize(256,256)); |
435 | |
436 | availableSizes = icon.availableSizes(mode: QIcon::Normal, state: QIcon::On); |
437 | QCOMPARE(availableSizes.size(), 1); |
438 | QCOMPARE(availableSizes.at(0), QSize(16,16)); |
439 | } |
440 | |
441 | { |
442 | // we try to load an icon from resources |
443 | QIcon icon(QLatin1String(":/styles/commonstyle/images/standardbutton-open-16.png" )); |
444 | QList<QSize> availableSizes = icon.availableSizes(); |
445 | QCOMPARE(availableSizes.size(), 1); |
446 | QCOMPARE(availableSizes.at(0), QSize(16, 16)); |
447 | } |
448 | |
449 | { |
450 | // load an icon from binary data. |
451 | QPixmap pix; |
452 | QFile file(QLatin1String(":/styles/commonstyle/images/standardbutton-open-16.png" )); |
453 | QVERIFY(file.open(QIODevice::ReadOnly)); |
454 | uchar *data = file.map(offset: 0, size: file.size()); |
455 | QVERIFY(data != 0); |
456 | pix.loadFromData(buf: data, len: file.size()); |
457 | QIcon icon(pix); |
458 | |
459 | QList<QSize> availableSizes = icon.availableSizes(); |
460 | QCOMPARE(availableSizes.size(), 1); |
461 | QCOMPARE(availableSizes.at(0), QSize(16,16)); |
462 | } |
463 | |
464 | { |
465 | // there shouldn't be available sizes for invalid images! |
466 | QVERIFY(QIcon(QLatin1String("" )).availableSizes().isEmpty()); |
467 | QVERIFY(QIcon(QLatin1String("non-existing.png" )).availableSizes().isEmpty()); |
468 | } |
469 | } |
470 | |
471 | void tst_QIcon::name() |
472 | { |
473 | { |
474 | // No name if icon does not come from a theme |
475 | QIcon icon(":/image.png" ); |
476 | QString name = icon.name(); |
477 | QVERIFY(name.isEmpty()); |
478 | } |
479 | |
480 | { |
481 | // Getting the name of an icon coming from a theme should work |
482 | QString searchPath = QLatin1String(":/icons" ); |
483 | QIcon::setThemeSearchPaths(QStringList() << searchPath); |
484 | QString themeName("testtheme" ); |
485 | QIcon::setThemeName(themeName); |
486 | |
487 | QIcon icon = QIcon::fromTheme(name: "appointment-new" ); |
488 | QString name = icon.name(); |
489 | QCOMPARE(name, QLatin1String("appointment-new" )); |
490 | } |
491 | } |
492 | |
493 | void tst_QIcon::streamAvailableSizes_data() |
494 | { |
495 | QTest::addColumn<QIcon>(name: "icon" ); |
496 | |
497 | QIcon icon; |
498 | icon.addFile(fileName: ":/image.png" , size: QSize(32,32)); |
499 | QTest::newRow( dataTag: "32x32" ) << icon; |
500 | icon.addFile(fileName: ":/image.png" , size: QSize(64,64)); |
501 | QTest::newRow( dataTag: "64x64" ) << icon; |
502 | icon.addFile(fileName: ":/image.png" , size: QSize(128,128)); |
503 | QTest::newRow( dataTag: "128x128" ) << icon; |
504 | icon.addFile(fileName: ":/image.png" , size: QSize(256,256)); |
505 | QTest::newRow( dataTag: "256x256" ) << icon; |
506 | } |
507 | |
508 | void tst_QIcon::streamAvailableSizes() |
509 | { |
510 | QFETCH(QIcon, icon); |
511 | |
512 | QByteArray ba; |
513 | // write to QByteArray |
514 | { |
515 | QBuffer buffer(&ba); |
516 | buffer.open(openMode: QIODevice::WriteOnly); |
517 | QDataStream stream(&buffer); |
518 | stream << icon; |
519 | } |
520 | |
521 | // read from QByteArray |
522 | { |
523 | QBuffer buffer(&ba); |
524 | buffer.open(openMode: QIODevice::ReadOnly); |
525 | QDataStream stream(&buffer); |
526 | QIcon i; |
527 | stream >> i; |
528 | QCOMPARE(i.isNull(), icon.isNull()); |
529 | QCOMPARE(i.availableSizes(), icon.availableSizes()); |
530 | } |
531 | } |
532 | |
533 | #ifndef QT_NO_WIDGETS |
534 | void tst_QIcon::task184901_badCache() |
535 | { |
536 | QPixmap pm(m_pngImageFileName); |
537 | QIcon icon(pm); |
538 | |
539 | //the disabled icon must have an effect (grayed) |
540 | QVERIFY(icon.pixmap(32, QIcon::Normal).toImage() != icon.pixmap(32, QIcon::Disabled).toImage()); |
541 | |
542 | icon.addPixmap(pixmap: pm, mode: QIcon::Disabled); |
543 | //the disabled icon must now be the same as the normal one. |
544 | QVERIFY( icon.pixmap(32, QIcon::Normal).toImage() == icon.pixmap(32, QIcon::Disabled).toImage() ); |
545 | } |
546 | #endif |
547 | |
548 | void tst_QIcon::fromTheme() |
549 | { |
550 | QString firstSearchPath = QLatin1String(":/icons" ); |
551 | QString secondSearchPath = QLatin1String(":/second_icons" ); |
552 | QIcon::setThemeSearchPaths(QStringList() << firstSearchPath << secondSearchPath); |
553 | QCOMPARE(QIcon::themeSearchPaths().size(), 2); |
554 | QCOMPARE(firstSearchPath, QIcon::themeSearchPaths()[0]); |
555 | QCOMPARE(secondSearchPath, QIcon::themeSearchPaths()[1]); |
556 | |
557 | QString fallbackSearchPath = QStringLiteral(":/fallback_icons" ); |
558 | QIcon::setFallbackSearchPaths(QStringList() << fallbackSearchPath); |
559 | QCOMPARE(QIcon::fallbackSearchPaths().size(), 1); |
560 | QCOMPARE(fallbackSearchPath, QIcon::fallbackSearchPaths().at(0)); |
561 | |
562 | QString themeName("testtheme" ); |
563 | QIcon::setThemeName(themeName); |
564 | QCOMPARE(QIcon::themeName(), themeName); |
565 | |
566 | // Test normal icon |
567 | QIcon appointmentIcon = QIcon::fromTheme(name: "appointment-new" ); |
568 | QVERIFY(!appointmentIcon.isNull()); |
569 | QVERIFY(!appointmentIcon.availableSizes(QIcon::Normal, QIcon::Off).isEmpty()); |
570 | QVERIFY(appointmentIcon.availableSizes().contains(QSize(16, 16))); |
571 | QVERIFY(appointmentIcon.availableSizes().contains(QSize(32, 32))); |
572 | QVERIFY(appointmentIcon.availableSizes().contains(QSize(22, 22))); |
573 | |
574 | // Test fallback to less specific icon |
575 | QIcon specificAppointmentIcon = QIcon::fromTheme(name: "appointment-new-specific" ); |
576 | QVERIFY(!QIcon::hasThemeIcon("appointment-new-specific" )); |
577 | QVERIFY(QIcon::hasThemeIcon("appointment-new" )); |
578 | QCOMPARE(specificAppointmentIcon.name(), QString::fromLatin1("appointment-new" )); |
579 | QCOMPARE(specificAppointmentIcon.availableSizes(), appointmentIcon.availableSizes()); |
580 | QCOMPARE(specificAppointmentIcon.pixmap(32).cacheKey(), appointmentIcon.pixmap(32).cacheKey()); |
581 | |
582 | // Test icon from parent theme |
583 | QIcon abIcon = QIcon::fromTheme(name: "address-book-new" ); |
584 | QVERIFY(!abIcon.isNull()); |
585 | QVERIFY(QIcon::hasThemeIcon("address-book-new" )); |
586 | QVERIFY(!abIcon.availableSizes().isEmpty()); |
587 | |
588 | // Test icon from fallback path |
589 | QIcon fallbackIcon = QIcon::fromTheme(name: "red" ); |
590 | QVERIFY(!fallbackIcon.isNull()); |
591 | QVERIFY(QIcon::hasThemeIcon("red" )); |
592 | QCOMPARE(fallbackIcon.availableSizes().size(), 1); |
593 | |
594 | // Test non existing icon |
595 | QIcon noIcon = QIcon::fromTheme(name: "broken-icon" ); |
596 | QVERIFY(noIcon.isNull()); |
597 | QVERIFY(!QIcon::hasThemeIcon("broken-icon" )); |
598 | QCOMPARE(noIcon.actualSize(QSize(32, 32), QIcon::Normal, QIcon::On), QSize(0, 0)); |
599 | |
600 | // Test non existing icon with fallback |
601 | noIcon = QIcon::fromTheme(name: "broken-icon" , fallback: abIcon); |
602 | QCOMPARE(noIcon.cacheKey(), abIcon.cacheKey()); |
603 | |
604 | // Test svg-only icon |
605 | noIcon = QIcon::fromTheme(name: "svg-icon" , fallback: abIcon); |
606 | QVERIFY(!noIcon.availableSizes().isEmpty()); |
607 | |
608 | // Pixmaps should be no larger than the requested size (QTBUG-17953) |
609 | QCOMPARE(appointmentIcon.pixmap(22).size(), QSize(22, 22)); // exact |
610 | QCOMPARE(appointmentIcon.pixmap(32).size(), QSize(32, 32)); // exact |
611 | QCOMPARE(appointmentIcon.pixmap(48).size(), QSize(32, 32)); // smaller |
612 | QCOMPARE(appointmentIcon.pixmap(16).size(), QSize(16, 16)); // scaled down |
613 | QCOMPARE(appointmentIcon.pixmap(8).size(), QSize(8, 8)); // scaled down |
614 | QCOMPARE(appointmentIcon.pixmap(16).size(), QSize(16, 16)); // scaled down |
615 | |
616 | QByteArray ba; |
617 | // write to QByteArray |
618 | { |
619 | QBuffer buffer(&ba); |
620 | buffer.open(openMode: QIODevice::WriteOnly); |
621 | QDataStream stream(&buffer); |
622 | stream << abIcon; |
623 | } |
624 | |
625 | // read from QByteArray |
626 | { |
627 | QBuffer buffer(&ba); |
628 | buffer.open(openMode: QIODevice::ReadOnly); |
629 | QDataStream stream(&buffer); |
630 | QIcon i; |
631 | stream >> i; |
632 | QCOMPARE(i.isNull(), abIcon.isNull()); |
633 | QCOMPARE(i.availableSizes(), abIcon.availableSizes()); |
634 | } |
635 | |
636 | // Make sure setting the theme name clears the state |
637 | QIcon::setThemeName("" ); |
638 | abIcon = QIcon::fromTheme(name: "address-book-new" ); |
639 | QVERIFY(abIcon.isNull()); |
640 | |
641 | // Passing a full path to fromTheme is not very useful, but should work anyway |
642 | QIcon fullPathIcon = QIcon::fromTheme(name: m_pngImageFileName); |
643 | QVERIFY(!fullPathIcon.isNull()); |
644 | } |
645 | |
646 | static inline QString findGtkUpdateIconCache() |
647 | { |
648 | QString binary = QLatin1String("gtk-update-icon-cache" ); |
649 | #ifdef Q_OS_WIN |
650 | binary += QLatin1String(".exe" ); |
651 | #endif |
652 | return QStandardPaths::findExecutable(executableName: binary); |
653 | } |
654 | |
655 | void tst_QIcon::fromThemeCache() |
656 | { |
657 | QTemporaryDir dir; |
658 | QVERIFY2(dir.isValid(), qPrintable(dir.errorString())); |
659 | |
660 | QVERIFY(QDir().mkpath(dir.path() + QLatin1String("/testcache/16x16/actions" ))); |
661 | QVERIFY(QFile(QStringLiteral(":/styles/commonstyle/images/standardbutton-open-16.png" )) |
662 | .copy( dir.path() + QLatin1String("/testcache/16x16/actions/button-open.png" ))); |
663 | |
664 | { |
665 | QFile index(dir.path() + QLatin1String("/testcache/index.theme" )); |
666 | QVERIFY(index.open(QFile::WriteOnly)); |
667 | index.write(data: "[Icon Theme]\nDirectories=16x16/actions\n[16x16/actions]\nSize=16\nContext=Actions\nType=Fixed\n" ); |
668 | } |
669 | QIcon::setThemeSearchPaths(QStringList() << dir.path()); |
670 | QIcon::setThemeName("testcache" ); |
671 | |
672 | // We just created a theme with that icon, it must exist |
673 | QVERIFY(!QIcon::fromTheme("button-open" ).isNull()); |
674 | |
675 | QString cacheName = dir.path() + QLatin1String("/testcache/icon-theme.cache" ); |
676 | |
677 | // An invalid cache should not prevent lookup |
678 | { |
679 | QFile cacheFile(cacheName); |
680 | QVERIFY(cacheFile.open(QFile::WriteOnly)); |
681 | QDataStream(&cacheFile) << quint16(1) << quint16(0) << "invalid corrupted stuff in there\n" ; |
682 | } |
683 | QIcon::setThemeSearchPaths(QStringList() << dir.path()); // reload themes |
684 | QVERIFY(!QIcon::fromTheme("button-open" ).isNull()); |
685 | |
686 | // An empty cache should prevent the lookup |
687 | { |
688 | QFile cacheFile(cacheName); |
689 | QVERIFY(cacheFile.open(QFile::WriteOnly)); |
690 | QDataStream ds(&cacheFile); |
691 | ds << quint16(1) << quint16(0); // 0: version |
692 | ds << quint32(12) << quint32(20); // 4: hash offset / dir list offset |
693 | ds << quint32(1) << quint32(0xffffffff); // 12: one empty bucket |
694 | ds << quint32(1) << quint32(28); // 20: list with one element |
695 | ds.writeRawData("16x16/actions" , len: sizeof("16x16/actions" )); // 28 |
696 | } |
697 | QIcon::setThemeSearchPaths(QStringList() << dir.path()); // reload themes |
698 | QVERIFY(QIcon::fromTheme("button-open" ).isNull()); // The icon was not in the cache, it should not be found |
699 | |
700 | // Adding an icon should be changing the modification date of one sub directory which should make the cache ignored |
701 | QTest::qWait(ms: 1000); // wait enough to have a different modification time in seconds |
702 | QVERIFY(QFile(QStringLiteral(":/styles/commonstyle/images/standardbutton-save-16.png" )) |
703 | .copy(dir.path() + QLatin1String("/testcache/16x16/actions/button-save.png" ))); |
704 | QVERIFY(QFileInfo(cacheName).lastModified() < QFileInfo(dir.path() + QLatin1String("/testcache/16x16/actions" )).lastModified()); |
705 | QIcon::setThemeSearchPaths(QStringList() << dir.path()); // reload themes |
706 | QVERIFY(!QIcon::fromTheme("button-open" ).isNull()); |
707 | |
708 | // Try to run the actual gtk-update-icon-cache and make sure that icons are still found |
709 | const QString gtkUpdateIconCache = findGtkUpdateIconCache(); |
710 | if (gtkUpdateIconCache.isEmpty()) { |
711 | QIcon::setThemeSearchPaths(QStringList()); |
712 | QSKIP("gtk-update-icon-cache not run (binary not found)" ); |
713 | } |
714 | #if QT_CONFIG(process) |
715 | QProcess process; |
716 | process.start(program: gtkUpdateIconCache, |
717 | arguments: QStringList() << QStringLiteral("-f" ) << QStringLiteral("-t" ) << (dir.path() + QLatin1String("/testcache" ))); |
718 | QVERIFY2(process.waitForStarted(), qPrintable(QLatin1String("Unable to start: " ) |
719 | + gtkUpdateIconCache + QLatin1String(": " ) |
720 | + process.errorString())); |
721 | QVERIFY(process.waitForFinished()); |
722 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
723 | QCOMPARE(process.exitCode(), 0); |
724 | #endif // QT_CONFIG(process) |
725 | QVERIFY(QFileInfo(cacheName).lastModified() >= QFileInfo(dir.path() + QLatin1String("/testcache/16x16/actions" )).lastModified()); |
726 | QIcon::setThemeSearchPaths(QStringList() << dir.path()); // reload themes |
727 | QVERIFY(!QIcon::fromTheme("button-open" ).isNull()); |
728 | QVERIFY(!QIcon::fromTheme("button-open-fallback" ).isNull()); |
729 | QVERIFY(QIcon::fromTheme("notexist-fallback" ).isNull()); |
730 | } |
731 | |
732 | void tst_QIcon::task223279_inconsistentAddFile() |
733 | { |
734 | QIcon icon1; |
735 | icon1.addFile(fileName: QLatin1String(":/styles/commonstyle/images/standardbutton-open-16.png" )); |
736 | icon1.addFile(fileName: QLatin1String("IconThatDoesntExist" ), size: QSize(32, 32)); |
737 | QPixmap pm1 = icon1.pixmap(w: 32, h: 32); |
738 | |
739 | QIcon icon2; |
740 | icon2.addFile(fileName: QLatin1String(":/styles/commonstyle/images/standardbutton-open-16.png" )); |
741 | icon2.addFile(fileName: QLatin1String("IconThatDoesntExist" )); |
742 | QPixmap pm2 = icon1.pixmap(w: 32, h: 32); |
743 | |
744 | QCOMPARE(pm1.isNull(), false); |
745 | QCOMPARE(pm1.size(), QSize(16,16)); |
746 | QCOMPARE(pm1.isNull(), pm2.isNull()); |
747 | QCOMPARE(pm1.size(), pm2.size()); |
748 | } |
749 | |
750 | |
751 | QTEST_MAIN(tst_QIcon) |
752 | #include "tst_qicon.moc" |
753 | |