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
32#include <qapplication.h>
33#include <qdebug.h>
34#include <qprogressbar.h>
35#include <qprogressdialog.h>
36#include <qpushbutton.h>
37#include <qlabel.h>
38#include <qpointer.h>
39#include <qthread.h>
40#include <qtranslator.h>
41
42class tst_QProgressDialog : public QObject
43{
44 Q_OBJECT
45
46private Q_SLOTS:
47 void cleanup();
48 void autoShow_data();
49 void autoShow();
50 void autoShowCtor();
51 void getSetCheck();
52 void task198202();
53 void QTBUG_31046();
54 void settingCustomWidgets();
55 void i18n();
56 void setValueReentrancyGuard();
57};
58
59void tst_QProgressDialog::cleanup()
60{
61 QVERIFY(QApplication::topLevelWindows().empty());
62}
63
64void tst_QProgressDialog::autoShow_data()
65{
66 QTest::addColumn<int>(name: "min");
67 QTest::addColumn<int>(name: "max");
68 QTest::addColumn<int>(name: "value"); // initial setValue call
69 QTest::addColumn<int>(name: "delay"); // then we wait for this long, and setValue(min+1)
70 QTest::addColumn<int>(name: "minDuration");
71 QTest::addColumn<bool>(name: "expectedAutoShow");
72
73 // Check that autoshow works even when not starting at 0
74 QTest::newRow(dataTag: "50_to_100_slow_shown") << 50 << 100 << 50 << 100 << 100 << true; // 50*100ms = 5s
75 QTest::newRow(dataTag: "50_to_100_fast_not_shown") << 50 << 100 << 50 << 1 << 100 << false; // 1ms is too short to even start estimating
76 QTest::newRow(dataTag: "50_to_60_high_minDuration_not_shown") << 50 << 60 << 50 << 100 << 2000 << false; // 10*100ms = 1s < 2s
77
78 // Check that setValue(0) still starts the timer as previously documented
79 QTest::newRow(dataTag: "50_to_100_slow_0_compat") << 50 << 100 << 0 << 100 << 100 << true; // 50*100ms = 5s
80 QTest::newRow(dataTag: "50_to_100_fast_0_compat") << 50 << 100 << 0 << 1 << 100 << false; // 1ms is too short to even start estimating
81 QTest::newRow(dataTag: "50_to_60_high_minDuration_0_compat") << 50 << 60 << 0 << 100 << 2000 << false; // 10*100ms = 1s < 2s
82
83 // Check the typical case of starting at 0
84 QTest::newRow(dataTag: "0_to_100_slow_shown") << 0 << 100 << 0 << 100 << 100 << true; // 100*100ms = 10s > 100ms
85 QTest::newRow(dataTag: "0_to_10_slow_shown") << 0 << 10 << 0 << 100 << 500 << true; // 10*100ms = 1s > 0.5s
86 QTest::newRow(dataTag: "0_to_10_high_minDuration_not_shown") << 0 << 10 << 0 << 100 << 2000 << false; // 10*100ms = 1s < 2s
87
88 // Check the special case of going via 0 at some point
89 QTest::newRow(dataTag: "-1_to_1_slow_shown") << -1 << 1 << -1 << 200 << 100 << true; // 1*200ms = 200ms > 100ms
90 QTest::newRow(dataTag: "-1_to_1_fast_not_shown") << -1 << 1 << -1 << 10 << 100 << false; // 10ms is too short to even start estimating
91 QTest::newRow(dataTag: "-1_to_1_high_minDuration_not_shown") << -1 << 1 << -1 << 100 << 2000 << false; // 1*100ms = 100ms < 2s
92
93}
94
95void tst_QProgressDialog::autoShow()
96{
97 QFETCH(int, min);
98 QFETCH(int, max);
99 QFETCH(int, value);
100 QFETCH(int, delay);
101 QFETCH(int, minDuration);
102 QFETCH(bool, expectedAutoShow);
103
104 QProgressDialog dlg("", "", min, max);
105 if (minDuration != dlg.minimumDuration())
106 dlg.setMinimumDuration(minDuration);
107 dlg.reset(); // cancel the timer started in the constructor,
108 // in order to test for the setValue() behavior instead
109 // See autoShowCtor() for the ctor timer check
110 dlg.setValue(value);
111 QThread::msleep(delay);
112 dlg.setValue(min+1);
113 QCOMPARE(dlg.isVisible(), expectedAutoShow);
114}
115
116void tst_QProgressDialog::autoShowCtor()
117{
118 QProgressDialog dlg;
119 QVERIFY(!dlg.isVisible());
120 QThread::msleep(dlg.minimumDuration());
121 QTRY_VERIFY(dlg.isVisible());
122}
123
124// Testing get/set functions
125void tst_QProgressDialog::getSetCheck()
126{
127 QProgressDialog obj1;
128 // bool QProgressDialog::autoReset()
129 // void QProgressDialog::setAutoReset(bool)
130 obj1.setAutoReset(false);
131 QCOMPARE(false, obj1.autoReset());
132 obj1.setAutoReset(true);
133 QCOMPARE(true, obj1.autoReset());
134
135 // bool QProgressDialog::autoClose()
136 // void QProgressDialog::setAutoClose(bool)
137 obj1.setAutoClose(false);
138 QCOMPARE(false, obj1.autoClose());
139 obj1.setAutoClose(true);
140 QCOMPARE(true, obj1.autoClose());
141
142 // int QProgressDialog::maximum()
143 // void QProgressDialog::setMaximum(int)
144 obj1.setMaximum(0);
145 QCOMPARE(0, obj1.maximum());
146 obj1.setMaximum(INT_MIN);
147 QCOMPARE(INT_MIN, obj1.maximum());
148 obj1.setMaximum(INT_MAX);
149 QCOMPARE(INT_MAX, obj1.maximum());
150
151 // int QProgressDialog::minimum()
152 // void QProgressDialog::setMinimum(int)
153 obj1.setMinimum(0);
154 QCOMPARE(0, obj1.minimum());
155 obj1.setMinimum(INT_MIN);
156 QCOMPARE(INT_MIN, obj1.minimum());
157 obj1.setMinimum(INT_MAX);
158 QCOMPARE(INT_MAX, obj1.minimum());
159
160 // int QProgressDialog::value()
161 // void QProgressDialog::setValue(int)
162 obj1.setMaximum(INT_MAX);
163 obj1.setMinimum(INT_MIN);
164 obj1.setValue(0);
165 QCOMPARE(0, obj1.value());
166 obj1.setValue(INT_MIN+1);
167 QCOMPARE(INT_MIN+1, obj1.value());
168 obj1.setValue(INT_MIN);
169 QCOMPARE(INT_MIN, obj1.value());
170 obj1.setValue(INT_MAX-1);
171 QCOMPARE(INT_MAX-1, obj1.value());
172
173 obj1.setValue(INT_MAX);
174 QCOMPARE(INT_MIN, obj1.value()); // We set autoReset, the thing is reset
175
176 obj1.setAutoReset(false);
177 obj1.setValue(INT_MAX);
178 QCOMPARE(INT_MAX, obj1.value());
179 obj1.setAutoReset(true);
180
181 // int QProgressDialog::minimumDuration()
182 // void QProgressDialog::setMinimumDuration(int)
183 obj1.setMinimumDuration(0);
184 QCOMPARE(0, obj1.minimumDuration());
185 obj1.setMinimumDuration(INT_MIN);
186 QCOMPARE(INT_MIN, obj1.minimumDuration());
187 obj1.setMinimumDuration(INT_MAX);
188 QCOMPARE(INT_MAX, obj1.minimumDuration());
189}
190
191void tst_QProgressDialog::task198202()
192{
193 //should not crash
194 QProgressDialog dlg(QLatin1String("test"),QLatin1String("test"),1,10);
195 dlg.show();
196 QVERIFY(QTest::qWaitForWindowExposed(&dlg));
197 int futureHeight = dlg.sizeHint().height() - dlg.findChild<QLabel*>()->sizeHint().height();
198 dlg.setLabel(0);
199 QTest::ignoreMessage(type: QtWarningMsg, message: "QProgressDialog::setBar: Cannot set a null progress bar");
200 dlg.setBar(0);
201 QTRY_COMPARE(dlg.sizeHint().height(), futureHeight);
202}
203
204void tst_QProgressDialog::QTBUG_31046()
205{
206 QProgressDialog dlg("", "", 50, 60);
207 dlg.setValue(0);
208 QThread::msleep(200);
209 dlg.setValue(50);
210 QCOMPARE(50, dlg.value());
211}
212
213void tst_QProgressDialog::settingCustomWidgets()
214{
215 QPointer<QLabel> l = new QLabel;
216 QPointer<QPushButton> btn = new QPushButton;
217 QPointer<QProgressBar> bar = new QProgressBar;
218 QVERIFY(!l->parent());
219 QVERIFY(!btn->parent());
220 QVERIFY(!bar->parent());
221
222 {
223 QProgressDialog dlg;
224
225 QVERIFY(!dlg.isAncestorOf(l));
226 dlg.setLabel(l);
227 QVERIFY(dlg.isAncestorOf(l));
228 QTest::ignoreMessage(type: QtWarningMsg, message: "QProgressDialog::setLabel: Attempt to set the same label again");
229 dlg.setLabel(l); // setting the same widget again should not crash
230 QVERIFY(l); // and not delete the (old == new) widget
231
232 QVERIFY(!dlg.isAncestorOf(btn));
233 dlg.setCancelButton(btn);
234 QVERIFY(dlg.isAncestorOf(btn));
235 QTest::ignoreMessage(type: QtWarningMsg, message: "QProgressDialog::setCancelButton: Attempt to set the same button again");
236 dlg.setCancelButton(btn); // setting the same widget again should not crash
237 QVERIFY(btn); // and not delete the (old == new) widget
238
239 QVERIFY(!dlg.isAncestorOf(bar));
240 dlg.setBar(bar);
241 QVERIFY(dlg.isAncestorOf(bar));
242 QTest::ignoreMessage(type: QtWarningMsg, message: "QProgressDialog::setBar: Attempt to set the same progress bar again");
243 dlg.setBar(bar); // setting the same widget again should not crash
244 QVERIFY(bar); // and not delete the (old == new) widget
245 }
246
247 QVERIFY(!l);
248 QVERIFY(!btn);
249 QVERIFY(!bar);
250}
251
252class QTestTranslator : public QTranslator
253{
254 const QString m_str;
255public:
256 explicit QTestTranslator(QString str) : m_str(std::move(str)) {}
257
258 QString translate(const char *, const char *sourceText, const char *, int) const override
259 { return m_str + sourceText + m_str; }
260
261 bool isEmpty() const override { return false; }
262};
263
264template <typename Translator>
265class QTranslatorGuard {
266 Translator t;
267public:
268 template <typename Arg>
269 explicit QTranslatorGuard(Arg a) : t(std::move(a))
270 { qApp->installTranslator(messageFile: &t); }
271 ~QTranslatorGuard()
272 { qApp->removeTranslator(messageFile: &t); }
273};
274
275void tst_QProgressDialog::i18n()
276{
277 QProgressDialog dlg;
278 QPushButton *btn = dlg.findChild<QPushButton*>();
279 QVERIFY(btn);
280 const QString xxx = QStringLiteral("xxx");
281 {
282 QTranslatorGuard<QTestTranslator> guard(xxx);
283 {
284 QPushButton *btn = dlg.findChild<QPushButton*>();
285 QVERIFY(btn);
286 QTRY_COMPARE(btn->text(), QProgressDialog::tr("Cancel"));
287 QVERIFY(btn->text().startsWith(xxx));
288 }
289 }
290 QVERIFY(btn);
291 QTRY_COMPARE(btn->text(), QProgressDialog::tr("Cancel"));
292 QVERIFY(!btn->text().startsWith(xxx));
293}
294
295void tst_QProgressDialog::setValueReentrancyGuard()
296{
297 // Tests setValue() of window modal QProgressBar with
298 // Qt::QueuedConnection:
299 // This test crashes with a stack overflow if the boolean
300 // guard "processingEvents" that prevents reentranct calls
301 // to QCoreApplication::processEvents() within setValue()
302 // has not been implemented
303
304 constexpr int steps = 100; // Should be at least 50 to test for crash
305
306 QProgressDialog dlg("Testing setValue reentrancy guard...", QString(), 0, steps);
307 dlg.setWindowModality(Qt::WindowModal);
308 dlg.setMinimumDuration(0);
309 dlg.setAutoReset(false);
310
311 // Simulate a quick work loop
312 for (int i = 0; i <= steps; ++i)
313 QMetaObject::invokeMethod(obj: &dlg, member: "setValue", type: Qt::QueuedConnection, Q_ARG(int, i));
314
315 QTRY_COMPARE(dlg.value(), steps);
316}
317
318QTEST_MAIN(tst_QProgressDialog)
319#include "tst_qprogressdialog.moc"
320

source code of qtbase/tests/auto/widgets/dialogs/qprogressdialog/tst_qprogressdialog.cpp