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 "qprogressbar.h" |
32 | #include <qlocale.h> |
33 | #include <qapplication.h> |
34 | #include <qstyleoption.h> |
35 | #include <qdebug.h> |
36 | #include <qtimer.h> |
37 | #include <QStyleFactory> |
38 | |
39 | class tst_QProgressBar : public QObject |
40 | { |
41 | Q_OBJECT |
42 | private slots: |
43 | void getSetCheck(); |
44 | void minMaxSameValue(); |
45 | void destroyIndeterminate(); |
46 | void text(); |
47 | void format(); |
48 | void setValueRepaint_data(); |
49 | void setValueRepaint(); |
50 | #ifndef Q_OS_MAC |
51 | void setMinMaxRepaint(); |
52 | #endif |
53 | void sizeHint(); |
54 | void formatedText_data(); |
55 | void formatedText(); |
56 | void localizedFormattedText(); |
57 | |
58 | void task245201_testChangeStyleAndDelete_data(); |
59 | void task245201_testChangeStyleAndDelete(); |
60 | }; |
61 | |
62 | // Testing get/set functions |
63 | void tst_QProgressBar::getSetCheck() |
64 | { |
65 | QProgressBar obj1; |
66 | // bool QProgressBar::invertedAppearance() |
67 | // void QProgressBar::setInvertedAppearance(bool) |
68 | obj1.setInvertedAppearance(false); |
69 | QCOMPARE(false, obj1.invertedAppearance()); |
70 | obj1.setInvertedAppearance(true); |
71 | QCOMPARE(true, obj1.invertedAppearance()); |
72 | |
73 | // int QProgressBar::minimum() |
74 | // void QProgressBar::setMinimum(int) |
75 | obj1.setMinimum(0); |
76 | QCOMPARE(0, obj1.minimum()); |
77 | obj1.setMinimum(INT_MAX); |
78 | QCOMPARE(INT_MAX, obj1.minimum()); |
79 | obj1.setMinimum(INT_MIN); |
80 | QCOMPARE(INT_MIN, obj1.minimum()); |
81 | |
82 | // int QProgressBar::maximum() |
83 | // void QProgressBar::setMaximum(int) |
84 | obj1.setMaximum(0); |
85 | QCOMPARE(0, obj1.maximum()); |
86 | obj1.setMaximum(INT_MIN); |
87 | QCOMPARE(INT_MIN, obj1.maximum()); |
88 | obj1.setMaximum(INT_MAX); |
89 | QCOMPARE(INT_MAX, obj1.maximum()); |
90 | |
91 | // int QProgressBar::value() |
92 | // void QProgressBar::setValue(int) |
93 | obj1.setValue(0); |
94 | QCOMPARE(0, obj1.value()); |
95 | obj1.setValue(INT_MIN); |
96 | QCOMPARE(INT_MIN, obj1.value()); |
97 | obj1.setValue(INT_MAX); |
98 | QCOMPARE(INT_MAX, obj1.value()); |
99 | } |
100 | |
101 | void tst_QProgressBar::minMaxSameValue() |
102 | { |
103 | QProgressBar bar; |
104 | bar.setRange(minimum: 10, maximum: 10); |
105 | bar.setValue(10); |
106 | bar.move(ax: 300, ay: 300); |
107 | bar.show(); |
108 | QVERIFY(QTest::qWaitForWindowExposed(&bar)); |
109 | } |
110 | |
111 | void tst_QProgressBar::destroyIndeterminate() |
112 | { |
113 | // This test crashes on styles that animate indeterminate / busy |
114 | // progressbars, and forget to remove the bars from internal logics when |
115 | // it's deleted. |
116 | QPointer<QProgressBar> bar = new QProgressBar; |
117 | bar->setMaximum(0); |
118 | bar->move(ax: 300, ay: 300); |
119 | bar->show(); |
120 | QVERIFY(QTest::qWaitForWindowExposed(bar.data())); |
121 | |
122 | QEventLoop loop; |
123 | QTimer::singleShot(msec: 500, receiver: bar, SLOT(deleteLater())); |
124 | QTimer::singleShot(msec: 3000, receiver: &loop, SLOT(quit())); |
125 | loop.exec(); |
126 | |
127 | QVERIFY(!bar); |
128 | } |
129 | |
130 | void tst_QProgressBar::text() |
131 | { |
132 | QProgressBar bar; |
133 | bar.setRange(minimum: 10, maximum: 10); |
134 | bar.setValue(10); |
135 | QCOMPARE(bar.text(), QString("100%" )); |
136 | bar.setRange(minimum: 0, maximum: 10); |
137 | QCOMPARE(bar.text(), QString("100%" )); |
138 | bar.setValue(5); |
139 | QCOMPARE(bar.text(), QString("50%" )); |
140 | bar.setRange(minimum: 0, maximum: 5); |
141 | bar.setValue(0); |
142 | bar.setRange(minimum: 5, maximum: 5); |
143 | QCOMPARE(bar.text(), QString()); |
144 | } |
145 | |
146 | class ProgressBar : public QProgressBar |
147 | { |
148 | void paintEvent(QPaintEvent *event) |
149 | { |
150 | repainted = true; |
151 | QProgressBar::paintEvent(event); |
152 | } |
153 | public: |
154 | bool repainted; |
155 | using QProgressBar::initStyleOption; |
156 | }; |
157 | |
158 | void tst_QProgressBar::format() |
159 | { |
160 | ProgressBar bar; |
161 | bar.setRange(minimum: 0, maximum: 10); |
162 | bar.setValue(1); |
163 | bar.move(ax: 300, ay: 300); |
164 | bar.show(); |
165 | QVERIFY(QTest::qWaitForWindowExposed(&bar)); |
166 | |
167 | bar.repainted = false; |
168 | bar.setFormat("%v of %m (%p%)" ); |
169 | QTRY_VERIFY(bar.repainted); |
170 | bar.repainted = false; |
171 | bar.setFormat("%v of %m (%p%)" ); |
172 | qApp->processEvents(); |
173 | |
174 | #if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN) |
175 | // Animated scroll bars get paint events all the time |
176 | QVERIFY(!bar.repainted); |
177 | #endif |
178 | |
179 | QCOMPARE(bar.text(), QString("1 of 10 (10%)" )); |
180 | bar.setRange(minimum: 5, maximum: 5); |
181 | bar.setValue(5); |
182 | QCOMPARE(bar.text(), QString("5 of 0 (100%)" )); |
183 | bar.setRange(minimum: 0, maximum: 5); |
184 | bar.setValue(0); |
185 | bar.setRange(minimum: 5, maximum: 5); |
186 | QCOMPARE(bar.text(), QString()); |
187 | } |
188 | |
189 | void tst_QProgressBar::setValueRepaint_data() |
190 | { |
191 | QTest::addColumn<int>(name: "min" ); |
192 | QTest::addColumn<int>(name: "max" ); |
193 | QTest::addColumn<int>(name: "increment" ); |
194 | |
195 | auto add = [](int min, int max, int increment) { |
196 | QTest::addRow(format: "%d-%d@%d" , min, max, increment) << min << max << increment; |
197 | }; |
198 | |
199 | add(0, 10, 1); |
200 | |
201 | auto add_set = [=](int min, int max, int increment) { |
202 | // check corner cases, and adjacent values (to circumvent explicit checks for corner cases) |
203 | add(min, max, increment); |
204 | add(min + 1, max, increment); |
205 | add(min, max - 1, increment); |
206 | add(min + 1, max - 1, increment); |
207 | }; |
208 | |
209 | add_set(INT_MIN, INT_MAX, INT_MAX / 50 + 1); |
210 | add_set(0, INT_MAX, INT_MAX / 100 + 1); |
211 | add_set(INT_MIN, 0, INT_MAX / 100 + 1); |
212 | } |
213 | |
214 | void tst_QProgressBar::setValueRepaint() |
215 | { |
216 | QFETCH(int, min); |
217 | QFETCH(int, max); |
218 | QFETCH(int, increment); |
219 | |
220 | ProgressBar pbar; |
221 | pbar.setMinimum(min); |
222 | pbar.setMaximum(max); |
223 | pbar.setFormat("%v" ); |
224 | pbar.move(ax: 300, ay: 300); |
225 | pbar.show(); |
226 | QVERIFY(QTest::qWaitForWindowExposed(&pbar)); |
227 | |
228 | QApplication::processEvents(); |
229 | for (qint64 i = min; i < max; i += increment) { |
230 | pbar.repainted = false; |
231 | pbar.setValue(int(i)); |
232 | QTRY_VERIFY(pbar.repainted); |
233 | } |
234 | } |
235 | |
236 | // This test is invalid on Mac, since progressbars |
237 | // are animated there |
238 | |
239 | #ifndef Q_OS_MAC |
240 | void tst_QProgressBar::setMinMaxRepaint() |
241 | { |
242 | if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland" ), cs: Qt::CaseInsensitive)) |
243 | QSKIP("Wayland: This fails. Figure out why." ); |
244 | |
245 | ProgressBar pbar; |
246 | pbar.setMinimum(0); |
247 | pbar.setMaximum(10); |
248 | pbar.setFormat("%v" ); |
249 | pbar.move(ax: 300, ay: 300); |
250 | pbar.show(); |
251 | qApp->setActiveWindow(&pbar); |
252 | QVERIFY(QTest::qWaitForWindowActive(&pbar)); |
253 | |
254 | // No repaint when setting minimum to the current minimum |
255 | pbar.repainted = false; |
256 | pbar.setMinimum(0); |
257 | QTest::qWait(ms: 50); |
258 | #ifdef Q_OS_WINRT |
259 | QEXPECT_FAIL("" , "Broken on WinRT - QTBUG-68297" , Abort); |
260 | #endif |
261 | QTRY_VERIFY(!pbar.repainted); |
262 | |
263 | // No repaint when setting maximum to the current maximum |
264 | pbar.repainted = false; |
265 | pbar.setMaximum(10); |
266 | QTest::qWait(ms: 50); |
267 | QTRY_VERIFY(!pbar.repainted); |
268 | |
269 | // Repaint when setting minimum |
270 | for (int i = 9; i >= 0; i--) { |
271 | pbar.repainted = false; |
272 | pbar.setMinimum(i); |
273 | QTRY_VERIFY(pbar.repainted); |
274 | } |
275 | |
276 | // Repaint when setting maximum |
277 | for (int i = 0; i < 10; ++i) { |
278 | pbar.repainted = false; |
279 | pbar.setMaximum(i); |
280 | QTRY_VERIFY(pbar.repainted); |
281 | } |
282 | } |
283 | #endif //Q_OS_MAC |
284 | |
285 | void tst_QProgressBar::sizeHint() |
286 | { |
287 | ProgressBar bar; |
288 | bar.setMinimum(0); |
289 | bar.setMaximum(10); |
290 | bar.setValue(5); |
291 | |
292 | //test if the sizeHint is big enough |
293 | QFontMetrics fm = bar.fontMetrics(); |
294 | QStyleOptionProgressBar opt; |
295 | bar.initStyleOption(option: &opt); |
296 | QSize size = QSize(9 * 7 + fm.horizontalAdvance(QLatin1Char('0')) * 4, fm.height() + 8); |
297 | size= bar.style()->sizeFromContents(ct: QStyle::CT_ProgressBar, opt: &opt, contentsSize: size, w: &bar); |
298 | QSize barSize = bar.sizeHint(); |
299 | QVERIFY(barSize.width() >= size.width()); |
300 | QCOMPARE(barSize.height(), size.height()); |
301 | } |
302 | |
303 | void tst_QProgressBar::formatedText_data() |
304 | { |
305 | QTest::addColumn<int>(name: "minimum" ); |
306 | QTest::addColumn<int>(name: "maximum" ); |
307 | QTest::addColumn<int>(name: "value" ); |
308 | QTest::addColumn<QString>(name: "format" ); |
309 | QTest::addColumn<QString>(name: "text" ); |
310 | |
311 | QTest::newRow(dataTag: "1" ) << -100 << 100 << 0 << QString::fromLatin1(str: " %p - %v - %m " ) << QString::fromLatin1(str: " 50 - 0 - 200 " ); |
312 | QTest::newRow(dataTag: "2" ) << -100 << 0 << -25 << QString::fromLatin1(str: " %p - %v - %m " ) << QString::fromLatin1(str: " 75 - -25 - 100 " ); |
313 | QTest::newRow(dataTag: "3" ) << 10 << 10 << 10 << QString::fromLatin1(str: " %p - %v - %m " ) << QString::fromLatin1(str: " 100 - 10 - 0 " ); |
314 | QTest::newRow(dataTag: "task152227" ) << INT_MIN << INT_MAX << 42 << QString::fromLatin1(str: " %p - %v - %m " ) << QString::fromLatin1(str: " 50 - 42 - 4294967295 " ); |
315 | } |
316 | |
317 | void tst_QProgressBar::formatedText() |
318 | { |
319 | QFETCH(int, minimum); |
320 | QFETCH(int, maximum); |
321 | QFETCH(int, value); |
322 | QFETCH(QString, format); |
323 | QFETCH(QString, text); |
324 | QProgressBar bar; |
325 | bar.setRange(minimum, maximum); |
326 | bar.setValue(value); |
327 | bar.setFormat(format); |
328 | QCOMPARE(bar.text(), text); |
329 | } |
330 | |
331 | void tst_QProgressBar::localizedFormattedText() // QTBUG-28751 |
332 | { |
333 | QProgressBar bar; |
334 | const int value = 42; |
335 | bar.setValue(value); |
336 | const QString defaultExpectedNumber = QString::number(value); |
337 | const QString defaultExpectedValue = defaultExpectedNumber + QLatin1Char('%'); |
338 | QCOMPARE(bar.text(), defaultExpectedValue); |
339 | |
340 | // Temporarily switch to Egyptian, which has a different percent sign and number formatting |
341 | QLocale egypt(QLocale::Arabic, QLocale::Egypt); |
342 | bar.setLocale(egypt); |
343 | const QString egyptianExpectedNumber = egypt.toString(i: value); |
344 | const QString egyptianExpectedValue = egyptianExpectedNumber + egypt.percent(); |
345 | if (egyptianExpectedValue == defaultExpectedValue) |
346 | QSKIP("Egyptian locale does not work on this system." ); |
347 | QCOMPARE(bar.text(), egyptianExpectedValue); |
348 | |
349 | bar.setLocale(QLocale()); |
350 | QCOMPARE(bar.text(), defaultExpectedValue); |
351 | |
352 | // Set a custom format containing only the number |
353 | bar.setFormat(QStringLiteral("%p" )); |
354 | QCOMPARE(bar.text(), defaultExpectedNumber); |
355 | bar.setLocale(egypt); |
356 | QCOMPARE(bar.text(), egyptianExpectedNumber); |
357 | |
358 | // Clear the format |
359 | bar.resetFormat(); |
360 | QCOMPARE(bar.text(), egyptianExpectedValue); |
361 | bar.setLocale(QLocale()); |
362 | QCOMPARE(bar.text(), defaultExpectedValue); |
363 | } |
364 | |
365 | void tst_QProgressBar::task245201_testChangeStyleAndDelete_data() |
366 | { |
367 | QTest::addColumn<QString>(name: "style1_str" ); |
368 | QTest::addColumn<QString>(name: "style2_str" ); |
369 | |
370 | QTest::newRow(dataTag: "fusion-windows" ) << QString::fromLatin1(str: "fusion" ) << QString::fromLatin1(str: "windows" ); |
371 | QTest::newRow(dataTag: "gtk-fusion" ) << QString::fromLatin1(str: "gtk" ) << QString::fromLatin1(str: "fusion" ); |
372 | } |
373 | |
374 | void tst_QProgressBar::task245201_testChangeStyleAndDelete() |
375 | { |
376 | QFETCH(QString, style1_str); |
377 | QFETCH(QString, style2_str); |
378 | |
379 | QProgressBar *bar = new QProgressBar; |
380 | |
381 | QStyle *style = QStyleFactory::create(style1_str); |
382 | bar->setStyle(style); |
383 | bar->move(ax: 300, ay: 300); |
384 | bar->show(); |
385 | QVERIFY(QTest::qWaitForWindowExposed(bar)); |
386 | QStyle *style2 = QStyleFactory::create(style2_str); |
387 | bar->setStyle(style2); |
388 | QTest::qWait(ms: 10); |
389 | |
390 | delete bar; |
391 | QTest::qWait(ms: 100); //should not crash |
392 | delete style; |
393 | delete style2; |
394 | } |
395 | |
396 | QTEST_MAIN(tst_QProgressBar) |
397 | #include "tst_qprogressbar.moc" |
398 | |