1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 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:LGPL$
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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 
43 #include <QtTest/QtTest>
44 #include <QString>
45 #include <QSpinBox>
46 #include <QPushButton>
47 #include <QLineEdit>
48 #include <QComboBox>
49 #include <QDialogButtonBox>
50 #include <qinputdialog.h>
51 
52 //TESTED_CLASS=
53 //TESTED_FILES=
54 
55 class tst_QInputDialog : public QObject
56 {
57     Q_OBJECT
58     QWidget *parent;
59     QDialog::DialogCode doneCode;
60     void (*testFunc)(QInputDialog *);
61     static void testFuncGetInteger(QInputDialog *dialog);
62     static void testFuncGetDouble(QInputDialog *dialog);
63     static void testFuncGetText(QInputDialog *dialog);
64     static void testFuncGetItem(QInputDialog *dialog);
65     void timerEvent(QTimerEvent *event);
66 private slots:
67     void getInteger_data();
68     void getInteger();
69     void getDouble_data();
70     void getDouble();
71     void task255502getDouble();
72     void getText_data();
73     void getText();
74     void getItem_data();
75     void getItem();
76     void task256299_getTextReturnNullStringOnRejected();
77     void inputMethodHintsOfChildWidget();
78 };
79 
stripFraction(const QString & s)80 QString stripFraction(const QString &s)
81 {
82     int period;
83     if (s.contains('.'))
84         period = s.indexOf('.');
85     else if (s.contains(','))
86         period = s.indexOf(',');
87     else
88         return s;
89     int end;
90     for (end = s.size() - 1; end > period && s[end] == '0'; --end) ;
91     return s.left(end + (end == period ? 0 : 1));
92 }
93 
normalizeNumericString(const QString & s)94 QString normalizeNumericString(const QString &s)
95 {
96     return stripFraction(s); // assumed to be sufficient
97 }
98 
_keyClick(QWidget * widget,char key)99 void _keyClick(QWidget *widget, char key)
100 {
101     QTest::keyClick(widget, key);
102 }
103 
_keyClick(QWidget * widget,Qt::Key key)104 void _keyClick(QWidget *widget, Qt::Key key)
105 {
106     QTest::keyClick(widget, key);
107 }
108 
109 template <typename SpinBoxType>
testTypingValue(SpinBoxType * sbox,QPushButton * okButton,const QString & value)110 void testTypingValue(
111     SpinBoxType* sbox, QPushButton *okButton, const QString &value)
112 {
113     sbox->selectAll();
114     for (int i = 0; i < value.size(); ++i) {
115         const QChar valChar = value[i];
116 	_keyClick(static_cast<QWidget *>(sbox), valChar.toAscii()); // ### always guaranteed to work?
117         if (sbox->hasAcceptableInput())
118             QVERIFY(okButton->isEnabled());
119         else
120             QVERIFY(!okButton->isEnabled());
121     }
122 }
123 
testTypingValue(QLineEdit * ledit,QPushButton * okButton,const QString & value)124 void testTypingValue(QLineEdit *ledit, QPushButton *okButton, const QString &value)
125 {
126     ledit->selectAll();
127     for (int i = 0; i < value.size(); ++i) {
128         const QChar valChar = value[i];
129 	_keyClick(ledit, valChar.toAscii()); // ### always guaranteed to work?
130         QVERIFY(ledit->hasAcceptableInput());
131         QVERIFY(okButton->isEnabled());
132     }
133 }
134 
135 template <typename SpinBoxType, typename ValueType>
testInvalidateAndRestore(SpinBoxType * sbox,QPushButton * okButton,QLineEdit * ledit,ValueType * =0)136 void testInvalidateAndRestore(
137     SpinBoxType* sbox, QPushButton *okButton, QLineEdit *ledit, ValueType * = 0)
138 {
139     const ValueType lastValidValue = sbox->value();
140 
141     sbox->selectAll();
142     _keyClick(ledit, Qt::Key_Delete);
143     QVERIFY(!sbox->hasAcceptableInput());
144     QVERIFY(!okButton->isEnabled());
145 
146     _keyClick(ledit, Qt::Key_Return); // should work with Qt::Key_Enter too
147     QVERIFY(sbox->hasAcceptableInput());
148     QVERIFY(okButton->isEnabled());
149     QCOMPARE(sbox->value(), lastValidValue);
150     QLocale loc;
151     QCOMPARE(
152         normalizeNumericString(ledit->text()),
153         normalizeNumericString(loc.toString(sbox->value())));
154 }
155 
156 template <typename SpinBoxType, typename ValueType>
testGetNumeric(QInputDialog * dialog,SpinBoxType * =0,ValueType * =0)157 void testGetNumeric(QInputDialog *dialog, SpinBoxType * = 0, ValueType * = 0)
158 {
159     SpinBoxType *sbox = qFindChild<SpinBoxType *>(dialog);
160     QVERIFY(sbox != 0);
161 
162     QLineEdit *ledit = qFindChild<QLineEdit *>(static_cast<QObject *>(sbox));
163     QVERIFY(ledit != 0);
164 
165     QDialogButtonBox *bbox = qFindChild<QDialogButtonBox *>(dialog);
166     QVERIFY(bbox != 0);
167     QPushButton *okButton = bbox->button(QDialogButtonBox::Ok);
168     QVERIFY(okButton != 0);
169 
170     QVERIFY(sbox->value() >= sbox->minimum());
171     QVERIFY(sbox->value() <= sbox->maximum());
172     QVERIFY(sbox->hasAcceptableInput());
173     QLocale loc;
174     QCOMPARE(
175         normalizeNumericString(ledit->selectedText()),
176         normalizeNumericString(loc.toString(sbox->value())));
177     QVERIFY(okButton->isEnabled());
178 
179     const ValueType origValue = sbox->value();
180 
181     testInvalidateAndRestore<SpinBoxType, ValueType>(sbox, okButton, ledit);
182     testTypingValue<SpinBoxType>(sbox, okButton, QString("%1").arg(sbox->minimum()));
183     testTypingValue<SpinBoxType>(sbox, okButton, QString("%1").arg(sbox->maximum()));
184     testTypingValue<SpinBoxType>(sbox, okButton, QString("%1").arg(sbox->minimum() - 1));
185     testTypingValue<SpinBoxType>(sbox, okButton, QString("%1").arg(sbox->maximum() + 1));
186     testTypingValue<SpinBoxType>(sbox, okButton, "0");
187     testTypingValue<SpinBoxType>(sbox, okButton, "0.0");
188     testTypingValue<SpinBoxType>(sbox, okButton, "foobar");
189 
190     testTypingValue<SpinBoxType>(sbox, okButton, loc.toString(origValue));
191 }
192 
testGetText(QInputDialog * dialog)193 void testGetText(QInputDialog *dialog)
194 {
195     QLineEdit *ledit = qFindChild<QLineEdit *>(dialog);
196     QVERIFY(ledit);
197 
198     QDialogButtonBox *bbox = qFindChild<QDialogButtonBox *>(dialog);
199     QVERIFY(bbox);
200     QPushButton *okButton = bbox->button(QDialogButtonBox::Ok);
201     QVERIFY(okButton);
202 
203     QVERIFY(ledit->hasAcceptableInput());
204     QCOMPARE(ledit->selectedText(), ledit->text());
205     QVERIFY(okButton->isEnabled());
206     const QString origValue = ledit->text();
207 
208     testTypingValue(ledit, okButton, origValue);
209 }
210 
testGetItem(QInputDialog * dialog)211 void testGetItem(QInputDialog *dialog)
212 {
213     QComboBox *cbox = qFindChild<QComboBox *>(dialog);
214     QVERIFY(cbox);
215 
216     QDialogButtonBox *bbox = qFindChild<QDialogButtonBox *>(dialog);
217     QVERIFY(bbox);
218     QPushButton *okButton = bbox->button(QDialogButtonBox::Ok);
219     QVERIFY(okButton);
220 
221     QVERIFY(okButton->isEnabled());
222     const int origIndex = cbox->currentIndex();
223     cbox->setCurrentIndex(origIndex - 1);
224     cbox->setCurrentIndex(origIndex);
225     QVERIFY(okButton->isEnabled());
226 }
227 
testFuncGetInteger(QInputDialog * dialog)228 void tst_QInputDialog::testFuncGetInteger(QInputDialog *dialog)
229 {
230     testGetNumeric<QSpinBox, int>(dialog);
231 }
232 
testFuncGetDouble(QInputDialog * dialog)233 void tst_QInputDialog::testFuncGetDouble(QInputDialog *dialog)
234 {
235     testGetNumeric<QDoubleSpinBox, double>(dialog);
236 }
237 
testFuncGetText(QInputDialog * dialog)238 void tst_QInputDialog::testFuncGetText(QInputDialog *dialog)
239 {
240     ::testGetText(dialog);
241 }
242 
testFuncGetItem(QInputDialog * dialog)243 void tst_QInputDialog::testFuncGetItem(QInputDialog *dialog)
244 {
245     ::testGetItem(dialog);
246 }
247 
timerEvent(QTimerEvent * event)248 void tst_QInputDialog::timerEvent(QTimerEvent *event)
249 {
250     killTimer(event->timerId());
251     QInputDialog *dialog = qFindChild<QInputDialog *>(parent);
252     QVERIFY(dialog);
253     if (testFunc)
254         testFunc(dialog);
255     dialog->done(doneCode); // cause static function call to return
256 }
257 
getInteger_data()258 void tst_QInputDialog::getInteger_data()
259 {
260     QTest::addColumn<int>("min");
261     QTest::addColumn<int>("max");
262     QTest::newRow("getInteger() - -") << -20 << -10;
263     QTest::newRow("getInteger() - 0") << -20 <<   0;
264     QTest::newRow("getInteger() - +") << -20 <<  20;
265     QTest::newRow("getInteger() 0 +") <<   0 <<  20;
266     QTest::newRow("getInteger() + +") <<  10 <<  20;
267 }
268 
getInteger()269 void tst_QInputDialog::getInteger()
270 {
271     QFETCH(int, min);
272     QFETCH(int, max);
273     QVERIFY(min < max);
274     parent = new QWidget;
275     doneCode = QDialog::Accepted;
276     testFunc = &tst_QInputDialog::testFuncGetInteger;
277     startTimer(0);
278     bool ok = false;
279     const int value = min + (max - min) / 2;
280     const int result = QInputDialog::getInteger(parent, "", "", value, min, max, 1, &ok);
281     QVERIFY(ok);
282     QCOMPARE(result, value);
283     delete parent;
284 }
285 
getDouble_data()286 void tst_QInputDialog::getDouble_data()
287 {
288     QTest::addColumn<double>("min");
289     QTest::addColumn<double>("max");
290     QTest::addColumn<int>("decimals");
291     QTest::newRow("getDouble() - - d0") << -20.0  << -10.0  << 0;
292     QTest::newRow("getDouble() - 0 d0") << -20.0  <<   0.0  << 0;
293     QTest::newRow("getDouble() - + d0") << -20.0  <<  20.0  << 0;
294     QTest::newRow("getDouble() 0 + d0") <<   0.0  <<  20.0  << 0;
295     QTest::newRow("getDouble() + + d0") <<  10.0  <<  20.0  << 0;
296     QTest::newRow("getDouble() - - d1") << -20.5  << -10.5  << 1;
297     QTest::newRow("getDouble() - 0 d1") << -20.5  <<   0.0  << 1;
298     QTest::newRow("getDouble() - + d1") << -20.5  <<  20.5  << 1;
299     QTest::newRow("getDouble() 0 + d1") <<   0.0  <<  20.5  << 1;
300     QTest::newRow("getDouble() + + d1") <<  10.5  <<  20.5  << 1;
301     QTest::newRow("getDouble() - - d2") << -20.05 << -10.05 << 2;
302     QTest::newRow("getDouble() - 0 d2") << -20.05 <<   0.0  << 2;
303     QTest::newRow("getDouble() - + d2") << -20.05 <<  20.05 << 2;
304     QTest::newRow("getDouble() 0 + d2") <<   0.0  <<  20.05 << 2;
305     QTest::newRow("getDouble() + + d2") <<  10.05 <<  20.05 << 2;
306 }
307 
getDouble()308 void tst_QInputDialog::getDouble()
309 {
310     QFETCH(double, min);
311     QFETCH(double, max);
312     QFETCH(int, decimals);
313     QVERIFY(min < max && decimals >= 0 && decimals <= 13);
314     parent = new QWidget;
315     doneCode = QDialog::Accepted;
316     testFunc = &tst_QInputDialog::testFuncGetDouble;
317     startTimer(0);
318     bool ok = false;
319     // avoid decimals due to inconsistent roundoff behavior in QInputDialog::getDouble()
320     // (at one decimal, 10.25 is rounded off to 10.2, while at two decimals, 10.025 is
321     // rounded off to 10.03)
322     const double value = static_cast<int>(min + (max - min) / 2);
323     const double result =
324         QInputDialog::getDouble(parent, "", "", value, min, max, decimals, &ok);
325     QVERIFY(ok);
326     QCOMPARE(result, value);
327     delete parent;
328 }
329 
task255502getDouble()330 void tst_QInputDialog::task255502getDouble()
331 {
332     parent = new QWidget;
333     doneCode = QDialog::Accepted;
334     testFunc = &tst_QInputDialog::testFuncGetDouble;
335     startTimer(0);
336     bool ok = false;
337     const double value = 0.001;
338     const double result =
339         QInputDialog::getDouble(parent, "", "", value, -1, 1, 4, &ok);
340     QVERIFY(ok);
341     QCOMPARE(result, value);
342     delete parent;
343 }
344 
getText_data()345 void tst_QInputDialog::getText_data()
346 {
347     QTest::addColumn<QString>("text");
348     QTest::newRow("getText() 1") << "";
349     QTest::newRow("getText() 2") << "foobar";
350     QTest::newRow("getText() 3") << "  foobar";
351     QTest::newRow("getText() 4") << "foobar  ";
352     QTest::newRow("getText() 5") << "aAzZ`1234567890-=~!@#$%^&*()_+[]{}\\|;:'\",.<>/?";
353 }
354 
getText()355 void tst_QInputDialog::getText()
356 {
357     QFETCH(QString, text);
358     parent = new QWidget;
359     doneCode = QDialog::Accepted;
360     testFunc = &tst_QInputDialog::testFuncGetText;
361     startTimer(0);
362     bool ok = false;
363     const QString result = QInputDialog::getText(parent, "", "", QLineEdit::Normal, text, &ok);
364     QVERIFY(ok);
365     QCOMPARE(result, text);
366     delete parent;
367 }
368 
task256299_getTextReturnNullStringOnRejected()369 void tst_QInputDialog::task256299_getTextReturnNullStringOnRejected()
370 {
371     parent = new QWidget;
372     doneCode = QDialog::Rejected;
373     testFunc = 0;
374     startTimer(0);
375     bool ok = true;
376     const QString result = QInputDialog::getText(parent, "", "", QLineEdit::Normal, "foobar", &ok);
377     QVERIFY(!ok);
378     QVERIFY(result.isNull());
379     delete parent;
380 }
381 
getItem_data()382 void tst_QInputDialog::getItem_data()
383 {
384     QTest::addColumn<QStringList>("items");
385     QTest::addColumn<bool>("editable");
386     QTest::newRow("getItem() 1 true") << (QStringList() << "") << true;
387     QTest::newRow("getItem() 2 true") <<
388         (QStringList() << "spring" << "summer" << "fall" << "winter") << true;
389     QTest::newRow("getItem() 1 false") << (QStringList() << "") << false;
390     QTest::newRow("getItem() 2 false") <<
391         (QStringList() << "spring" << "summer" << "fall" << "winter") << false;
392 }
393 
getItem()394 void tst_QInputDialog::getItem()
395 {
396     QFETCH(QStringList, items);
397     QFETCH(bool, editable);
398     parent = new QWidget;
399     doneCode = QDialog::Accepted;
400     testFunc = &tst_QInputDialog::testFuncGetItem;
401     startTimer(0);
402     bool ok = false;
403     const int index = items.size() / 2;
404     const QString result = QInputDialog::getItem(parent, "", "", items, index, editable, &ok);
405     QVERIFY(ok);
406     QCOMPARE(result, items[index]);
407     delete parent;
408 }
409 
inputMethodHintsOfChildWidget()410 void tst_QInputDialog::inputMethodHintsOfChildWidget()
411 {
412     QInputDialog dialog;
413     dialog.setInputMode(QInputDialog::TextInput);
414     QList<QObject *> children = dialog.children();
415     QLineEdit *editWidget = 0;
416     for (int c = 0; c < children.size(); c++) {
417         editWidget = qobject_cast<QLineEdit *>(children.at(c));
418         if (editWidget)
419             break;
420     }
421     QVERIFY(editWidget);
422     QCOMPARE(editWidget->inputMethodHints(), dialog.inputMethodHints());
423     QCOMPARE(editWidget->inputMethodHints(), Qt::ImhNone);
424     dialog.setInputMethodHints(Qt::ImhDigitsOnly);
425     QCOMPARE(editWidget->inputMethodHints(), dialog.inputMethodHints());
426     QCOMPARE(editWidget->inputMethodHints(), Qt::ImhDigitsOnly);
427 }
428 
429 QTEST_MAIN(tst_QInputDialog)
430 #include "tst_qinputdialog.moc"
431