summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp')
-rw-r--r--tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp367
1 files changed, 363 insertions, 4 deletions
diff --git a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp
index 26b4b7d020..6345d238e1 100644
--- a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp
+++ b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp
@@ -195,6 +195,8 @@ private slots:
void specialValueText();
void setRange_data();
void setRange();
+ void editingRanged_data();
+ void editingRanged();
void selectAndScrollWithKeys();
void backspaceKey();
@@ -294,6 +296,13 @@ private slots:
void stepModifierPressAndHold_data();
void stepModifierPressAndHold();
+
+ void springForward_data();
+ void springForward();
+
+ void stepIntoDSTGap_data();
+ void stepIntoDSTGap();
+
private:
EditorDateEdit* testWidget;
QWidget *testFocusWidget;
@@ -1303,6 +1312,120 @@ void tst_QDateTimeEdit::setRange()
}
}
+/*
+ Test that a user can input a date into a ranged QDateTimeEdit or QDateEdit
+ where a part of date is larger than the respective part of the maximum, or
+ smaller than the respective part of the minimum of the range.
+
+ This test is expected to fail unless keyboard tracking of the edit is set
+ to off. Otherwise the changed-signal would be emitted with values outside
+ of the allowed range as the user types.
+*/
+void tst_QDateTimeEdit::editingRanged_data()
+{
+ QTest::addColumn<QDate>("minDate");
+ QTest::addColumn<QTime>("minTime");
+ QTest::addColumn<QDate>("maxDate");
+ QTest::addColumn<QTime>("maxTime");
+ QTest::addColumn<QString>("userInput");
+ QTest::addColumn<QDateTime>("expected");
+
+ QTest::addRow("trivial")
+ << QDate(2010, 1, 1) << QTime(9, 0)
+ << QDate(2011, 12, 31) << QTime(16, 0)
+ << QString::fromLatin1("311220101600")
+ << QDateTime(QDate(2010, 12, 31), QTime(16, 0));
+
+ QTest::addRow("data0")
+ << QDate(2010, 12, 30) << QTime(16, 0)
+ << QDate(2011, 1, 2) << QTime(9, 0)
+ << QString::fromLatin1("311220102359")
+ << QDateTime(QDate(2010, 12, 31), QTime(23, 59));
+
+ QTest::addRow("data1")
+ << QDate(2010, 12, 30) << QTime(16, 0)
+ << QDate(2011, 1, 2) << QTime(9, 0)
+ << QString::fromLatin1("010120111823")
+ << QDateTime(QDate(2011, 1, 1), QTime(18, 23));
+
+ QTest::addRow("Out of range")
+ << QDate(2010, 12, 30) << QTime(16, 0)
+ << QDate(2011, 1, 2) << QTime(9, 0)
+ << QString::fromLatin1("090920111823")
+ << QDateTime(QDate(2011, 1, 2), QTime(9, 0));
+
+ QTest::addRow("only date")
+ << QDate(2010, 12, 30) << QTime()
+ << QDate(2011, 1, 2) << QTime()
+ << QString::fromLatin1("01012011")
+ << QDateTime(QDate(2011, 1, 1), QTime());
+}
+
+void tst_QDateTimeEdit::editingRanged()
+{
+ QFETCH(QDate, minDate);
+ QFETCH(QTime, minTime);
+ QFETCH(QDate, maxDate);
+ QFETCH(QTime, maxTime);
+ QFETCH(QString, userInput);
+ QFETCH(QDateTime, expected);
+
+ QDateTimeEdit *edit;
+ if (minTime.isValid()) {
+ edit = new QDateTimeEdit;
+ edit->setDisplayFormat("dd.MM.yyyy hh:mm");
+ edit->setDateTimeRange(QDateTime(minDate, minTime), QDateTime(maxDate, maxTime));
+ } else {
+ edit = new QDateEdit;
+ edit->setDisplayFormat("dd.MM.yyyy");
+ edit->setDateRange(minDate, maxDate);
+ }
+
+ int callCount = 0;
+ connect(edit, &QDateTimeEdit::dateTimeChanged, [&](const QDateTime &dateTime) {
+ ++callCount;
+ if (minTime.isValid()) {
+ QVERIFY(dateTime >= QDateTime(minDate, minTime));
+ QVERIFY(dateTime <= QDateTime(maxDate, maxTime));
+ } else {
+ QVERIFY(dateTime.date() >= minDate);
+ QVERIFY(dateTime.date() <= maxDate);
+ }
+ });
+
+ edit->show();
+ QApplication::setActiveWindow(edit);
+ if (!QTest::qWaitForWindowActive(edit))
+ QSKIP("Failed to make window active, aborting");
+ edit->setFocus();
+
+ // with keyboard tracking, never get a signal with an out-of-range value
+ edit->setKeyboardTracking(true);
+ QTest::keyClicks(edit, userInput);
+ QTest::keyClick(edit, Qt::Key_Return);
+ QVERIFY(callCount > 0);
+
+ // QDateTimeEdit blocks these dates from being entered - see QTBUG-65
+ QEXPECT_FAIL("data0", "Can't enter this date", Continue);
+ QEXPECT_FAIL("data1", "Can't enter this date", Continue);
+ QEXPECT_FAIL("Out of range", "Can't enter this date", Continue);
+ QEXPECT_FAIL("only date", "Can't enter this date", Continue);
+ QCOMPARE(edit->dateTime(), expected);
+
+ // reset
+ edit->clearFocus();
+ edit->setFocus();
+ callCount = 0;
+
+ edit->setKeyboardTracking(false);
+ QTest::keyClicks(edit, userInput);
+ QTest::keyClick(edit, Qt::Key_Return);
+ QCOMPARE(edit->dateTime(), expected);
+ QCOMPARE(callCount, 1);
+
+ delete edit;
+}
+
void tst_QDateTimeEdit::wrappingTime_data()
{
QTest::addColumn<bool>("startWithMin");
@@ -2303,7 +2426,7 @@ void tst_QDateTimeEdit::mousePress()
QRect rectUp = testWidget->style()->subControlRect(QStyle::CC_SpinBox, &so, QStyle::SC_SpinBoxUp, testWidget);
// Send mouseClick to center of SC_SpinBoxUp
- QTest::mouseClick(testWidget, Qt::LeftButton, 0, rectUp.center());
+ QTest::mouseClick(testWidget, Qt::LeftButton, {}, rectUp.center());
QCOMPARE(testWidget->date().year(), 2005);
}
@@ -2916,7 +3039,8 @@ void tst_QDateTimeEdit::calendarPopup()
opt.editable = true;
opt.subControls = QStyle::SC_ComboBoxArrow;
QRect rect = style->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow, testWidget);
- QTest::mouseClick(testWidget, Qt::LeftButton, 0, QPoint(rect.left()+rect.width()/2, rect.top()+rect.height()/2));
+ QTest::mouseClick(testWidget, Qt::LeftButton, {},
+ QPoint(rect.left() + rect.width() / 2, rect.top() + rect.height() / 2));
QWidget *wid = testWidget->findChild<QWidget *>("qt_datetimedit_calendar");
QVERIFY(wid != 0);
testWidget->hide();
@@ -2928,7 +3052,8 @@ void tst_QDateTimeEdit::calendarPopup()
opt.initFrom(&timeEdit);
opt.subControls = QStyle::SC_ComboBoxArrow;
rect = style->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow, &timeEdit);
- QTest::mouseClick(&timeEdit, Qt::LeftButton, 0, QPoint(rect.left()+rect.width()/2, rect.top()+rect.height()/2));
+ QTest::mouseClick(&timeEdit, Qt::LeftButton, {},
+ QPoint(rect.left() + rect.width() / 2, rect.top() + rect.height() / 2));
QWidget *wid2 = timeEdit.findChild<QWidget *>("qt_datetimedit_calendar");
QVERIFY(!wid2);
timeEdit.hide();
@@ -2942,7 +3067,8 @@ void tst_QDateTimeEdit::calendarPopup()
opt.initFrom(&dateEdit);
opt.subControls = QStyle::SC_ComboBoxArrow;
rect = style->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow, &dateEdit);
- QTest::mouseClick(&dateEdit, Qt::LeftButton, 0, QPoint(rect.left()+rect.width()/2, rect.top()+rect.height()/2));
+ QTest::mouseClick(&dateEdit, Qt::LeftButton, {},
+ QPoint(rect.left() + rect.width() / 2, rect.top() + rect.height() / 2));
QWidget *wid3 = dateEdit.findChild<QWidget *>("qt_datetimedit_calendar");
QVERIFY(!wid3);
dateEdit.hide();
@@ -4097,6 +4223,9 @@ void tst_QDateTimeEdit::stepModifierKeys_data()
void tst_QDateTimeEdit::stepModifierKeys()
{
+ if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
+ QSKIP("Wayland: This fails. Figure out why.");
+
QFETCH(QDate, startDate);
QFETCH(int, stepModifier);
QFETCH(QDateTimeEdit::Section, section);
@@ -4198,6 +4327,9 @@ void tst_QDateTimeEdit::stepModifierButtons_data()
void tst_QDateTimeEdit::stepModifierButtons()
{
+ if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
+ QSKIP("Wayland: This fails. Figure out why.");
+
QFETCH(QStyle::SubControl, subControl);
QFETCH(int, stepModifier);
QFETCH(Qt::KeyboardModifiers, modifiers);
@@ -4285,6 +4417,9 @@ void tst_QDateTimeEdit::stepModifierPressAndHold_data()
void tst_QDateTimeEdit::stepModifierPressAndHold()
{
+ if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
+ QSKIP("Wayland: This fails. Figure out why.");
+
QFETCH(QStyle::SubControl, subControl);
QFETCH(int, stepModifier);
QFETCH(Qt::KeyboardModifiers, modifiers);
@@ -4325,5 +4460,229 @@ void tst_QDateTimeEdit::stepModifierPressAndHold()
QCOMPARE(value.toDate(), expectedDate);
}
+/*
+ The following tests verify correct handling of the spring forward gap; which
+ hour is skipped, and on which day, depends on the local time zone. We try to
+ make it reasonably robust by discovering the first day of spring in a given
+ year, but we won't try to handle every situation.
+
+ If this function returns an invalid QDateTime, then the tests should be skipped.
+*/
+static QDateTime findSpring(int year, const QTimeZone &timeZone)
+{
+ if (!timeZone.hasTransitions())
+ return QDateTime();
+
+ // Southern hemisphere spring is after midsummer
+ const QDateTime midSummer = QDate(year, 6, 21).startOfDay();
+ const QTimeZone::OffsetData transition =
+ midSummer.isDaylightTime() ? timeZone.previousTransition(midSummer)
+ : timeZone.nextTransition(midSummer);
+ const QDateTime spring = transition.atUtc.toLocalTime();
+ // there might have been DST at some point, but not in the year we care about
+ if (spring.date().year() != year || !spring.isDaylightTime())
+ return QDateTime();
+
+ return spring;
+};
+
+/*!
+ Test that typing in a time that is invalid due to spring forward gap
+ produces reasonable results.
+*/
+void tst_QDateTimeEdit::springForward_data()
+{
+ QTest::addColumn<QDateTime>("start");
+ QTest::addColumn<QAbstractSpinBox::CorrectionMode>("correctionMode");
+ QTest::addColumn<QTime>("inputTime");
+ QTest::addColumn<QDateTime>("expected");
+
+ const QTimeZone timeZone = QTimeZone::systemTimeZone();
+ if (!timeZone.hasDaylightTime())
+ QSKIP("This test needs to run in a timezone that observes DST!");
+
+ const QDateTime springTransition = findSpring(2019, timeZone);
+ if (!springTransition.isValid())
+ QSKIP("Failed to obtain valid spring forward datetime for 2019!");
+
+ const QDate springDate = springTransition.date();
+ const int gapWidth = timeZone.daylightTimeOffset(springTransition.addDays(1));
+ const QTime springGap = springTransition.time().addSecs(-gapWidth);
+ const QTime springGapMiddle = springTransition.time().addSecs(-gapWidth/2);
+
+ QTest::addRow("forward to %s, correct to previous", qPrintable(springGap.toString("hh:mm")))
+ << QDateTime(springDate, springGap.addSecs(-gapWidth))
+ << QAbstractSpinBox::CorrectToPreviousValue
+ << springGap
+ << QDateTime(springDate, springGap.addSecs(-gapWidth));
+
+ QTest::addRow("back to %s, correct to previous", qPrintable(springGap.toString("hh:mm")))
+ << springTransition
+ << QAbstractSpinBox::CorrectToPreviousValue
+ << springGap
+ << springTransition;
+
+ QTest::addRow("forward to %s, correct to nearest", qPrintable(springGap.toString("hh:mm")))
+ << QDateTime(springDate, springGap.addSecs(-gapWidth))
+ << QAbstractSpinBox::CorrectToNearestValue
+ << springGapMiddle
+ << springTransition;
+
+ QTest::addRow("back to %s, correct to nearest", qPrintable(springGap.toString("hh:mm")))
+ << springTransition
+ << QAbstractSpinBox::CorrectToNearestValue
+ << springGapMiddle
+ << springTransition;
+
+ QTest::addRow("jump to %s, correct to nearest", qPrintable(springGapMiddle.toString("hh:mm")))
+ << QDateTime(QDate(1980, 5, 10), springGap)
+ << QAbstractSpinBox::CorrectToNearestValue
+ << springGapMiddle
+ << springTransition;
+}
+
+void tst_QDateTimeEdit::springForward()
+{
+ QFETCH(QDateTime, start);
+ QFETCH(QAbstractSpinBox::CorrectionMode, correctionMode);
+ QFETCH(QTime, inputTime);
+ QFETCH(QDateTime, expected);
+
+ QDateTimeEdit edit;
+ edit.setDisplayFormat(QLatin1String("dd.MM.yyyy hh:mm"));
+ edit.setCorrectionMode(correctionMode);
+
+ // we always want to start with a valid time
+ QVERIFY(start.isValid());
+ edit.setDateTime(start);
+
+ edit.setSelectedSection(QDateTimeEdit::DaySection);
+ const QDate date = expected.date();
+ const QString day = QString::number(date.day()).rightJustified(2, QLatin1Char('0'));
+ const QString month = QString::number(date.month()).rightJustified(2, QLatin1Char('0'));
+ const QString year = QString::number(date.year());
+ const QString hour = QString::number(inputTime.hour()).rightJustified(2, QLatin1Char('0'));
+ const QString minute = QString::number(inputTime.minute()).rightJustified(2, QLatin1Char('0'));
+ QTest::keyClicks(&edit, day);
+ QTest::keyClicks(&edit, month);
+ QTest::keyClicks(&edit, year);
+ QTest::keyClicks(&edit, hour);
+ QTest::keyClicks(&edit, minute);
+ QTest::keyClick(&edit, Qt::Key_Return, {});
+
+ QCOMPARE(edit.dateTime(), expected);
+}
+
+/*!
+ Test that using the up/down spinners to modify a valid time into a time that
+ is invalid due to daylight-saving changes produces reasonable results.
+
+ 2007 is a year in which the DST transition in most tested places was not on the
+ last or first day of the month, which allows us to test the various steps.
+*/
+void tst_QDateTimeEdit::stepIntoDSTGap_data()
+{
+ QTest::addColumn<QDateTime>("start");
+ QTest::addColumn<QDateTimeEdit::Section>("section");
+ QTest::addColumn<int>("steps");
+ QTest::addColumn<QDateTime>("end");
+
+ const QTimeZone timeZone = QTimeZone::systemTimeZone();
+ if (!timeZone.hasDaylightTime())
+ QSKIP("This test needs to run in a timezone that observes DST!");
+
+ const QDateTime springTransition = findSpring(2007, timeZone);
+ if (!springTransition.isValid())
+ QSKIP("Failed to obtain valid spring forward datetime for 2007!");
+
+ const QDate spring = springTransition.date();
+ const int gapWidth = timeZone.daylightTimeOffset(springTransition.addDays(1));
+ const QTime springGap = springTransition.time().addSecs(-gapWidth);
+
+ // change hour
+ if (springGap.hour() != 0) {
+ QTest::addRow("hour up into %s gap", qPrintable(springGap.toString("hh:mm")))
+ << QDateTime(spring, springGap.addSecs(-3600))
+ << QDateTimeEdit::HourSection
+ << +1
+ << springTransition;
+
+ // 3:00:10 into 2:00:10 should get us to 1:00:10
+ QTest::addRow("hour down into %s gap", qPrintable(springGap.toString("hh:mm")))
+ << QDateTime(spring, springGap.addSecs(3610))
+ << QDateTimeEdit::HourSection
+ << -1
+ << QDateTime(spring, springGap.addSecs(-3590));
+ }
+
+ // change day
+ if (spring.day() != 1) {
+ // today's 2:05 is tomorrow's 3:05
+ QTest::addRow("day up into %s gap", qPrintable(springGap.toString("hh:mm")))
+ << QDateTime(spring.addDays(-1), springGap.addSecs(300))
+ << QDateTimeEdit::DaySection
+ << +1
+ << springTransition.addSecs(300);
+ }
+
+ if (spring.day() != spring.daysInMonth()) {
+ QTest::addRow("day down into %s gap", qPrintable(springGap.toString("hh:mm")))
+ << QDateTime(spring.addDays(1), springGap)
+ << QDateTimeEdit::DaySection
+ << -1
+ << springTransition;
+ }
+
+ // 2018-03-25 - change month
+ QTest::addRow("month up into %s gap", qPrintable(springGap.toString("hh:mm")))
+ << QDateTime(spring.addMonths(-1), springGap)
+ << QDateTimeEdit::MonthSection
+ << +1
+ << springTransition;
+ QTest::addRow("month down into %s gap", qPrintable(springGap.toString("hh:mm")))
+ << QDateTime(spring.addMonths(1), springGap)
+ << QDateTimeEdit::MonthSection
+ << -1
+ << springTransition;
+
+ // 2018-03-25 - change year
+ QTest::addRow("year up into %s gap", qPrintable(springGap.toString("hh:mm")))
+ << QDateTime(spring.addYears(-1), springGap)
+ << QDateTimeEdit::YearSection
+ << +1
+ << springTransition;
+ QTest::addRow("year down into %s gap", qPrintable(springGap.toString("hh:mm")))
+ << QDateTime(spring.addYears(1), springGap)
+ << QDateTimeEdit::YearSection
+ << -1
+ << springTransition;
+}
+
+void tst_QDateTimeEdit::stepIntoDSTGap()
+{
+ QFETCH(QDateTime, start);
+ QFETCH(QDateTimeEdit::Section, section);
+ QFETCH(int, steps);
+ QFETCH(QDateTime, end);
+
+ QDateTimeEdit edit;
+ edit.setDisplayFormat(QLatin1String("dd.MM.yyyy hh:mm"));
+
+ // we always want to start with a valid time
+ QVERIFY(start.isValid());
+ edit.setDateTime(start);
+
+ edit.setSelectedSection(section);
+
+ // we want to end with a valid value
+ QVERIFY(end.isValid());
+
+ const auto stepCount = qAbs(steps);
+ for (int step = 0; step < stepCount; ++step)
+ QTest::keyClick(&edit, steps > 0 ? Qt::Key_Up : Qt::Key_Down, {});
+
+ QCOMPARE(edit.dateTime(), end);
+}
+
QTEST_MAIN(tst_QDateTimeEdit)
#include "tst_qdatetimeedit.moc"