File indexing completed on 2025-07-13 04:57:37

0001 /*
0002     SPDX-FileCopyrightText: 2023 Natalie Clarius <natalie_clarius@yahoo.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include <KRunner/AbstractRunnerTest>
0008 
0009 #include <QDateTime>
0010 #include <QTimeZone>
0011 
0012 #include <qtestcase.h>
0013 
0014 using namespace Qt::StringLiterals;
0015 using namespace KRunner;
0016 
0017 class DateTimeRunnerTest : public AbstractRunnerTest
0018 {
0019     Q_OBJECT
0020 private Q_SLOTS:
0021     void initTestCase();
0022     void testLocalTimeInfo();
0023     void testRemoteTimeInfo();
0024     void testFindTimezones_data();
0025     void testFindTimezones();
0026     void testConversion_data();
0027     void testConversion();
0028 };
0029 
0030 #ifndef Q_OS_WIN
0031 void initEnv()
0032 {
0033     setenv("LC_ALL", "en_US.UTF-8", 1);
0034     setenv("TZ", "GMT", 1);
0035 }
0036 Q_CONSTRUCTOR_FUNCTION(initEnv)
0037 #endif
0038 
0039 void DateTimeRunnerTest::initTestCase()
0040 {
0041     initProperties();
0042 }
0043 
0044 void DateTimeRunnerTest::testLocalTimeInfo()
0045 {
0046     const QTime localTime = QDateTime::currentDateTime().time();
0047     const QString timeStr = QLocale().toString(localTime);
0048 
0049     launchQuery("time");
0050 
0051     QCOMPARE(manager->matches().count(), 1);
0052     QVERIFY(manager->matches().first().text().contains(timeStr));
0053 }
0054 
0055 void DateTimeRunnerTest::testRemoteTimeInfo()
0056 {
0057     // See the definition of GMT in https://data.iana.org/time-zones/releases/tzdata2023c.tar.gz -> etcetera
0058     //
0059     // Be consistent with POSIX TZ settings in the Zone names,
0060     // even though this is the opposite of what many people expect.
0061     // POSIX has positive signs west of Greenwich, but many people expect
0062     // positive signs east of Greenwich.For example, TZ = 'Etc/GMT+4' uses
0063     // the abbreviation "-04" and corresponds to 4 hours behind UT
0064     // (i.e.west of Greenwich) even though many people would expect it to
0065     // mean 4 hours ahead of UT(i.e.east of Greenwich).
0066 
0067     const QTime remoteTime = QDateTime::currentDateTime().toTimeZone(QTimeZone("GMT-2")).time();
0068     const QString timeStr = QLocale().toString(remoteTime, QLocale::ShortFormat);
0069     constexpr QLatin1String timeDiffStr{"2 hours later"};
0070 
0071     launchQuery("time gmt-2");
0072     auto matches = manager->matches();
0073     std::sort(matches.begin(), matches.end(), [](const KRunner::QueryMatch &a, const KRunner::QueryMatch &b) {
0074         return a.relevance() > b.relevance();
0075     });
0076 
0077     QCOMPARE(manager->matches().count(), 1);
0078     QVERIFY2(manager->matches().first().text().contains(timeStr),
0079              QStringLiteral("first match: %1 timeStr: %2").arg(manager->matches().first().text(), timeStr).toUtf8().constData());
0080     QVERIFY(manager->matches().first().text().contains(timeDiffStr));
0081 }
0082 
0083 void DateTimeRunnerTest::testFindTimezones()
0084 {
0085     QFETCH(QString, searchTerm);
0086     QFETCH(int, minMatchCount);
0087     QFETCH(QString, expectedTimezone);
0088 
0089     launchQuery("time " + searchTerm);
0090     auto matches = manager->matches();
0091     // Skip Daylight Saving Time for now, as it causes troubles in the test
0092     matches.erase(std::remove_if(matches.begin(),
0093                                  matches.end(),
0094                                  [](const QueryMatch &match) {
0095                                      return match.text().contains(QLatin1String("PST8PDT"));
0096                                  }),
0097                   matches.end());
0098     std::sort(matches.begin(), matches.end(), [](const KRunner::QueryMatch &a, const KRunner::QueryMatch &b) {
0099         return a.relevance() > b.relevance();
0100     });
0101 
0102     QVERIFY2(matches.size() >= minMatchCount,
0103              QStringLiteral("searchTerm: %1, matches.size(): %2, minMatchCount: %3")
0104                  .arg(searchTerm, QString::number(matches.size()), QString::number(minMatchCount))
0105                  .toUtf8()
0106                  .constData());
0107     QVERIFY2(matches.first().text().contains(expectedTimezone),
0108              QStringLiteral("first match: %1, expectedTimezone: %2").arg(matches.first().text(), expectedTimezone).toUtf8().constData());
0109 }
0110 
0111 void DateTimeRunnerTest::testFindTimezones_data()
0112 {
0113     QTest::addColumn<QString>("searchTerm");
0114     QTest::addColumn<int>("minMatchCount");
0115     QTest::addColumn<QString>("expectedTimezone");
0116 
0117     QTest::newRow("Should find time zones by city name") << "Harare" << 1 << "Harare";
0118     QTest::newRow("Should find time zones by long name") << "Central Africa Time" << 1 << "Central Africa Time";
0119     QTest::newRow("Should find time zones by short name") << "GMT+2" << 1 << "GMT+2";
0120     QTest::newRow("Should find time zones by offset name") << "UTC+02:00" << 1 << "UTC+02:00";
0121     QTest::newRow("Should find time zones by abbreviation, and show all time zones with that abbreviation") << "PST" << 1 << "(PST)";
0122     QTest::newRow("Should find time zones by country name, and show all time zones in that country") << "Brazil" << 2 << "Brazil - ";
0123     QTest::newRow("Should find time zones with the 'in' keyword") << "in Harare" << 1 << "Harare";
0124 }
0125 
0126 void DateTimeRunnerTest::testConversion()
0127 {
0128 #if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
0129     QSKIP("CLDR 42 replaced plain ASCII space U+0020 with U+202F when formatting dates. It's fixed in Qt 6.6.");
0130 #endif
0131     QFETCH(QString, query);
0132     QFETCH(QString, expectedSourceTimezone);
0133     QFETCH(QString, expectedSourceTime);
0134     QFETCH(QString, expectedTargetTimezone);
0135     QFETCH(QString, expectedTargetTime);
0136     QFETCH(QString, expectedTimeDiff);
0137 
0138     launchQuery(query);
0139     auto matches = manager->matches();
0140     std::sort(matches.begin(), matches.end(), [](const KRunner::QueryMatch &a, const KRunner::QueryMatch &b) {
0141         return a.relevance() > b.relevance();
0142     });
0143 
0144     QVERIFY(!matches.isEmpty());
0145     qDebug() << matches.first().text();
0146     const QString targetText = u"%1: %2"_s.arg(expectedTargetTimezone, expectedTargetTime);
0147     QVERIFY2(matches.first().text().contains(targetText), qUtf8Printable(targetText));
0148     const QString sourceText = u"%1: %2"_s.arg(expectedSourceTimezone, expectedSourceTime);
0149     QVERIFY2(matches.first().text().contains(sourceText), qUtf8Printable(sourceText));
0150     QVERIFY2(matches.first().text().contains(expectedTimeDiff), qUtf8Printable(expectedTimeDiff));
0151 }
0152 
0153 void DateTimeRunnerTest::testConversion_data()
0154 {
0155     const QTimeZone systemTimeZone = QTimeZone::systemTimeZone().isValid() ? QTimeZone::systemTimeZone() : QTimeZone::utc(); // needed for FreeBSD CI
0156     const QString systemTimeZoneName = systemTimeZone.abbreviation(QDateTime::currentDateTime());
0157 
0158     QTest::addColumn<QString>("query");
0159     QTest::addColumn<QString>("expectedSourceTimezone");
0160     QTest::addColumn<QString>("expectedSourceTime");
0161     QTest::addColumn<QString>("expectedTargetTimezone");
0162     QTest::addColumn<QString>("expectedTargetTime");
0163     QTest::addColumn<QString>("expectedTimeDiff");
0164 
0165     QTest::newRow("Should convert from first to second time zone") << "12:00 PM gmt-2 in greenwich"
0166                                                                    << "GMT-2" << u"12:00 PM"_s << "Greenwich" << u"10:00 AM"_s << "2 hours earlier";
0167     QTest::newRow("Should convert from first to second time zone with date") << "1/5/23 12:00 PM gmt-2 in greenwich"
0168                                                                              << "GMT-2" << u"12:00 PM"_s << "Greenwich" << u"10:00 AM"_s << "2 hours earlier";
0169     QTest::newRow("Should convert from system time zone to second time zone")
0170         << "12:00 PM in gmt+2" << systemTimeZoneName << u"12:00 PM"_s << "GMT+2" << u"2:00 PM"_s << "2 hours later";
0171     QTest::newRow("Should convert from first time zone to system time zone")
0172         << "12:00 PM gmt+2"
0173         << "GMT+2" << u"12:00 PM"_s << systemTimeZoneName << u"10:00 AM"_s << "2 hours earlier";
0174 }
0175 
0176 QTEST_MAIN(DateTimeRunnerTest)
0177 
0178 #include "datetimerunnertest.moc"