diff options
Diffstat (limited to 'tests/auto/corelib')
52 files changed, 5267 insertions, 437 deletions
diff --git a/tests/auto/corelib/global/qlogging/tst_qlogging.cpp b/tests/auto/corelib/global/qlogging/tst_qlogging.cpp index 04ce504189..94387704f6 100644 --- a/tests/auto/corelib/global/qlogging/tst_qlogging.cpp +++ b/tests/auto/corelib/global/qlogging/tst_qlogging.cpp @@ -101,6 +101,7 @@ void tst_qmessagehandler::initTestCase() QVERIFY2(!m_appDir.isEmpty(), qPrintable( QString::fromLatin1("Couldn't find helper app dir starting from %1.").arg(QDir::currentPath()))); +#ifndef QT_NO_PROCESS m_baseEnvironment = QProcess::systemEnvironment(); for (int i = 0; i < m_baseEnvironment.count(); ++i) { if (m_baseEnvironment.at(i).startsWith("QT_MESSAGE_PATTERN=")) { @@ -108,6 +109,7 @@ void tst_qmessagehandler::initTestCase() break; } } +#endif // !QT_NO_PROCESS } void tst_qmessagehandler::cleanup() @@ -641,6 +643,9 @@ void tst_qmessagehandler::cleanupFuncinfo() void tst_qmessagehandler::qMessagePattern() { +#ifdef QT_NO_PROCESS + QSKIP("This test requires QProcess support"); +#else QProcess process; const QString appExe = m_appDir + "/app"; @@ -710,10 +715,14 @@ void tst_qmessagehandler::qMessagePattern() output.replace("\r\n", "\n"); #endif QCOMPARE(QString::fromLatin1(output), QString::fromLatin1(expected)); +#endif // !QT_NO_PROCESS } void tst_qmessagehandler::qMessagePatternIf() { +#ifdef QT_NO_PROCESS + QSKIP("This test requires QProcess support"); +#else QProcess process; const QString appExe = m_appDir + "/app"; @@ -773,6 +782,7 @@ void tst_qmessagehandler::qMessagePatternIf() QVERIFY(output.contains("QT_MESSAGE_PATTERN: %{if-*} cannot be nested")); QVERIFY(output.contains("A DEBUG qDebug")); QVERIFY(output.contains("A qWarning")); +#endif // !QT_NO_PROCESS } QTEST_MAIN(tst_qmessagehandler) diff --git a/tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp b/tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp index 3607467ff9..a6d76ea7b6 100644 --- a/tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp +++ b/tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp @@ -269,7 +269,8 @@ static int NColorRoles[] = { QPalette::ToolTipText + 1, // Qt_4_6 QPalette::ToolTipText + 1, // Qt_5_0 QPalette::ToolTipText + 1, // Qt_5_1 - 0 // add the correct value for Qt_5_2 here later + QPalette::ToolTipText + 1, // Qt_5_2 + 0 // add the correct value for Qt_5_3 here later }; // Testing get/set functions diff --git a/tests/auto/corelib/io/qipaddress/tst_qipaddress.cpp b/tests/auto/corelib/io/qipaddress/tst_qipaddress.cpp index 1f57e38b44..4fcc46efab 100644 --- a/tests/auto/corelib/io/qipaddress/tst_qipaddress.cpp +++ b/tests/auto/corelib/io/qipaddress/tst_qipaddress.cpp @@ -361,7 +361,7 @@ void tst_QIpAddress::parseIp6() #endif Ip6 result; - bool ok = QIPAddressUtils::parseIp6(result.u8, address.constBegin(), address.constEnd()); + bool ok = QIPAddressUtils::parseIp6(result.u8, address.constBegin(), address.constEnd()) == 0; QVERIFY(ok); QCOMPARE(result, expected); } @@ -441,7 +441,7 @@ void tst_QIpAddress::invalidParseIp6() #endif Ip6 result; - bool ok = QIPAddressUtils::parseIp6(result.u8, address.constBegin(), address.constEnd()); + bool ok = QIPAddressUtils::parseIp6(result.u8, address.constBegin(), address.constEnd()) == 0; QVERIFY(!ok); } diff --git a/tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp b/tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp index c0bf77cfb2..fdb29b60d8 100644 --- a/tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp +++ b/tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp @@ -77,7 +77,7 @@ void tst_QLockFile::initTestCase() QString testdata_dir = QFileInfo(QFINDTESTDATA("qlockfiletesthelper")).absolutePath(); QVERIFY2(QDir::setCurrent(testdata_dir), qPrintable("Could not chdir to " + testdata_dir)); m_helperApp = "qlockfiletesthelper/qlockfile_test_helper"; -#endif +#endif // !QT_NO_PROCESS } void tst_QLockFile::lockUnlock() @@ -111,6 +111,9 @@ void tst_QLockFile::lockUnlock() void tst_QLockFile::lockOutOtherProcess() { +#ifdef QT_NO_PROCESS + QSKIP("This test requires QProcess support"); +#else // Lock const QString fileName = dir.path() + "/lockOtherProcess"; QLockFile lockFile(fileName); @@ -132,6 +135,7 @@ void tst_QLockFile::lockOutOtherProcess() QCOMPARE(ret, int(QLockFile::NoError)); // Lock doesn't survive process though (on clean exit) QVERIFY(!QFile::exists(fileName)); +#endif // !QT_NO_PROCESS } static QLockFile::LockError tryLockFromThread(const QString &fileName) @@ -228,6 +232,9 @@ void tst_QLockFile::staleLockFromCrashedProcess_data() void tst_QLockFile::staleLockFromCrashedProcess() { +#ifdef QT_NO_PROCESS + QSKIP("This test requires QProcess support"); +#else QFETCH(int, staleLockTime); const QString fileName = dir.path() + "/staleLockFromCrashedProcess"; @@ -245,10 +252,14 @@ void tst_QLockFile::staleLockFromCrashedProcess() QVERIFY(secondLock.tryLock()); #endif QCOMPARE(int(secondLock.error()), int(QLockFile::NoError)); +#endif // !QT_NO_PROCESS } void tst_QLockFile::staleShortLockFromBusyProcess() { +#ifdef QT_NO_PROCESS + QSKIP("This test requires QProcess support"); +#else const QString fileName = dir.path() + "/staleLockFromBusyProcess"; QProcess proc; @@ -274,10 +285,14 @@ void tst_QLockFile::staleShortLockFromBusyProcess() proc.waitForFinished(); QVERIFY(secondLock.tryLock()); +#endif // !QT_NO_PROCESS } void tst_QLockFile::staleLongLockFromBusyProcess() { +#ifdef QT_NO_PROCESS + QSKIP("This test requires QProcess support"); +#else const QString fileName = dir.path() + "/staleLockFromBusyProcess"; QProcess proc; @@ -297,6 +312,7 @@ void tst_QLockFile::staleLongLockFromBusyProcess() QVERIFY(!secondLock.removeStaleLockFile()); proc.waitForFinished(); +#endif // !QT_NO_PROCESS } static QString tryStaleLockFromThread(const QString &fileName) @@ -326,6 +342,9 @@ static QString tryStaleLockFromThread(const QString &fileName) void tst_QLockFile::staleLockRace() { +#ifdef QT_NO_PROCESS + QSKIP("This test requires QProcess support"); +#else // Multiple threads notice a stale lock at the same time // Only one thread should delete it, otherwise a race will ensue const QString fileName = dir.path() + "/sharedFile"; @@ -341,6 +360,7 @@ void tst_QLockFile::staleLockRace() synchronizer.waitForFinished(); foreach (const QFuture<QString> &future, synchronizer.futures()) QVERIFY2(future.result().isEmpty(), qPrintable(future.result())); +#endif // !QT_NO_PROCESS } void tst_QLockFile::noPermissions() diff --git a/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp b/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp index f1df6187d0..becff75836 100644 --- a/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp +++ b/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp @@ -72,7 +72,9 @@ void tst_QNoDebug::noDebugOutput() const void tst_QNoDebug::streaming() const { QDateTime dt(QDate(1,2,3),QTime(4,5,6)); - QTest::ignoreMessage(QtWarningMsg, qPrintable(QString::fromLatin1("QDateTime(\"%1\") ").arg(dt.toString()))); + QString debugString = dt.toString(QStringLiteral("yyyy-MM-dd HH:mm:ss.zzz t")) + + QStringLiteral(" Qt::LocalTime"); + QTest::ignoreMessage(QtWarningMsg, qPrintable(QString::fromLatin1("QDateTime(\"%1\") ").arg(debugString))); qWarning() << dt; } diff --git a/tests/auto/corelib/io/qprocess/testForwarding/main.cpp b/tests/auto/corelib/io/qprocess/testForwarding/main.cpp index e69ae173f4..42394f6414 100644 --- a/tests/auto/corelib/io/qprocess/testForwarding/main.cpp +++ b/tests/auto/corelib/io/qprocess/testForwarding/main.cpp @@ -39,30 +39,49 @@ ** ****************************************************************************/ +#include <QtCore/QCoreApplication> #include <QtCore/QProcess> -int main() +#include <stdlib.h> + +int main(int argc, char **argv) { + QCoreApplication app(argc, argv); + + if (argc < 3) + return 13; + #ifndef QT_NO_PROCESS QProcess process; - process.setProcessChannelMode(QProcess::ForwardedChannels); - if (process.processChannelMode() != QProcess::ForwardedChannels) - return -1; - process.start("testProcessEcho/testProcessEcho"); + QProcess::ProcessChannelMode mode = (QProcess::ProcessChannelMode)atoi(argv[1]); + process.setProcessChannelMode(mode); + if (process.processChannelMode() != mode) + return 1; - if (!process.waitForStarted(5000)) - return -1; + QProcess::InputChannelMode inmode = (QProcess::InputChannelMode)atoi(argv[2]); + process.setInputChannelMode(inmode); + if (process.inputChannelMode() != inmode) + return 11; - if (process.write("forwarded\n") != 10) - return -1; + process.start("testProcessEcho2/testProcessEcho2"); - process.waitForReadyRead(250); - if (process.bytesAvailable() != 0) - return -1; + if (!process.waitForStarted(5000)) + return 2; + + if (inmode == QProcess::ManagedInputChannel && process.write("forwarded") != 9) + return 3; process.closeWriteChannel(); - process.waitForFinished(5000); + if (!process.waitForFinished(5000)) + return 4; + + if ((mode == QProcess::ForwardedOutputChannel || mode == QProcess::ForwardedChannels) + && !process.readAllStandardOutput().isEmpty()) + return 5; + if ((mode == QProcess::ForwardedErrorChannel || mode == QProcess::ForwardedChannels) + && !process.readAllStandardError().isEmpty()) + return 6; #endif return 0; } diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp index 7a3f6837f8..d248f022ed 100644 --- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp +++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp @@ -107,8 +107,8 @@ private slots: void softExitInSlots_data(); void softExitInSlots(); void mergedChannels(); + void forwardedChannels_data(); void forwardedChannels(); - void forwardedChannelsOutput(); void atEnd(); void atEnd2(); void waitForFinishedWithTimeout(); @@ -122,6 +122,7 @@ private slots: void setStandardInputFile(); void setStandardOutputFile_data(); void setStandardOutputFile(); + void setStandardOutputFile2(); void setStandardOutputProcess_data(); void setStandardOutputProcess(); void removeFileWhileProcessIsRunning(); @@ -1089,36 +1090,55 @@ void tst_QProcess::mergedChannels() //----------------------------------------------------------------------------- #ifndef Q_OS_WINCE // Reading and writing to a process is not supported on Qt/CE -void tst_QProcess::forwardedChannels() -{ - QProcess process; - process.setReadChannelMode(QProcess::ForwardedChannels); - QCOMPARE(process.readChannelMode(), QProcess::ForwardedChannels); - - process.start("testProcessEcho2/testProcessEcho2"); - QVERIFY(process.waitForStarted(5000)); - QCOMPARE(process.write("forwarded\n"), qlonglong(10)); - QVERIFY(!process.waitForReadyRead(250)); - QCOMPARE(process.bytesAvailable(), qlonglong(0)); +void tst_QProcess::forwardedChannels_data() +{ + QTest::addColumn<int>("mode"); + QTest::addColumn<int>("inmode"); + QTest::addColumn<QByteArray>("outdata"); + QTest::addColumn<QByteArray>("errdata"); - process.closeWriteChannel(); - QVERIFY(process.waitForFinished(5000)); + QTest::newRow("separate") << int(QProcess::SeparateChannels) << int(QProcess::ManagedInputChannel) + << QByteArray() << QByteArray(); + QTest::newRow("forwarded") << int(QProcess::ForwardedChannels) << int(QProcess::ManagedInputChannel) + << QByteArray("forwarded") << QByteArray("forwarded"); + QTest::newRow("stdout") << int(QProcess::ForwardedOutputChannel) << int(QProcess::ManagedInputChannel) + << QByteArray("forwarded") << QByteArray(); + QTest::newRow("stderr") << int(QProcess::ForwardedErrorChannel) << int(QProcess::ManagedInputChannel) + << QByteArray() << QByteArray("forwarded"); + QTest::newRow("fwdinput") << int(QProcess::ForwardedErrorChannel) << int(QProcess::ForwardedInputChannel) + << QByteArray() << QByteArray("input"); } -#endif -#ifndef Q_OS_WINCE -// Reading and writing to a process is not supported on Qt/CE -void tst_QProcess::forwardedChannelsOutput() +void tst_QProcess::forwardedChannels() { + QFETCH(int, mode); + QFETCH(int, inmode); + QFETCH(QByteArray, outdata); + QFETCH(QByteArray, errdata); + QProcess process; - process.start("testForwarding/testForwarding"); + process.start("testForwarding/testForwarding", QStringList() << QString::number(mode) << QString::number(inmode)); QVERIFY(process.waitForStarted(5000)); + QCOMPARE(process.write("input"), 5); + process.closeWriteChannel(); QVERIFY(process.waitForFinished(5000)); - QVERIFY(!process.exitCode()); - QByteArray data = process.readAll(); - QVERIFY(!data.isEmpty()); - QVERIFY(data.contains("forwarded")); + const char *err; + switch (process.exitCode()) { + case 0: err = "ok"; break; + case 1: err = "processChannelMode is wrong"; break; + case 11: err = "inputChannelMode is wrong"; break; + case 2: err = "failed to start"; break; + case 3: err = "failed to write"; break; + case 4: err = "did not finish"; break; + case 5: err = "unexpected stdout"; break; + case 6: err = "unexpected stderr"; break; + case 13: err = "parameter error"; break; + default: err = "unknown exit code"; break; + } + QVERIFY2(!process.exitCode(), err); + QCOMPARE(process.readAllStandardOutput(), outdata); + QCOMPARE(process.readAllStandardError(), errdata); } #endif @@ -1868,6 +1888,13 @@ void tst_QProcess::setStandardInputFile() QByteArray all = process.readAll(); QCOMPARE(all.size(), int(sizeof data) - 1); // testProcessEcho drops the ending \0 QVERIFY(all == data); + + QProcess process2; + process2.setStandardInputFile(QProcess::nullDevice()); + process2.start("testProcessEcho/testProcessEcho"); + QPROCESS_VERIFY(process2, waitForFinished()); + all = process2.readAll(); + QCOMPARE(all.size(), 0); } #endif @@ -1902,6 +1929,23 @@ void tst_QProcess::setStandardOutputFile_data() << true; } +//----------------------------------------------------------------------------- +#ifndef Q_OS_WINCE +void tst_QProcess::setStandardOutputFile2() +{ + static const char testdata[] = "Test data."; + + QProcess process; + process.setStandardOutputFile(QProcess::nullDevice()); + process.start("testProcessEcho2/testProcessEcho2"); + process.write(testdata, sizeof testdata); + QPROCESS_VERIFY(process,waitForFinished()); + QVERIFY(!process.bytesAvailable()); + + QVERIFY(!QFileInfo(QProcess::nullDevice()).isFile()); +} +#endif + void tst_QProcess::setStandardOutputFile() { static const char data[] = "Original data. "; diff --git a/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp b/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp index 45f143b9fb..9d3519680c 100644 --- a/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp +++ b/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp @@ -43,10 +43,6 @@ #include <QObject> #include <QProcessEnvironment> -#ifdef QT_NO_PROCESS -QTEST_NOOP_MAIN -#else - class tst_QProcessEnvironment: public QObject { Q_OBJECT @@ -322,4 +318,3 @@ void tst_QProcessEnvironment::putenv() QTEST_MAIN(tst_QProcessEnvironment) #include "tst_qprocessenvironment.moc" -#endif diff --git a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp index f94c8eac4f..501dde1ba5 100644 --- a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp +++ b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp @@ -166,7 +166,7 @@ void tst_qstandardpaths::testCustomLocations() void tst_qstandardpaths::enableTestMode() { QVERIFY(!QStandardPaths::isTestModeEnabled()); - QStandardPaths::enableTestMode(true); + QStandardPaths::setTestModeEnabled(true); QVERIFY(QStandardPaths::isTestModeEnabled()); #ifdef Q_XDG_PLATFORM @@ -204,7 +204,7 @@ void tst_qstandardpaths::enableTestMode() // On Windows, what should "Program Files" become, in test mode? //testLocations.insert(QStandardPaths::ApplicationsLocation, QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)); - QStandardPaths::enableTestMode(false); + QStandardPaths::setTestModeEnabled(false); for (LocationHash::const_iterator it = testLocations.constBegin(); it != testLocations.constEnd(); ++it) QVERIFY2(QStandardPaths::writableLocation(it.key()) != it.value(), qPrintable(it.value())); diff --git a/tests/auto/corelib/io/qtextstream/readAllStdinProcess/main.cpp b/tests/auto/corelib/io/qtextstream/readAllStdinProcess/main.cpp index 40621957ce..db2a5b53fd 100644 --- a/tests/auto/corelib/io/qtextstream/readAllStdinProcess/main.cpp +++ b/tests/auto/corelib/io/qtextstream/readAllStdinProcess/main.cpp @@ -40,13 +40,11 @@ ****************************************************************************/ -#include <QtCore/QCoreApplication> #include <QtCore/QTextStream> -#include <QtCore/QDebug> +#include <stdio.h> -int main(int argc, char **argv) +int main(int, char**) { - QCoreApplication a(argc, argv); - qDebug() << QTextStream(stdin).readAll(); + fprintf(stderr, "%s\n", QTextStream(stdin).readAll().toLatin1().constData()); return 0; } diff --git a/tests/auto/corelib/io/qtextstream/stdinProcess/main.cpp b/tests/auto/corelib/io/qtextstream/stdinProcess/main.cpp index e36a2aeda8..cb4e75c6a2 100644 --- a/tests/auto/corelib/io/qtextstream/stdinProcess/main.cpp +++ b/tests/auto/corelib/io/qtextstream/stdinProcess/main.cpp @@ -40,17 +40,16 @@ ****************************************************************************/ -#include <QtCore/QCoreApplication> #include <QtCore/QTextStream> +#include <stdio.h> -int main(int argc, char **argv) +int main(int, char**) { - QCoreApplication a(argc, argv); QTextStream qin(stdin); if (!qin.atEnd()) { - int a, b, c; - qin >> a >> b >> c; - qDebug("%d %d %d", a, b, c); + int a, b, c; + qin >> a >> b >> c; + fprintf(stderr, "%d %d %d\n", a, b, c); } return 0; } diff --git a/tests/auto/corelib/io/qtextstream/tst_qtextstream.cpp b/tests/auto/corelib/io/qtextstream/tst_qtextstream.cpp index 56c07f1590..c19e80bff3 100644 --- a/tests/auto/corelib/io/qtextstream/tst_qtextstream.cpp +++ b/tests/auto/corelib/io/qtextstream/tst_qtextstream.cpp @@ -1425,8 +1425,7 @@ void tst_QTextStream::readAllFromStdin() stdinProcess.closeWriteChannel(); QVERIFY(stdinProcess.waitForFinished(5000)); - QChar quoteChar('"'); - QCOMPARE(stream.readAll(), QString::fromLatin1("%1hello world%2 \n").arg(quoteChar).arg(quoteChar)); + QCOMPARE(stream.readAll(), QString::fromLatin1("hello world\n")); } // ------------------------------------------------------------------------------ diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index 2a506ef8e2..e2fa0d3b47 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -111,6 +111,8 @@ private slots: void percentEncoding(); void swap(); void symmetry(); + void ipvfuture_data(); + void ipvfuture(); void ipv6_data(); void ipv6(); void ipv6_2_data(); @@ -146,6 +148,8 @@ private slots: void stripTrailingSlash(); void hosts_data(); void hosts(); + void hostFlags_data(); + void hostFlags(); void setPort(); void toEncoded_data(); void toEncoded(); @@ -160,6 +164,8 @@ private slots: void binaryData(); void fromUserInput_data(); void fromUserInput(); + void fileName_data(); + void fileName(); void isEmptyForEncodedUrl(); void toEncodedNotUsingUninitializedPath(); void emptyAuthorityRemovesExistingAuthority(); @@ -291,6 +297,8 @@ void tst_QUrl::comparison() QVERIFY(url1 == url2); QVERIFY(!(url1 < url2)); QVERIFY(!(url2 < url1)); + QVERIFY(url1.matches(url2, QUrl::None)); + QVERIFY(url1.matches(url2, QUrl::StripTrailingSlash)); // 6.2.2 Syntax-based Normalization QUrl url3 = QUrl::fromEncoded("example://a/b/c/%7Bfoo%7D"); @@ -298,6 +306,22 @@ void tst_QUrl::comparison() QEXPECT_FAIL("", "Normalization not implemented, will probably not be implemented like this", Continue); QCOMPARE(url3, url4); + QUrl url3bis = QUrl::fromEncoded("example://a/b/c/%7Bfoo%7D/"); + QUrl url3bisNoSlash = QUrl::fromEncoded("example://a/b/c/%7Bfoo%7D"); + QUrl url4bis = QUrl::fromEncoded("example://a/.//b/../b/c//%7Bfoo%7D/"); + QCOMPARE(url4bis.adjusted(QUrl::NormalizePathSegments), url3bis); + QCOMPARE(url4bis.adjusted(QUrl::NormalizePathSegments | QUrl::StripTrailingSlash), url3bisNoSlash); + QVERIFY(url3bis.matches(url4bis, QUrl::NormalizePathSegments)); + QVERIFY(!url3bisNoSlash.matches(url4bis, QUrl::NormalizePathSegments)); + QVERIFY(url3bisNoSlash.matches(url4bis, QUrl::NormalizePathSegments | QUrl::StripTrailingSlash)); + + QUrl url4EncodedDots = QUrl("example://a/.//b/%2E%2E%2F/b/c/"); + QCOMPARE(url4EncodedDots.path(QUrl::PrettyDecoded), QString("/.//b/..%2F/b/c/")); + QCOMPARE(url4EncodedDots.path(QUrl::FullyDecoded), QString("/.//b/..//b/c/")); + QCOMPARE(QString::fromLatin1(url4EncodedDots.toEncoded()), QString::fromLatin1("example://a/.//b/..%2F/b/c/")); + QCOMPARE(url4EncodedDots.toString(), QString("example://a/.//b/..%2F/b/c/")); + QCOMPARE(url4EncodedDots.adjusted(QUrl::NormalizePathSegments).toString(), QString("example://a/b/..%2F/b/c/")); + // 6.2.2.1 Make sure hexdecimal characters in percent encoding are // treated case-insensitively QUrl url5; @@ -312,6 +336,59 @@ void tst_QUrl::comparison() url8.setEncodedQuery("a=c"); QVERIFY(url7 != url8); QVERIFY(url7 < url8); + + // Trailing slash difference + QUrl url9("http://qt-project.org/path/"); + QUrl url9NoSlash("http://qt-project.org/path"); + QVERIFY(!(url9 == url9NoSlash)); + QVERIFY(!url9.matches(url9NoSlash, QUrl::None)); + QVERIFY(url9.matches(url9NoSlash, QUrl::StripTrailingSlash)); + + // RemoveFilename + QUrl url10("http://qt-project.org/file"); + QUrl url10bis("http://qt-project.org/otherfile"); + QVERIFY(!(url10 == url10bis)); + QVERIFY(!url10.matches(url10bis, QUrl::None)); + QVERIFY(!url10.matches(url10bis, QUrl::StripTrailingSlash)); + QVERIFY(url10.matches(url10bis, QUrl::RemoveFilename)); + + // RemoveAuthority + QUrl authUrl1("x://host/a/b"); + QUrl authUrl2("x://host/a/"); + QUrl authUrl3("x:/a/b"); + QVERIFY(authUrl1.matches(authUrl2, QUrl::RemoveFilename)); + QCOMPARE(authUrl1.adjusted(QUrl::RemoveAuthority), authUrl3.adjusted(QUrl::RemoveAuthority)); + QVERIFY(authUrl1.matches(authUrl3, QUrl::RemoveAuthority)); + QCOMPARE(authUrl2.adjusted(QUrl::RemoveAuthority | QUrl::RemoveFilename), authUrl3.adjusted(QUrl::RemoveAuthority | QUrl::RemoveFilename)); + QVERIFY(authUrl2.matches(authUrl3, QUrl::RemoveAuthority | QUrl::RemoveFilename)); + QVERIFY(authUrl3.matches(authUrl2, QUrl::RemoveAuthority | QUrl::RemoveFilename)); + + QUrl hostUrl1("file:/foo"); + QUrl hostUrl2("file:///foo"); + QVERIFY(hostUrl1 == hostUrl2); + QVERIFY(hostUrl1.matches(hostUrl2, QUrl::None)); + QVERIFY(hostUrl1.matches(hostUrl2, QUrl::RemoveAuthority)); + + // RemovePassword + QUrl passUrl1("http://user:pass@host/"); + QUrl passUrl2("http://user:PASS@host/"); + QVERIFY(!(passUrl1 == passUrl2)); + QVERIFY(passUrl1 != passUrl2); + QVERIFY(!passUrl1.matches(passUrl2, QUrl::None)); + QVERIFY(passUrl1.matches(passUrl2, QUrl::RemovePassword)); + + // RemoveQuery, RemoveFragment + QUrl queryFragUrl1("http://host/file?query#fragment"); + QUrl queryFragUrl2("http://host/file?q2#f2"); + QUrl queryFragUrl3("http://host/file"); + QVERIFY(!(queryFragUrl1 == queryFragUrl2)); + QVERIFY(queryFragUrl1 != queryFragUrl2); + QVERIFY(!queryFragUrl1.matches(queryFragUrl2, QUrl::None)); + QVERIFY(!queryFragUrl1.matches(queryFragUrl2, QUrl::RemoveQuery)); + QVERIFY(!queryFragUrl1.matches(queryFragUrl2, QUrl::RemoveFragment)); + QVERIFY(queryFragUrl1.matches(queryFragUrl2, QUrl::RemoveQuery | QUrl::RemoveFragment)); + QVERIFY(queryFragUrl1.matches(queryFragUrl3, QUrl::RemoveQuery | QUrl::RemoveFragment)); + QVERIFY(queryFragUrl3.matches(queryFragUrl1, QUrl::RemoveQuery | QUrl::RemoveFragment)); } void tst_QUrl::comparison2_data() @@ -422,18 +499,20 @@ void tst_QUrl::setUrl() } { - QUrl url("http://user:pass@[56::56:56:56:127.0.0.1]:99"); + QUrl url("http://user%3A:pass%40@[56::56:56:56:127.0.0.1]:99"); QVERIFY(url.isValid()); QCOMPARE(url.scheme(), QString::fromLatin1("http")); QCOMPARE(url.path(), QString()); QVERIFY(url.encodedQuery().isEmpty()); - QCOMPARE(url.userInfo(), QString::fromLatin1("user:pass")); + QCOMPARE(url.userName(), QString::fromLatin1("user:")); + QCOMPARE(url.password(), QString::fromLatin1("pass@")); + QCOMPARE(url.userInfo(), QString::fromLatin1("user%3A:pass@")); QVERIFY(url.fragment().isEmpty()); QCOMPARE(url.host(), QString::fromLatin1("56::56:56:56:7f00:1")); - QCOMPARE(url.authority(), QString::fromLatin1("user:pass@[56::56:56:56:7f00:1]:99")); + QCOMPARE(url.authority(), QString::fromLatin1("user%3A:pass%40@[56::56:56:56:7f00:1]:99")); QCOMPARE(url.port(), 99); - QCOMPARE(url.url(), QString::fromLatin1("http://user:pass@[56::56:56:56:7f00:1]:99")); - QCOMPARE(url.toDisplayString(), QString::fromLatin1("http://user@[56::56:56:56:7f00:1]:99")); + QCOMPARE(url.url(), QString::fromLatin1("http://user%3A:pass%40@[56::56:56:56:7f00:1]:99")); + QCOMPARE(url.toDisplayString(), QString::fromLatin1("http://user%3A@[56::56:56:56:7f00:1]:99")); } { @@ -611,8 +690,8 @@ void tst_QUrl::setUrl() QUrl charles; charles.setPath("/home/charles/foo%20moo"); - QCOMPARE(charles.path(), QString::fromLatin1("/home/charles/foo moo")); - QCOMPARE(charles.path(QUrl::FullyEncoded), QString::fromLatin1("/home/charles/foo%20moo")); + QCOMPARE(charles.path(), QString::fromLatin1("/home/charles/foo%20moo")); + QCOMPARE(charles.path(QUrl::FullyEncoded), QString::fromLatin1("/home/charles/foo%2520moo")); QUrl charles2("file:/home/charles/foo%20moo"); QCOMPARE(charles2.path(), QString::fromLatin1("/home/charles/foo moo")); @@ -686,7 +765,7 @@ void tst_QUrl::setUrl() QCOMPARE(url.scheme(), QString("data")); QCOMPARE(url.host(), QString()); QCOMPARE(url.path(), QString("text/javascript,d5 = 'five\\u0027s';")); - QCOMPARE(url.encodedPath().constData(), "text/javascript,d5%20=%20'five%5Cu0027s';"); + QCOMPARE(url.encodedPath().constData(), "text/javascript,d5%20%3D%20'five%5Cu0027s'%3B"); } { @@ -946,8 +1025,12 @@ void tst_QUrl::toString() QFETCH(uint, options); QFETCH(QString, string); + QUrl::FormattingOptions opt(options); + QUrl url(urlString); - QCOMPARE(url.toString(QUrl::FormattingOptions(options)), string); + QCOMPARE(url.toString(opt), string); + + QCOMPARE(url.adjusted(opt).toString(), string); } void tst_QUrl::toAndFromStringList_data() @@ -1135,11 +1218,11 @@ void tst_QUrl::fromLocalFile_data() QTest::newRow("data7") << QString::fromLatin1("/Mambo <#5>.mp3") << QString::fromLatin1("file:///Mambo <%235>.mp3") << QString::fromLatin1("/Mambo <#5>.mp3"); QTest::newRow("data8") << QString::fromLatin1("/a%.txt") << QString::fromLatin1("file:///a%25.txt") - << QString::fromLatin1("/a%25.txt"); + << QString::fromLatin1("/a%.txt"); QTest::newRow("data9") << QString::fromLatin1("/a%25.txt") << QString::fromLatin1("file:///a%2525.txt") - << QString::fromLatin1("/a%2525.txt"); + << QString::fromLatin1("/a%25.txt"); QTest::newRow("data10") << QString::fromLatin1("/%80.txt") << QString::fromLatin1("file:///%2580.txt") - << QString::fromLatin1("/%2580.txt"); + << QString::fromLatin1("/%80.txt"); } void tst_QUrl::fromLocalFile() @@ -1498,17 +1581,17 @@ void tst_QUrl::relative() void tst_QUrl::percentEncoding_data() { + // This test is limited. It's superseded by componentEncodings below QTest::addColumn<QString>("original"); QTest::addColumn<QByteArray>("encoded"); QTest::newRow("test_01") << QString::fromLatin1("sdfsdf") << QByteArray("sdfsdf"); QTest::newRow("test_02") << QString::fromUtf8("æss") << QByteArray("%C3%A6ss"); - // not unreserved or reserved - QTest::newRow("test_03") << QString::fromLatin1("{}") << QByteArray("%7B%7D"); } void tst_QUrl::percentEncoding() { + // This test is limited. It's superseded by componentEncodings below QFETCH(QString, original); QFETCH(QByteArray, encoded); @@ -1583,21 +1666,83 @@ void tst_QUrl::symmetry() { QString urlString = QString::fromLatin1("http://desktop:33326/upnp/{32f525a6-6f31-426e-91ca-01c2e6c2c57e}"); + QString encodedUrlString = QString("http://desktop:33326/upnp/%7B32f525a6-6f31-426e-91ca-01c2e6c2c57e%7D"); QUrl urlPreviewList(urlString); - QCOMPARE(urlPreviewList.toString(), urlString); + QCOMPARE(urlPreviewList.toString(), encodedUrlString); QByteArray b = urlPreviewList.toEncoded(); - QCOMPARE(b.constData(), "http://desktop:33326/upnp/%7B32f525a6-6f31-426e-91ca-01c2e6c2c57e%7D"); - QCOMPARE(QUrl::fromEncoded(b).toString(), urlString); - QCOMPARE(QUrl(b).toString(), urlString); + QCOMPARE(b.constData(), encodedUrlString.toLatin1().constData()); + QCOMPARE(QUrl::fromEncoded(b).toString(), encodedUrlString); + QCOMPARE(QUrl(b).toString(), encodedUrlString); } { QString urlString = QString::fromLatin1("http://desktop:53423/deviceDescription?uuid={7977c17b-00bf-4af9-894e-fed28573c3a9}"); + QString encodedUrlString = QString("http://desktop:53423/deviceDescription?uuid=%7B7977c17b-00bf-4af9-894e-fed28573c3a9%7D"); QUrl urlPreviewList(urlString); - QCOMPARE(urlPreviewList.toString(), urlString); + QCOMPARE(urlPreviewList.toString(), encodedUrlString); QByteArray b = urlPreviewList.toEncoded(); - QCOMPARE(b.constData(), "http://desktop:53423/deviceDescription?uuid=%7B7977c17b-00bf-4af9-894e-fed28573c3a9%7D"); - QCOMPARE(QUrl::fromEncoded(b).toString(), urlString); - QCOMPARE(QUrl(b).toString(), urlString); + QCOMPARE(b.constData(), encodedUrlString.toLatin1().constData()); + QCOMPARE(QUrl::fromEncoded(b).toString(), encodedUrlString); + QCOMPARE(QUrl(b).toString(), encodedUrlString); + } +} + +void tst_QUrl::ipvfuture_data() +{ + QTest::addColumn<QString>("input"); + QTest::addColumn<bool>("isValid"); + QTest::addColumn<QString>("output"); + + // No one uses IPvFuture yet, so we have no clue what it might contain + // We're just testing that it can hold what the RFC says it should hold: + // IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) + QTest::newRow("missing-version-dot") << "x://[v]" << false; + QTest::newRow("missing-version") << "x://[v.]" << false; + QTest::newRow("missing-version-2") << "x://[v.1234]" << false; + QTest::newRow("missing-dot") << "x://[v7]" << false; + QTest::newRow("missing-dot-2") << "x://[v71234]" << false; + QTest::newRow("missing-data") << "x://[v7.]" << false; + QTest::newRow("non-hex-version") << "x://[vz.1234]" << false; + + QTest::newRow("digit-ver") << "x://[v7.1]" << true << "x://[v7.1]"; + QTest::newRow("lowercase-hex-ver") << "x://[va.1]" << true << "x://[vA.1]"; + QTest::newRow("lowercase-hex-ver") << "x://[vA.1]" << true << "x://[vA.1]"; + + QTest::newRow("data-digits") << "x://[v7.1234]" << true << "x://[v7.1234]"; + QTest::newRow("data-unreserved") << "x://[v7.hello~-WORLD_.com]" << true << "x://[v7.hello~-WORLD_.com]"; + QTest::newRow("data-sub-delims-colon") << "x://[v7.!$&'()*+,;=:]" << true << "x://[v7.!$&'()*+,;=:]"; + + // we're using the tolerant parser + QTest::newRow("data-encoded-digits") << "x://[v7.%31%32%33%34]" << true << "x://[v7.1234]"; + QTest::newRow("data-encoded-unreserved") << "x://[v7.%7E%2D%54%5f%2E]" << true << "x://[v7.~-T_.]"; + QTest::newRow("data-encoded-sub-delims-colon") << "x://[v7.%21%24%26%27%28%29%2A%2B%2C%3B%3D%3A]" << true << "x://[v7.!$&'()*+,;=:]"; + + // should we test "[%76%37%2ex]" -> "[v7.x]" ? + + QTest::newRow("data-invalid-space") << "x://[v7.%20]" << false; + QTest::newRow("data-invalid-control") << "x://[v7.\x7f]" << false; + QTest::newRow("data-invalid-other-1") << "x://[v7.{1234}]" << false; + QTest::newRow("data-invalid-other-2") << "x://[v7.<hello>]" << false; + QTest::newRow("data-invalid-unicode") << "x://[v7.æøå]" << false; + QTest::newRow("data-invalid-percent") << "x://[v7.%]" << false; + QTest::newRow("data-invalid-percent-percent") << "x://[v7.%25]" << false; +} + +void tst_QUrl::ipvfuture() +{ + QFETCH(QString, input); + QFETCH(bool, isValid); + + QUrl url(input); + if (isValid) { + QVERIFY2(url.isValid(), qPrintable(url.errorString())); + + QFETCH(QString, output); + QCOMPARE(url.toString(), output); + + QUrl url2(output); + QCOMPARE(url2, url); + } else { + QVERIFY(!url.isValid()); } } @@ -1641,6 +1786,9 @@ void tst_QUrl::ipv6_data() QTest::newRow("case :,") << QString::fromLatin1("//[:,]") << false << ""; QTest::newRow("case ::bla") << QString::fromLatin1("//[::bla]") << false << ""; QTest::newRow("case v4-mapped") << "//[0:0:0:0:0:ffff:7f00:1]" << true << "//[::ffff:127.0.0.1]"; + + QTest::newRow("encoded-digit") << "//[::%31]" << true << "//[::1]"; + QTest::newRow("encoded-colon") << "//[%3A%3A]" << true << "//[::]"; } void tst_QUrl::ipv6() @@ -1932,9 +2080,14 @@ void tst_QUrl::strictParser_data() QTest::newRow("invalid-regname") << "http://bad<hostname>" << "Invalid hostname (contains invalid characters)"; QTest::newRow("invalid-regname-2") << "http://b%61d" << "Invalid hostname (contains invalid characters)"; QTest::newRow("invalid-ipv6") << "http://[:::]" << "Invalid IPv6 address"; + QTest::newRow("invalid-ipv6-char1") << "http://[::g]" << "Invalid IPv6 address (character 'g' not permitted)"; + QTest::newRow("invalid-ipv6-char2") << "http://[z::]" << "Invalid IPv6 address (character 'z' not permitted)"; QTest::newRow("invalid-ipvfuture-1") << "http://[v7]" << "Invalid IPvFuture address"; QTest::newRow("invalid-ipvfuture-2") << "http://[v7.]" << "Invalid IPvFuture address"; QTest::newRow("invalid-ipvfuture-3") << "http://[v789]" << "Invalid IPvFuture address"; + QTest::newRow("invalid-ipvfuture-char1") << "http://[v7.^]" << "Invalid IPvFuture address"; + QTest::newRow("invalid-encoded-ipv6") << "x://[%3a%3a%31]" << "Invalid IPv6 address"; + QTest::newRow("invalid-encoded-ipvfuture") << "x://[v7.%7E%2D%54%5f%2E]" << "Invalid IPvFuture address"; QTest::newRow("unbalanced-brackets") << "http://[ff02::1" << "Expected ']' to match '[' in hostname"; // invalid hostnames happen in TolerantMode too @@ -2035,35 +2188,22 @@ void tst_QUrl::tolerantParser() url.setUrl("http://foo.bar/[image][1].jpg"); QVERIFY(url.isValid()); QVERIFY(!url.toString().isEmpty()); - QCOMPARE(url.toString(QUrl::FullyEncoded), QString("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); - QCOMPARE(url.toEncoded(), QByteArray("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("http://foo.bar/[image][1].jpg")); + QCOMPARE(url.toEncoded(), QByteArray("http://foo.bar/[image][1].jpg")); QCOMPARE(url.toString(), QString("http://foo.bar/[image][1].jpg")); - url.setUrl("[].jpg"); - QCOMPARE(url.toString(QUrl::FullyEncoded), QString("%5B%5D.jpg")); - QCOMPARE(url.toEncoded(), QByteArray("%5B%5D.jpg")); - QCOMPARE(url.toString(), QString("[].jpg")); - - url.setUrl("/some/[path]/[]"); - QCOMPARE(url.toString(QUrl::FullyEncoded), QString("/some/%5Bpath%5D/%5B%5D")); - QCOMPARE(url.toEncoded(), QByteArray("/some/%5Bpath%5D/%5B%5D")); - QCOMPARE(url.toString(), QString("/some/[path]/[]")); + url.setUrl("http://foo.bar/%5Bimage%5D%5B1%5D.jpg"); + QVERIFY(url.isValid()); + QVERIFY(!url.toString().isEmpty()); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); + QCOMPARE(url.toEncoded(), QByteArray("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); + QCOMPARE(url.toString(), QString("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); url.setUrl("//[::56:56:56:56:56:56:56]"); QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]")); QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]")); QCOMPARE(url.toString(), QString("//[0:56:56:56:56:56:56:56]")); - url.setUrl("//[::56:56:56:56:56:56:56]#[]"); - QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]#%5B%5D")); - QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]#%5B%5D")); - QCOMPARE(url.toString(), QString("//[0:56:56:56:56:56:56:56]#[]")); - - url.setUrl("//[::56:56:56:56:56:56:56]?[]"); - QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]?[]")); - QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]?[]")); - QCOMPARE(url.toString(), QString("//[0:56:56:56:56:56:56:56]?[]")); - // invoke the tolerant parser's error correction url.setUrl("%hello.com/f%"); QCOMPARE(url.toString(QUrl::FullyEncoded), QString("%25hello.com/f%25")); @@ -2076,38 +2216,24 @@ void tst_QUrl::tolerantParser() url.setEncodedUrl("http://foo.bar/[image][1].jpg"); QVERIFY(url.isValid()); - QCOMPARE(url.toString(QUrl::FullyEncoded), QString("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); - QCOMPARE(url.toEncoded(), QByteArray("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("http://foo.bar/[image][1].jpg")); + QCOMPARE(url.toEncoded(), QByteArray("http://foo.bar/[image][1].jpg")); QCOMPARE(url.toString(), QString("http://foo.bar/[image][1].jpg")); - url.setEncodedUrl("[].jpg"); - QCOMPARE(url.toString(QUrl::FullyEncoded), QString("%5B%5D.jpg")); - QCOMPARE(url.toEncoded(), QByteArray("%5B%5D.jpg")); - QCOMPARE(url.toString(), QString("[].jpg")); - - url.setEncodedUrl("/some/[path]/[]"); - QCOMPARE(url.toString(QUrl::FullyEncoded), QString("/some/%5Bpath%5D/%5B%5D")); - QCOMPARE(url.toEncoded(), QByteArray("/some/%5Bpath%5D/%5B%5D")); - QCOMPARE(url.toString(), QString("/some/[path]/[]")); + url.setEncodedUrl("http://foo.bar/%5Bimage%5D%5B1%5D.jpg"); + QVERIFY(url.isValid()); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); + QCOMPARE(url.toEncoded(), QByteArray("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); + QCOMPARE(url.toString(), QString("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); url.setEncodedUrl("//[::56:56:56:56:56:56:56]"); QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]")); QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]")); - url.setEncodedUrl("//[::56:56:56:56:56:56:56]#[]"); - QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]#%5B%5D")); - QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]#%5B%5D")); - QCOMPARE(url.toString(), QString("//[0:56:56:56:56:56:56:56]#[]")); - - url.setEncodedUrl("//[::56:56:56:56:56:56:56]?[]"); - QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]?[]")); - QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]?[]")); - QCOMPARE(url.toString(), QString("//[0:56:56:56:56:56:56:56]?[]")); - url.setEncodedUrl("data:text/css,div%20{%20border-right:%20solid;%20}"); QCOMPARE(url.toString(QUrl::FullyEncoded), QString("data:text/css,div%20%7B%20border-right:%20solid;%20%7D")); QCOMPARE(url.toEncoded(), QByteArray("data:text/css,div%20%7B%20border-right:%20solid;%20%7D")); - QCOMPARE(url.toString(), QString("data:text/css,div { border-right: solid; }")); + QCOMPARE(url.toString(), QString("data:text/css,div %7B border-right: solid; %7D")); } { @@ -2386,7 +2512,7 @@ void tst_QUrl::setEncodedFragment_data() QTest::newRow("basic test") << BA("http://www.kde.org") << BA("abc") << BA("http://www.kde.org#abc"); QTest::newRow("initial url has fragment") << BA("http://www.kde.org#old") << BA("new") << BA("http://www.kde.org#new"); QTest::newRow("encoded fragment") << BA("http://www.kde.org") << BA("a%20c") << BA("http://www.kde.org#a%20c"); - QTest::newRow("with #") << BA("http://www.kde.org") << BA("a#b") << BA("http://www.kde.org#a#b"); + QTest::newRow("with #") << BA("http://www.kde.org") << BA("a#b") << BA("http://www.kde.org#a%23b"); // toString uses "a#b" QTest::newRow("unicode") << BA("http://www.kde.org") << BA("\xc3\xa9") << BA("http://www.kde.org#%C3%A9"); QTest::newRow("binary") << BA("http://www.kde.org") << BA("\x00\xc0\x80", 3) << BA("http://www.kde.org#%00%C0%80"); } @@ -2408,8 +2534,9 @@ void tst_QUrl::setEncodedFragment() void tst_QUrl::fromEncoded() { QUrl qurl2 = QUrl::fromEncoded("print:/specials/Print%20To%20File%20(PDF%252FAcrobat)", QUrl::TolerantMode); - QCOMPARE(qurl2.path(), QString::fromLatin1("/specials/Print To File (PDF%252FAcrobat)")); - QCOMPARE(QFileInfo(qurl2.path()).fileName(), QString::fromLatin1("Print To File (PDF%252FAcrobat)")); + QCOMPARE(qurl2.path(), QString::fromLatin1("/specials/Print To File (PDF%2FAcrobat)")); + QCOMPARE(QFileInfo(qurl2.path()).fileName(), QString::fromLatin1("Print To File (PDF%2FAcrobat)")); + QCOMPARE(qurl2.fileName(), QString::fromLatin1("Print To File (PDF%2FAcrobat)")); QCOMPARE(qurl2.toEncoded().constData(), "print:/specials/Print%20To%20File%20(PDF%252FAcrobat)"); QUrl qurl = QUrl::fromEncoded("http://\303\244.de"); @@ -2427,23 +2554,37 @@ void tst_QUrl::fromEncoded() void tst_QUrl::stripTrailingSlash_data() { QTest::addColumn<QString>("url"); - QTest::addColumn<QString>("expected"); + QTest::addColumn<QString>("expectedStrip"); // toString(Strip) + QTest::addColumn<QString>("expectedDir"); // toString(RemoveFilename) + QTest::addColumn<QString>("expectedDirStrip"); // toString(RemoveFilename|Strip) - QTest::newRow("ftp no slash") << "ftp://ftp.de.kde.org/dir" << "ftp://ftp.de.kde.org/dir"; - QTest::newRow("ftp slash") << "ftp://ftp.de.kde.org/dir/" << "ftp://ftp.de.kde.org/dir"; - QTest::newRow("file slash") << "file:///dir/" << "file:///dir"; - QTest::newRow("file no slash") << "file:///dir/" << "file:///dir"; - QTest::newRow("file root") << "file:///" << "file:///"; - QTest::newRow("no path") << "remote://" << "remote://"; + QTest::newRow("subdir no slash") << "ftp://kde.org/dir/subdir" << "ftp://kde.org/dir/subdir" << "ftp://kde.org/dir/" << "ftp://kde.org/dir"; + QTest::newRow("ftp no slash") << "ftp://kde.org/dir" << "ftp://kde.org/dir" << "ftp://kde.org/" << "ftp://kde.org/"; + QTest::newRow("ftp slash") << "ftp://kde.org/dir/" << "ftp://kde.org/dir" << "ftp://kde.org/dir/" << "ftp://kde.org/dir"; + QTest::newRow("ftp_two_slashes") << "ftp://kde.org/dir//" << "ftp://kde.org/dir" << "ftp://kde.org/dir//" << "ftp://kde.org/dir"; + QTest::newRow("file slash") << "file:///dir/" << "file:///dir" << "file:///dir/" << "file:///dir"; + QTest::newRow("file no slash") << "file:///dir" << "file:///dir" << "file:///" << "file:///"; + QTest::newRow("file root") << "file:///" << "file:///" << "file:///" << "file:///"; + QTest::newRow("file_root_manyslashes") << "file://///" << "file:///" << "file://///" << "file:///"; + QTest::newRow("no path") << "remote://" << "remote://" << "remote://" << "remote://"; } void tst_QUrl::stripTrailingSlash() { QFETCH(QString, url); - QFETCH(QString, expected); + QFETCH(QString, expectedStrip); + QFETCH(QString, expectedDir); + QFETCH(QString, expectedDirStrip); QUrl u(url); - QCOMPARE(u.toString(QUrl::StripTrailingSlash), expected); + QCOMPARE(u.toString(QUrl::StripTrailingSlash), expectedStrip); + QCOMPARE(u.toString(QUrl::RemoveFilename), expectedDir); + QCOMPARE(u.toString(QUrl::RemoveFilename | QUrl::StripTrailingSlash), expectedDirStrip); + + // Same thing, using QUrl::adjusted() + QCOMPARE(u.adjusted(QUrl::StripTrailingSlash).toString(), expectedStrip); + QCOMPARE(u.adjusted(QUrl::RemoveFilename).toString(), expectedDir); + QCOMPARE(u.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toString(), expectedDirStrip); } void tst_QUrl::hosts_data() @@ -2503,6 +2644,29 @@ void tst_QUrl::hosts() QTEST(QUrl(url).host(), "host"); } +void tst_QUrl::hostFlags_data() +{ + QTest::addColumn<QString>("urlStr"); + QTest::addColumn<QUrl::FormattingOptions>("options"); + QTest::addColumn<QString>("expectedHost"); + + QString swedish = QString::fromUtf8("http://www.räksmörgås.se/pub?a=b&a=dø&a=f#vræl"); + QTest::newRow("se_fullydecoded") << swedish << QUrl::FormattingOptions(QUrl::FullyDecoded) << QString::fromUtf8("www.räksmörgås.se"); + QTest::newRow("se_fullyencoded") << swedish << QUrl::FormattingOptions(QUrl::FullyEncoded) << QString::fromUtf8("www.xn--rksmrgs-5wao1o.se"); + QTest::newRow("se_prettydecoded") << swedish << QUrl::FormattingOptions(QUrl::PrettyDecoded) << QString::fromUtf8("www.räksmörgås.se"); + QTest::newRow("se_encodespaces") << swedish << QUrl::FormattingOptions(QUrl::EncodeSpaces) << QString::fromUtf8("www.räksmörgås.se"); +} + +void tst_QUrl::hostFlags() +{ + QFETCH(QString, urlStr); + QFETCH(QUrl::FormattingOptions, options); + QFETCH(QString, expectedHost); + + QUrl url(urlStr); + QCOMPARE(url.host(options), expectedHost); +} + void tst_QUrl::setPort() { { @@ -2729,6 +2893,43 @@ void tst_QUrl::fromUserInput() QCOMPARE(url, guessUrlFromString); } +void tst_QUrl::fileName_data() +{ + QTest::addColumn<QString>("urlStr"); + QTest::addColumn<QString>("expectedDirPath"); + QTest::addColumn<QString>("expectedPrettyDecodedFileName"); + QTest::addColumn<QString>("expectedFullyDecodedFileName"); + + QTest::newRow("fromDocu") << "http://qt-project.org/support/file.html" + << "/support/" << "file.html" << "file.html"; + QTest::newRow("absoluteFile") << "file:///temp/tmp.txt" + << "/temp/" << "tmp.txt" << "tmp.txt"; + QTest::newRow("absoluteDir") << "file:///temp/" + << "/temp/" << QString() << QString(); + QTest::newRow("absoluteInRoot") << "file:///temp" + << "/" << "temp" << "temp"; + QTest::newRow("relative") << "temp/tmp.txt" + << "temp/" << "tmp.txt" << "tmp.txt"; + QTest::newRow("relativeNoSlash") << "tmp.txt" + << QString() << "tmp.txt" << "tmp.txt"; + QTest::newRow("encoded") << "print:/specials/Print%20To%20File%20(PDF%252FAcrobat)" + << "/specials/" << "Print To File (PDF%252FAcrobat)" << "Print To File (PDF%2FAcrobat)"; +} + +void tst_QUrl::fileName() +{ + QFETCH(QString, urlStr); + QFETCH(QString, expectedDirPath); + QFETCH(QString, expectedPrettyDecodedFileName); + QFETCH(QString, expectedFullyDecodedFileName); + + QUrl url(urlStr); + QVERIFY(url.isValid()); + QCOMPARE(url.adjusted(QUrl::RemoveFilename).path(), expectedDirPath); + QCOMPARE(url.fileName(QUrl::PrettyDecoded), expectedPrettyDecodedFileName); + QCOMPARE(url.fileName(QUrl::FullyDecoded), expectedFullyDecodedFileName); +} + // This is a regression test for a previously fixed bug where isEmpty didn't // work for an encoded URL that was yet to be decoded. The test checks that // isEmpty works for an encoded URL both after and before decoding. @@ -2866,7 +3067,8 @@ void tst_QUrl::effectiveTLDs() { QFETCH(QUrl, domain); QFETCH(QString, TLD); - QCOMPARE(domain.topLevelDomain(), TLD); + QCOMPARE(domain.topLevelDomain(QUrl::PrettyDecoded), TLD); + QCOMPARE(domain.topLevelDomain(QUrl::FullyDecoded), TLD); } void tst_QUrl::lowercasesScheme() @@ -2950,19 +3152,25 @@ void tst_QUrl::componentEncodings_data() // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" // / "*" / "+" / "," / ";" / "=" - // like the unreserved, these are decoded everywhere - // don't test in query because they might remain encoded - QTest::newRow("decoded-subdelims") << QUrl("x://%21%24%26:%27%28%29@host/%2a%2b%2c#%3b%3d") + // these are always left alone + QTest::newRow("decoded-subdelims") << QUrl("x://!$&:'()@host/*+,?$=(+)#;=") << int(QUrl::FullyEncoded) << "!$&" << "'()" << "!$&:'()" << "host" << "!$&:'()@host" - << "/*+," << "" << ";=" - << "x://!$&:'()@host/*+,#;="; + << "/*+," << "$=(+)" << ";=" + << "x://!$&:'()@host/*+,?$=(+)#;="; + QTest::newRow("encoded-subdelims") << QUrl("x://%21%24%26:%27%28%29@host/%2a%2b%2c?%26=%26&%3d=%3d#%3b%3d") + << MostDecoded + << "%21%24%26" << "%27%28%29" << "%21%24%26:%27%28%29" + << "host" << "%21%24%26:%27%28%29@host" + << "/%2A%2B%2C" << "%26=%26&%3D=%3D" << "%3B%3D" + << "x://%21%24%26:%27%28%29@host/%2A%2B%2C?%26=%26&%3D=%3D#%3B%3D"; // gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" // these are the separators between fields - // they must appear encoded in certain positions, no exceptions - // in other positions, they can appear decoded, so they always do + // they must appear encoded in certain positions in the full URL, no exceptions + // when in those positions, they appear decoded in the isolated parts + // in other positions and the other delimiters are always left untransformed // 1) test the delimiters that must appear encoded // (if they were decoded, they'd would change the URL parsing) QTest::newRow("encoded-gendelims-changing") << QUrl("x://%5b%3a%2f%3f%23%40%5d:%5b%2f%3f%23%40%5d@host/%2f%3f%23?%23") @@ -2972,32 +3180,21 @@ void tst_QUrl::componentEncodings_data() << "/%2F?#" << "#" << "" << "x://%5B%3A%2F%3F%23%40%5D:%5B%2F%3F%23%40%5D@host/%2F%3F%23?%23"; - // 2) test the delimiters that may appear decoded and would not change the meaning - // and test that %2f is *not* decoded to a slash in the path - // don't test the query because in this mode it doesn't transform anything - QTest::newRow("decoded-gendelims-unchanging") << QUrl("x://:%3a@host/%2f%3a%40#%23%3a%2f%3f%40") + // 2) test that the other delimiters remain decoded + QTest::newRow("decoded-gendelims-unchanging") << QUrl("x://::@host/:@/[]?:/?@[]?#:/?@[]") << int(QUrl::FullyEncoded) << "" << ":" << "::" << "host" << "::@host" - << "/%2F:@" << "" << "#:/?@" - << "x://::@host/%2F:@##:/?@"; - - // 3) test "[" and "]". Even though they are not ambiguous in the path, query or fragment - // the RFC does not allow them to appear there decoded. QUrl adheres strictly in FullyEncoded mode - QTest::newRow("encoded-square-brackets") << QUrl("x:/[]#[]") - << int(QUrl::FullyEncoded) - << "" << "" << "" - << "" << "" - << "/%5B%5D" << "" << "%5B%5D" - << "x:/%5B%5D#%5B%5D"; - - // 4) like above, but now decode them, which is allowed - QTest::newRow("decoded-square-brackets") << QUrl("x:/%5B%5D#%5B%5D") - << MostDecoded - << "" << "" << "" - << "" << "" - << "/[]" << "" << "[]" - << "x:/[]#[]"; + << "/:@/[]" << ":/?@[]?" << ":/?@[]" + << "x://::@host/:@/[]?:/?@[]?#:/?@[]"; + + // 3) and test that the same encoded sequences remain encoded + QTest::newRow("encoded-gendelims-unchanging") << QUrl("x://:%3A@host/%3A%40%5B%5D?%3A%2F%3F%40%5B%5D#%23%3A%2F%3F%40%5B%5D") + << MostDecoded + << "" << "%3A" << ":%3A" + << "host" << ":%3A@host" + << "/%3A%40%5B%5D" << "%3A%2F%3F%40%5B%5D" << "%23%3A%2F%3F%40%5B%5D" + << "x://:%3A@host/%3A%40%5B%5D?%3A%2F%3F%40%5B%5D#%23%3A%2F%3F%40%5B%5D"; // test the query // since QUrl doesn't know what chars the user wants to use for the pair and value delimiters, @@ -3051,23 +3248,13 @@ void tst_QUrl::componentEncodings_data() << QString::fromUtf8("é ") << QString::fromUtf8("x:// é:é @smørbrød.example.no/é ? é#é "); - // the pretty form re-encodes the subdelims (except in the query, where they are left alone) - QTest::newRow("pretty-subdelims") << QUrl("x://%21%24%26:%27%28%29@host/%2a%2b%2c?%26=%26&%3d=%3d#%3b%3d") - << int(QUrl::PrettyDecoded) - << "!$&" << "'()" << "!$&:'()" - << "host" << "!$&:'()@host" - << "/*+," << "%26=%26&%3D=%3D" << ";=" - << "x://!$&:'()@host/*+,?%26=%26&%3D=%3D#;="; - - // the pretty form decodes all unambiguous gen-delims - // (except in query, where they are left alone) - QTest::newRow("pretty-gendelims") << QUrl("x://%5b%3a%40%2f%5d:%5b%3a%40%2f%5d@host" - "/%3a%40%5b%3f%23%5d?[?%3f%23]%5b:%3a@%40%5d#%23") + // the pretty form decodes all unambiguous gen-delims in the individual parts + QTest::newRow("pretty-gendelims") << QUrl("x://%5b%3a%40%2f%3f%23%5d:%5b%40%2f%3f%23%5d@host/%3f%23?%23") << int(QUrl::PrettyDecoded) - << "[:@/]" << "[:@/]" << "[%3A@/]:[:@/]" - << "host" << "%5B%3A%40/%5D:%5B:%40/%5D@host" - << "/:@[?#]" << "[?%3F#]%5B:%3A@%40%5D" << "#" - << "x://%5B%3A%40%2F%5D:%5B:%40%2F%5D@host/:@[%3F%23]?[?%3F%23]%5B:%3A@%40%5D##"; + << "[:@/?#]" << "[@/?#]" << "[%3A@/?#]:[@/?#]" + << "host" << "%5B%3A%40/?#%5D:%5B%40/?#%5D@host" + << "/?#" << "#" << "" + << "x://%5B%3A%40%2F%3F%23%5D:%5B%40%2F%3F%23%5D@host/%3F%23?%23"; // the pretty form keeps the other characters decoded everywhere // except when rebuilding the full URL, when we only allow "{}" to remain decoded @@ -3076,8 +3263,8 @@ void tst_QUrl::componentEncodings_data() << "\"<>^\\{|}" << "\"<>^\\{|}" << "\"<>^\\{|}:\"<>^\\{|}" << "host" << "\"<>^\\{|}:\"<>^\\{|}@host" << "/\"<>^\\{|}" << "\"<>^\\{|}" << "\"<>^\\{|}" - << "x://%22%3C%3E%5E%5C%7B%7C%7D:%22%3C%3E%5E%5C%7B%7C%7D@host/%22%3C%3E%5E%5C{%7C}" - "?%22%3C%3E%5E%5C{%7C}#%22%3C%3E%5E%5C%7B%7C%7D"; + << "x://%22%3C%3E%5E%5C%7B%7C%7D:%22%3C%3E%5E%5C%7B%7C%7D@host/%22%3C%3E%5E%5C%7B%7C%7D" + "?%22%3C%3E%5E%5C%7B%7C%7D#%22%3C%3E%5E%5C%7B%7C%7D"; } void tst_QUrl::componentEncodings() @@ -3181,6 +3368,17 @@ void tst_QUrl::setComponents_data() QTest::newRow("path-empty") << QUrl("http://example.com/path") << int(Path) << "" << Tolerant << true << PrettyDecoded << "" << "http://example.com"; + // If the %3A gets decoded to ":", the URL becomes invalid; + // see test path-invalid-1 below + QTest::newRow("path-%3A-before-slash") << QUrl() + << int(Path) << "c%3A/" << Tolerant << true + << PrettyDecoded << "c%3A/" << "c%3A/"; + QTest::newRow("path-doubleslash") << QUrl("trash:/") + << int(Path) << "//path" << Tolerant << true + << PrettyDecoded << "/path" << "trash:/path"; + QTest::newRow("path-withdotdot") << QUrl("file:///tmp") + << int(Path) << "//tmp/..///root/." << Tolerant << true + << PrettyDecoded << "/root" << "file:///root"; // the other fields can be present and be empty // that is, their delimiters would be present, but there would be nothing to one side @@ -3346,9 +3544,6 @@ void tst_QUrl::setComponents_data() QTest::newRow("invalid-scheme-encode") << QUrl("http://example.com") << int(Scheme) << "http%61" << Decoded << false << PrettyDecoded << "" << ""; - QTest::newRow("userinfo-encode") << QUrl("http://example.com") - << int(UserInfo) << "h%61llo:world@" << Decoded << true - << PrettyDecoded << "h%2561llo:world@" << "http://h%2561llo:world%40@example.com"; QTest::newRow("username-encode") << QUrl("http://example.com") << int(UserName) << "h%61llo:world" << Decoded << true << PrettyDecoded << "h%2561llo:world" << "http://h%2561llo%3Aworld@example.com"; @@ -3359,9 +3554,6 @@ void tst_QUrl::setComponents_data() QTest::newRow("invalid-host-encode") << QUrl("http://example.com") << int(Host) << "ex%61mple.com" << Decoded << false << PrettyDecoded << "" << ""; - QTest::newRow("invalid-authority-encode") << QUrl("http://example.com") - << int(Authority) << "ex%61mple.com" << Decoded << false - << PrettyDecoded << "" << ""; QTest::newRow("path-encode") << QUrl("http://example.com/foo") << int(Path) << "/bar%23" << Decoded << true << PrettyDecoded << "/bar%2523" << "http://example.com/bar%2523"; @@ -3371,11 +3563,7 @@ void tst_QUrl::setComponents_data() QTest::newRow("fragment-encode") << QUrl("http://example.com/foo#z") << int(Fragment) << "bar%23" << Decoded << true << PrettyDecoded << "bar%2523" << "http://example.com/foo#bar%2523"; - // force decoding; note how the userinfo becomes ambiguous - QTest::newRow("userinfo-decode") << QUrl("http://example.com") - << int(UserInfo) << "hello%3Aworld:}}>b9o%25kR(" << Tolerant << true - << FullyDecoded << "hello:world:}}>b9o%kR(" - << "http://hello%3Aworld:%7D%7D%3Eb9o%25kR(@example.com"; + // force decoding QTest::newRow("username-decode") << QUrl("http://example.com") << int(UserName) << "hello%3Aworld%25" << Tolerant << true << FullyDecoded << "hello:world%" << "http://hello%3Aworld%25@example.com"; @@ -3460,6 +3648,8 @@ void tst_QUrl::setComponents() if (isValid) { QFETCH(QString, toString); QCOMPARE(copy.toString(), toString); + // Check round-tripping + QCOMPARE(QUrl(copy.toString()).toString(), toString); } else { QVERIFY(copy.toString().isEmpty()); } diff --git a/tests/auto/corelib/io/qurlinternal/tst_qurlinternal.cpp b/tests/auto/corelib/io/qurlinternal/tst_qurlinternal.cpp index 2014045171..05d5f94e3d 100644 --- a/tests/auto/corelib/io/qurlinternal/tst_qurlinternal.cpp +++ b/tests/auto/corelib/io/qurlinternal/tst_qurlinternal.cpp @@ -824,12 +824,20 @@ void tst_QUrlInternal::correctEncodedMistakes() QFETCH(QString, expected); // prepend some data to be sure that it remains there - QString output = QTest::currentDataTag(); - expected.prepend(output); + QString dataTag = QTest::currentDataTag(); + QString output = dataTag; if (!qt_urlRecode(output, input.constData(), input.constData() + input.length(), 0)) output += input; - QCOMPARE(output, expected); + QCOMPARE(output, dataTag + expected); + + // now try the full decode mode + output = dataTag; + QString expected2 = QUrl::fromPercentEncoding(expected.toLatin1()); + + if (!qt_urlRecode(output, input.constData(), input.constData() + input.length(), QUrl::FullyDecoded)) + output += input; + QCOMPARE(output, dataTag + expected2); } static void addUtf8Data(const char *name, const char *data) @@ -1027,6 +1035,15 @@ void tst_QUrlInternal::encodingRecodeInvalidUtf8() if (!qt_urlRecode(output, input.constData(), input.constData() + input.length(), QUrl::FullyEncoded)) output += input; QCOMPARE(output, QTest::currentDataTag() + input); + + // verify for security reasons that all bad UTF-8 data got replaced by QChar::ReplacementCharacter + output = QTest::currentDataTag(); + if (!qt_urlRecode(output, input.constData(), input.constData() + input.length(), QUrl::FullyEncoded)) + output += input; + for (int i = strlen(QTest::currentDataTag()); i < output.length(); ++i) { + QVERIFY2(output.at(i).unicode() < 0x80 || output.at(i) == QChar::ReplacementCharacter, + qPrintable(QString("Character at i == %1 was U+%2").arg(i).arg(output.at(i).unicode(), 4, 16, QLatin1Char('0')))); + } } void tst_QUrlInternal::recodeByteArray_data() diff --git a/tests/auto/corelib/json/tst_qtjson.cpp b/tests/auto/corelib/json/tst_qtjson.cpp index 94e6e1129e..9dbd6414ad 100644 --- a/tests/auto/corelib/json/tst_qtjson.cpp +++ b/tests/auto/corelib/json/tst_qtjson.cpp @@ -45,6 +45,7 @@ #include "qjsonobject.h" #include "qjsonvalue.h" #include "qjsondocument.h" +#include <limits> #define INVALID_UNICODE "\357\277\277" // "\uffff" #define UNICODE_DJE "\320\202" // Character from the Serbian Cyrillic alphabet @@ -97,6 +98,8 @@ private Q_SLOTS: void toVariantList(); void toJson(); + void toJsonSillyNumericValues(); + void toJsonLargeNumericValues(); void fromJson(); void fromJsonErrors(); void fromBinary(); @@ -446,13 +449,13 @@ void tst_QtJson::testObjectSimple() void tst_QtJson::testObjectSmallKeys() { QJsonObject data1; - data1.insert(QStringLiteral("1"), 123); + data1.insert(QStringLiteral("1"), 123.); QVERIFY(data1.contains(QStringLiteral("1"))); QCOMPARE(data1.value(QStringLiteral("1")).toDouble(), (double)123); - data1.insert(QStringLiteral("12"), 133); + data1.insert(QStringLiteral("12"), 133.); QCOMPARE(data1.value(QStringLiteral("12")).toDouble(), (double)133); QVERIFY(data1.contains(QStringLiteral("12"))); - data1.insert(QStringLiteral("123"), 323); + data1.insert(QStringLiteral("123"), 323.); QCOMPARE(data1.value(QStringLiteral("12")).toDouble(), (double)133); QVERIFY(data1.contains(QStringLiteral("123"))); QCOMPARE(data1.value(QStringLiteral("123")).type(), QJsonValue::Double); @@ -667,11 +670,15 @@ void tst_QtJson::testValueRef() array.append(1.); array.append(2.); array.append(3.); + array.append(4); + array.append(4.1); array[1] = false; - QCOMPARE(array.size(), 3); + QCOMPARE(array.size(), 5); QCOMPARE(array.at(0).toDouble(), 1.); QCOMPARE(array.at(2).toDouble(), 3.); + QCOMPARE(array.at(3).toInt(), 4); + QCOMPARE(array.at(4).toInt(), 0); QCOMPARE(array.at(1).type(), QJsonValue::Bool); QCOMPARE(array.at(1).toBool(), false); @@ -1201,6 +1208,89 @@ void tst_QtJson::toJson() } } +void tst_QtJson::toJsonSillyNumericValues() +{ + QJsonObject object; + QJsonArray array; + array.append(QJsonValue(std::numeric_limits<double>::infinity())); // encode to: null + array.append(QJsonValue(-std::numeric_limits<double>::infinity())); // encode to: null + array.append(QJsonValue(std::numeric_limits<double>::quiet_NaN())); // encode to: null + object.insert("Array", array); + + QByteArray json = QJsonDocument(object).toJson(); + + QByteArray expected = + "{\n" + " \"Array\": [\n" + " null,\n" + " null,\n" + " null\n" + " ]\n" + "}\n"; + + QCOMPARE(json, expected); + + QJsonDocument doc; + doc.setObject(object); + json = doc.toJson(); + QCOMPARE(json, expected); +} + +void tst_QtJson::toJsonLargeNumericValues() +{ + QJsonObject object; + QJsonArray array; + array.append(QJsonValue(1.234567)); // actual precision bug in Qt 5.0.0 + array.append(QJsonValue(1.7976931348623157e+308)); // JS Number.MAX_VALUE + array.append(QJsonValue(5e-324)); // JS Number.MIN_VALUE + array.append(QJsonValue(std::numeric_limits<double>::min())); + array.append(QJsonValue(std::numeric_limits<double>::max())); + array.append(QJsonValue(std::numeric_limits<double>::epsilon())); + array.append(QJsonValue(std::numeric_limits<double>::denorm_min())); + array.append(QJsonValue(0.0)); + array.append(QJsonValue(-std::numeric_limits<double>::min())); + array.append(QJsonValue(-std::numeric_limits<double>::max())); + array.append(QJsonValue(-std::numeric_limits<double>::epsilon())); + array.append(QJsonValue(-std::numeric_limits<double>::denorm_min())); + array.append(QJsonValue(-0.0)); + array.append(QJsonValue(9007199254740992LL)); // JS Number max integer + array.append(QJsonValue(-9007199254740992LL)); // JS Number min integer + object.insert("Array", array); + + QByteArray json = QJsonDocument(object).toJson(); + + QByteArray expected = + "{\n" + " \"Array\": [\n" + " 1.234567,\n" + " 1.7976931348623157e+308,\n" + // ((4.9406564584124654e-324 == 5e-324) == true) + // I can only think JavaScript has a special formatter to + // emit this value for this IEEE754 bit pattern. + " 4.9406564584124654e-324,\n" + " 2.2250738585072014e-308,\n" + " 1.7976931348623157e+308,\n" + " 2.2204460492503131e-16,\n" + " 4.9406564584124654e-324,\n" + " 0,\n" + " -2.2250738585072014e-308,\n" + " -1.7976931348623157e+308,\n" + " -2.2204460492503131e-16,\n" + " -4.9406564584124654e-324,\n" + " 0,\n" + " 9007199254740992,\n" + " -9007199254740992\n" + " ]\n" + "}\n"; + + QCOMPARE(json, expected); + + QJsonDocument doc; + doc.setObject(object); + json = doc.toJson(); + QCOMPARE(json, expected); +} + void tst_QtJson::fromJson() { { @@ -2084,6 +2174,16 @@ void tst_QtJson::valueEquals() QVERIFY(QJsonValue(true) != QJsonValue(QJsonArray())); QVERIFY(QJsonValue(true) != QJsonValue(QJsonObject())); + QVERIFY(QJsonValue(1) == QJsonValue(1)); + QVERIFY(QJsonValue(1) != QJsonValue(2)); + QVERIFY(QJsonValue(1) == QJsonValue(1.)); + QVERIFY(QJsonValue(1) != QJsonValue(1.1)); + QVERIFY(QJsonValue(1) != QJsonValue(QJsonValue::Undefined)); + QVERIFY(QJsonValue(1) != QJsonValue()); + QVERIFY(QJsonValue(1) != QJsonValue(true)); + QVERIFY(QJsonValue(1) != QJsonValue(QJsonArray())); + QVERIFY(QJsonValue(1) != QJsonValue(QJsonObject())); + QVERIFY(QJsonValue(1.) == QJsonValue(1.)); QVERIFY(QJsonValue(1.) != QJsonValue(2.)); QVERIFY(QJsonValue(1.) != QJsonValue(QJsonValue::Undefined)); diff --git a/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp b/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp index 1c07425fc7..93cf799982 100644 --- a/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp +++ b/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp @@ -148,8 +148,7 @@ void tst_QEventDispatcher::registerTimer() // process events, waiting for the next event... this should only fire the precise timer receivedEventType = -1; timerIdFromEvent = -1; - QVERIFY(eventDispatcher->processEvents(QEventLoop::WaitForMoreEvents)); - QCOMPARE(receivedEventType, int(QEvent::Timer)); + QTRY_COMPARE_WITH_TIMEOUT(receivedEventType, int(QEvent::Timer), PreciseTimerInterval * 2); QCOMPARE(timerIdFromEvent, preciseTimerId); // now unregister it and make sure it's gone eventDispatcher->unregisterTimer(preciseTimerId); @@ -161,8 +160,7 @@ void tst_QEventDispatcher::registerTimer() // do the same again for the coarse timer receivedEventType = -1; timerIdFromEvent = -1; - QVERIFY(eventDispatcher->processEvents(QEventLoop::WaitForMoreEvents)); - QCOMPARE(receivedEventType, int(QEvent::Timer)); + QTRY_COMPARE_WITH_TIMEOUT(receivedEventType, int(QEvent::Timer), CoarseTimerInterval * 2); QCOMPARE(timerIdFromEvent, coarseTimerId); // now unregister it and make sure it's gone eventDispatcher->unregisterTimer(coarseTimerId); diff --git a/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp b/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp index df374ffc23..c6d04e64db 100644 --- a/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp +++ b/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp @@ -195,11 +195,11 @@ protected: void tst_QEventLoop::processEvents() { - QSignalSpy spy1(QAbstractEventDispatcher::instance(), SIGNAL(aboutToBlock())); - QSignalSpy spy2(QAbstractEventDispatcher::instance(), SIGNAL(awake())); + QSignalSpy aboutToBlockSpy(QAbstractEventDispatcher::instance(), SIGNAL(aboutToBlock())); + QSignalSpy awakeSpy(QAbstractEventDispatcher::instance(), SIGNAL(awake())); - QVERIFY(spy1.isValid()); - QVERIFY(spy2.isValid()); + QVERIFY(aboutToBlockSpy.isValid()); + QVERIFY(awakeSpy.isValid()); QEventLoop eventLoop; @@ -208,8 +208,8 @@ void tst_QEventLoop::processEvents() // process posted events, QEventLoop::processEvents() should return // true QVERIFY(eventLoop.processEvents()); - QCOMPARE(spy1.count(), 0); - QCOMPARE(spy2.count(), 1); + QCOMPARE(aboutToBlockSpy.count(), 0); + QCOMPARE(awakeSpy.count(), 1); // allow any session manager to complete its handshake, so that // there are no pending events left. @@ -222,28 +222,28 @@ void tst_QEventLoop::processEvents() // no events to process, QEventLoop::processEvents() should return // false - spy1.clear(); - spy2.clear(); + aboutToBlockSpy.clear(); + awakeSpy.clear(); QVERIFY(!eventLoop.processEvents()); - QCOMPARE(spy1.count(), 0); - QCOMPARE(spy2.count(), 1); + QCOMPARE(aboutToBlockSpy.count(), 0); + QCOMPARE(awakeSpy.count(), 1); // make sure the test doesn't block forever int timerId = startTimer(100); // wait for more events to process, QEventLoop::processEvents() // should return true - spy1.clear(); - spy2.clear(); + aboutToBlockSpy.clear(); + awakeSpy.clear(); QVERIFY(eventLoop.processEvents(QEventLoop::WaitForMoreEvents)); // Verify that the eventloop has blocked and woken up. Some eventloops // may block and wake up multiple times. - QVERIFY(spy1.count() > 0); - QVERIFY(spy2.count() > 0); + QVERIFY(aboutToBlockSpy.count() > 0); + QVERIFY(awakeSpy.count() > 0); // We should get one awake for each aboutToBlock, plus one awake when // processEvents is entered. - QVERIFY(spy2.count() >= spy1.count()); + QVERIFY(awakeSpy.count() >= aboutToBlockSpy.count()); killTimer(timerId); } diff --git a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp index 98e90ed805..009b4fadda 100644 --- a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp +++ b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp @@ -205,7 +205,7 @@ void tst_QMetaMethod::method_data() << (QList<int>()) << (QList<QByteArray>()) << (QList<QByteArray>()) - << QMetaMethod::Protected + << QMetaMethod::Public << QMetaMethod::Signal; QTest::newRow("voidInvokable") @@ -241,7 +241,7 @@ void tst_QMetaMethod::method_data() << QList<int>() << QList<QByteArray>() << QList<QByteArray>() - << QMetaMethod::Protected + << QMetaMethod::Public << QMetaMethod::Signal; QTest::newRow("voidSignalInt") @@ -250,7 +250,7 @@ void tst_QMetaMethod::method_data() << (QList<int>() << int(QMetaType::Int)) << (QList<QByteArray>() << QByteArray("int")) << (QList<QByteArray>() << QByteArray("voidSignalIntArg")) - << QMetaMethod::Protected + << QMetaMethod::Public << QMetaMethod::Signal; QTest::newRow("voidInvokableInt") @@ -286,7 +286,7 @@ void tst_QMetaMethod::method_data() << (QList<int>() << qMetaTypeId<qreal>()) << (QList<QByteArray>() << QByteArray("qreal")) << (QList<QByteArray>() << QByteArray("voidSignalQRealArg")) - << QMetaMethod::Protected + << QMetaMethod::Public << QMetaMethod::Signal; QTest::newRow("voidInvokableQReal") @@ -322,7 +322,7 @@ void tst_QMetaMethod::method_data() << (QList<int>() << int(QMetaType::QString)) << (QList<QByteArray>() << QByteArray("QString")) << (QList<QByteArray>() << QByteArray("voidSignalQStringArg")) - << QMetaMethod::Protected + << QMetaMethod::Public << QMetaMethod::Signal; QTest::newRow("voidInvokableQString") @@ -358,7 +358,7 @@ void tst_QMetaMethod::method_data() << (QList<int>() << qMetaTypeId<CustomType>()) << (QList<QByteArray>() << QByteArray("CustomType")) << (QList<QByteArray>() << QByteArray("voidSignalCustomTypeArg")) - << QMetaMethod::Protected + << QMetaMethod::Public << QMetaMethod::Signal; QTest::newRow("voidInvokableCustomType") @@ -394,7 +394,7 @@ void tst_QMetaMethod::method_data() << (QList<int>() << 0) << (QList<QByteArray>() << QByteArray("CustomUnregisteredType")) << (QList<QByteArray>() << QByteArray("voidSignalCustomUnregisteredTypeArg")) - << QMetaMethod::Protected + << QMetaMethod::Public << QMetaMethod::Signal; QTest::newRow("voidInvokableCustomUnregisteredType") @@ -430,7 +430,7 @@ void tst_QMetaMethod::method_data() << (QList<int>()) << (QList<QByteArray>()) << (QList<QByteArray>()) - << QMetaMethod::Protected + << QMetaMethod::Public << QMetaMethod::Signal; QTest::newRow("boolInvokable") @@ -457,7 +457,7 @@ void tst_QMetaMethod::method_data() << (QList<int>()) << (QList<QByteArray>()) << (QList<QByteArray>()) - << QMetaMethod::Protected + << QMetaMethod::Public << QMetaMethod::Signal; QTest::newRow("qrealInvokable") @@ -484,7 +484,7 @@ void tst_QMetaMethod::method_data() << (QList<int>()) << (QList<QByteArray>()) << (QList<QByteArray>()) - << QMetaMethod::Protected + << QMetaMethod::Public << QMetaMethod::Signal; QTest::newRow("qstringInvokable") @@ -529,7 +529,7 @@ void tst_QMetaMethod::method_data() "bool,int,uint,qlonglong,qulonglong,double,long,short,char,ulong,ushort,uchar,float)") << int(QMetaType::QVariant) << QByteArray("QVariant") << parameterTypes << parameterTypeNames << parameterNames - << QMetaMethod::Protected + << QMetaMethod::Public << QMetaMethod::Signal; QTest::newRow("qvariantInvokableBoolIntUIntLonglongULonglongDoubleLongShortCharUlongUshortUcharFloat") @@ -562,7 +562,7 @@ void tst_QMetaMethod::method_data() << (QList<int>() << int(QMetaType::Bool) << int(QMetaType::Int)) << (QList<QByteArray>() << QByteArray("bool") << QByteArray("int")) << (QList<QByteArray>() << QByteArray("") << QByteArray("")) - << QMetaMethod::Protected + << QMetaMethod::Public << QMetaMethod::Signal; QTest::newRow("voidInvokableNoParameterNames") diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 137ce56730..8f6bd50cca 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -397,7 +397,7 @@ void tst_QMetaObjectBuilder::signal() QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QVERIFY(method1.parameterNames().isEmpty()); QVERIFY(method1.tag().isEmpty()); - QVERIFY(method1.access() == QMetaMethod::Protected); + QVERIFY(method1.access() == QMetaMethod::Public); QCOMPARE(method1.attributes(), 0); QCOMPARE(method1.index(), 0); QCOMPARE(builder.methodCount(), 1); @@ -410,7 +410,7 @@ void tst_QMetaObjectBuilder::signal() QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); - QVERIFY(method2.access() == QMetaMethod::Protected); + QVERIFY(method2.access() == QMetaMethod::Public); QCOMPARE(method2.attributes(), 0); QCOMPARE(method2.index(), 1); QCOMPARE(builder.methodCount(), 2); diff --git a/tests/auto/corelib/kernel/qmetatype/qmetatype.pro b/tests/auto/corelib/kernel/qmetatype/qmetatype.pro index 5009fedc4f..23a8e6d23a 100644 --- a/tests/auto/corelib/kernel/qmetatype/qmetatype.pro +++ b/tests/auto/corelib/kernel/qmetatype/qmetatype.pro @@ -1,6 +1,7 @@ CONFIG += testcase parallel_test TARGET = tst_qmetatype QT = core testlib +INCLUDEPATH += $$PWD/../../../other/qvariant_common SOURCES = tst_qmetatype.cpp TESTDATA=./typeFlags.bin DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index 77ea39da53..47900204e7 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -43,6 +43,8 @@ #include <QtCore> #include <QtTest/QtTest> +#include "tst_qvariant_common.h" + #ifdef Q_OS_LINUX # include <pthread.h> #endif @@ -111,6 +113,11 @@ private slots: void metaObject(); void constexprMetaTypeIds(); void constRefs(); + void convertCustomType_data(); + void convertCustomType(); + void compareCustomType_data(); + void compareCustomType(); + void customDebugStream(); }; struct Foo { int i; }; @@ -1302,15 +1309,20 @@ Q_DECLARE_METATYPE(MyObjectPtr) void tst_QMetaType::automaticTemplateRegistration() { - { - QList<int> intList; - intList << 42; - QVERIFY(QVariant::fromValue(intList).value<QList<int> >().first() == 42); - QVector<QList<int> > vectorList; - vectorList << intList; - QVERIFY(QVariant::fromValue(vectorList).value<QVector<QList<int> > >().first().first() == 42); +#define TEST_SEQUENTIAL_CONTAINER(CONTAINER, VALUE_TYPE) \ + { \ + CONTAINER<VALUE_TYPE> innerContainer; \ + innerContainer.push_back(42); \ + QVERIFY(*QVariant::fromValue(innerContainer).value<CONTAINER<VALUE_TYPE> >().begin() == 42); \ + QVector<CONTAINER<VALUE_TYPE> > outerContainer; \ + outerContainer << innerContainer; \ + QVERIFY(*QVariant::fromValue(outerContainer).value<QVector<CONTAINER<VALUE_TYPE> > >().first().begin() == 42); \ } + TEST_SEQUENTIAL_CONTAINER(QList, int) + TEST_SEQUENTIAL_CONTAINER(std::vector, int) + TEST_SEQUENTIAL_CONTAINER(std::list, int) + { QList<QByteArray> bytearrayList; bytearrayList << QByteArray("foo"); @@ -1323,14 +1335,9 @@ void tst_QMetaType::automaticTemplateRegistration() QCOMPARE(::qMetaTypeId<QVariantList>(), (int)QMetaType::QVariantList); QCOMPARE(::qMetaTypeId<QList<QVariant> >(), (int)QMetaType::QVariantList); - { - QList<QVariant> variantList; - variantList << 42; - QVERIFY(QVariant::fromValue(variantList).value<QList<QVariant> >().first() == 42); - QVector<QList<QVariant> > vectorList; - vectorList << variantList; - QVERIFY(QVariant::fromValue(vectorList).value<QVector<QList<QVariant> > >().first().first() == 42); - } + TEST_SEQUENTIAL_CONTAINER(QList, QVariant) + TEST_SEQUENTIAL_CONTAINER(std::vector, QVariant) + TEST_SEQUENTIAL_CONTAINER(std::list, QVariant) { QList<QSharedPointer<QObject> > sharedPointerList; @@ -1395,6 +1402,31 @@ void tst_QMetaType::automaticTemplateRegistration() QCOMPARE(QVariant::fromValue(variantMap).value<QVariantMap>().value(QStringLiteral("4")), QVariant(2)); } { + typedef std::map<int, int> IntIntMap; + IntIntMap intIntMap; + intIntMap[4] = 2; + QCOMPARE(QVariant::fromValue(intIntMap).value<IntIntMap>()[4], 2); + } + { + typedef std::map<int, uint> StdIntUIntMap; + StdIntUIntMap intUIntMap; + intUIntMap[4] = 2; + QCOMPARE(QVariant::fromValue(intUIntMap).value<StdIntUIntMap>()[4], (uint)2); + } + { + typedef std::map<int, CustomObject*> StdMapIntCustomObject ; + StdMapIntCustomObject intComparableMap; + CustomObject *o = 0; + intComparableMap[4] = o; + QCOMPARE(QVariant::fromValue(intComparableMap).value<StdMapIntCustomObject >()[4], o); + } + { + typedef std::map<QString, QVariant> StdMapStringVariant; + StdMapStringVariant variantMap; + variantMap[QStringLiteral("4")] = 2; + QCOMPARE(QVariant::fromValue(variantMap).value<StdMapStringVariant>()[QStringLiteral("4")], QVariant(2)); + } + { typedef QPair<int, int> IntIntPair; IntIntPair intIntPair = qMakePair(4, 2); QCOMPARE(QVariant::fromValue(intIntPair).value<IntIntPair>().first, 4); @@ -1412,6 +1444,25 @@ void tst_QMetaType::automaticTemplateRegistration() QCOMPARE(QVariant::fromValue(intComparablePair).value<IntComparablePair>().second, m); } { + typedef std::pair<int, int> IntIntPair; + IntIntPair intIntPair = std::make_pair(4, 2); + QCOMPARE(QVariant::fromValue(intIntPair).value<IntIntPair>().first, 4); + QCOMPARE(QVariant::fromValue(intIntPair).value<IntIntPair>().second, 2); + } + { + typedef std::pair<int, uint> StdIntUIntPair; + StdIntUIntPair intUIntPair = std::make_pair<int, uint>(4, 2); + QCOMPARE(QVariant::fromValue(intUIntPair).value<StdIntUIntPair>().first, 4); + QCOMPARE(QVariant::fromValue(intUIntPair).value<StdIntUIntPair>().second, (uint)2); + } + { + typedef std::pair<int, CustomQObject*> StdIntComparablePair; + CustomQObject* o = 0; + StdIntComparablePair intComparablePair = std::make_pair(4, o); + QCOMPARE(QVariant::fromValue(intComparablePair).value<StdIntComparablePair>().first, 4); + QCOMPARE(QVariant::fromValue(intComparablePair).value<StdIntComparablePair>().second, o); + } + { typedef QHash<int, UnregisteredType> IntUnregisteredTypeHash; QVERIFY(qRegisterMetaType<IntUnregisteredTypeHash>("IntUnregisteredTypeHash") > 0); } @@ -1446,17 +1497,14 @@ void tst_QMetaType::automaticTemplateRegistration() F(uint, __VA_ARGS__) \ F(qlonglong, __VA_ARGS__) \ F(qulonglong, __VA_ARGS__) \ - F(double, __VA_ARGS__) \ F(long, __VA_ARGS__) \ F(short, __VA_ARGS__) \ F(char, __VA_ARGS__) \ F(ulong, __VA_ARGS__) \ F(ushort, __VA_ARGS__) \ F(uchar, __VA_ARGS__) \ - F(float, __VA_ARGS__) \ F(QObject*, __VA_ARGS__) \ - F(QString, __VA_ARGS__) \ - F(CustomMovable, __VA_ARGS__) + F(QString, __VA_ARGS__) #define CREATE_AND_VERIFY_CONTAINER(CONTAINER, ...) \ @@ -1774,6 +1822,366 @@ void tst_QMetaType::constRefs() #endif } +struct CustomConvertibleType +{ + explicit CustomConvertibleType(const QVariant &foo = QVariant()) : m_foo(foo) {} + virtual ~CustomConvertibleType() {} + QString toString() const { return m_foo.toString(); } + operator QPoint() const { return QPoint(12, 34); } + template<typename To> + To convert() const { return s_value.value<To>();} + template<typename To> + To convertOk(bool *ok) const { *ok = s_ok; return s_value.value<To>();} + + QVariant m_foo; + static QVariant s_value; + static bool s_ok; +}; + +bool operator<(const CustomConvertibleType &lhs, const CustomConvertibleType &rhs) +{ return lhs.m_foo < rhs.m_foo; } +bool operator==(const CustomConvertibleType &lhs, const CustomConvertibleType &rhs) +{ return lhs.m_foo == rhs.m_foo; } +bool operator!=(const CustomConvertibleType &lhs, const CustomConvertibleType &rhs) +{ return !operator==(lhs, rhs); } + +QVariant CustomConvertibleType::s_value; +bool CustomConvertibleType::s_ok = true; + +struct CustomConvertibleType2 +{ + // implicit + CustomConvertibleType2(const CustomConvertibleType &t = CustomConvertibleType()) + : m_foo(t.m_foo) {} + virtual ~CustomConvertibleType2() {} + + QVariant m_foo; +}; + +struct CustomDebugStreamableType +{ + QString toString() const { return "test"; } +}; + +QDebug operator<<(QDebug dbg, const CustomDebugStreamableType&) +{ + return dbg << "string-content"; +} + +bool operator==(const CustomConvertibleType2 &lhs, const CustomConvertibleType2 &rhs) +{ return lhs.m_foo == rhs.m_foo; } +bool operator!=(const CustomConvertibleType2 &lhs, const CustomConvertibleType2 &rhs) +{ return !operator==(lhs, rhs); } + +Q_DECLARE_METATYPE(CustomConvertibleType); +Q_DECLARE_METATYPE(CustomConvertibleType2); +Q_DECLARE_METATYPE(CustomDebugStreamableType); + +template<typename T, typename U> +U convert(const T &t) +{ + return t; +} + +template<typename From> +struct ConvertFunctor +{ + CustomConvertibleType operator()(const From& f) const + { + return CustomConvertibleType(QVariant::fromValue(f)); + } +}; + +template<typename From, typename To> +bool hasRegisteredConverterFunction() +{ + return QMetaType::hasRegisteredConverterFunction<From, To>(); +} + +template<typename From, typename To> +void testCustomTypeNotYetConvertible() +{ + QVERIFY((!hasRegisteredConverterFunction<From, To>())); + QVERIFY((!QVariant::fromValue<From>(From()).canConvert(qMetaTypeId<To>()))); +} + +template<typename From, typename To> +void testCustomTypeConvertible() +{ + QVERIFY((hasRegisteredConverterFunction<From, To>())); + QVERIFY((QVariant::fromValue<From>(From()).canConvert(qMetaTypeId<To>()))); +} + +void customTypeNotYetConvertible() +{ + testCustomTypeNotYetConvertible<CustomConvertibleType, QString>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, bool>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, int>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, double>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, float>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QRect>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QRectF>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QPoint>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QPointF>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QSize>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QSizeF>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QLine>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QLineF>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QChar>(); + testCustomTypeNotYetConvertible<QString, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<bool, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<int, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<double, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<float, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QRect, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QRectF, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QPoint, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QPointF, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QSize, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QSizeF, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QLine, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QLineF, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QChar, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, CustomConvertibleType2>(); +} + +void registerCustomTypeConversions() +{ + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QString>(&CustomConvertibleType::convertOk<QString>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, bool>(&CustomConvertibleType::convert<bool>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, int>(&CustomConvertibleType::convertOk<int>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, double>(&CustomConvertibleType::convert<double>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, float>(&CustomConvertibleType::convertOk<float>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QRect>(&CustomConvertibleType::convert<QRect>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QRectF>(&CustomConvertibleType::convertOk<QRectF>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QPoint>(convert<CustomConvertibleType,QPoint>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QPointF>(&CustomConvertibleType::convertOk<QPointF>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QSize>(&CustomConvertibleType::convert<QSize>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QSizeF>(&CustomConvertibleType::convertOk<QSizeF>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLine>(&CustomConvertibleType::convert<QLine>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLineF>(&CustomConvertibleType::convertOk<QLineF>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QChar>(&CustomConvertibleType::convert<QChar>))); + QVERIFY((QMetaType::registerConverter<QString, CustomConvertibleType>(ConvertFunctor<QString>()))); + QVERIFY((QMetaType::registerConverter<bool, CustomConvertibleType>(ConvertFunctor<bool>()))); + QVERIFY((QMetaType::registerConverter<int, CustomConvertibleType>(ConvertFunctor<int>()))); + QVERIFY((QMetaType::registerConverter<double, CustomConvertibleType>(ConvertFunctor<double>()))); + QVERIFY((QMetaType::registerConverter<float, CustomConvertibleType>(ConvertFunctor<float>()))); + QVERIFY((QMetaType::registerConverter<QRect, CustomConvertibleType>(ConvertFunctor<QRect>()))); + QVERIFY((QMetaType::registerConverter<QRectF, CustomConvertibleType>(ConvertFunctor<QRectF>()))); + QVERIFY((QMetaType::registerConverter<QPoint, CustomConvertibleType>(ConvertFunctor<QPoint>()))); + QVERIFY((QMetaType::registerConverter<QPointF, CustomConvertibleType>(ConvertFunctor<QPointF>()))); + QVERIFY((QMetaType::registerConverter<QSize, CustomConvertibleType>(ConvertFunctor<QSize>()))); + QVERIFY((QMetaType::registerConverter<QSizeF, CustomConvertibleType>(ConvertFunctor<QSizeF>()))); + QVERIFY((QMetaType::registerConverter<QLine, CustomConvertibleType>(ConvertFunctor<QLine>()))); + QVERIFY((QMetaType::registerConverter<QLineF, CustomConvertibleType>(ConvertFunctor<QLineF>()))); + QVERIFY((QMetaType::registerConverter<QChar, CustomConvertibleType>(ConvertFunctor<QChar>()))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>())); + QTest::ignoreMessage(QtWarningMsg, "Type conversion already registered from type CustomConvertibleType to type CustomConvertibleType2"); + QVERIFY((!QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>())); +} + +void tst_QMetaType::convertCustomType_data() +{ + customTypeNotYetConvertible(); + registerCustomTypeConversions(); + + QTest::addColumn<bool>("ok"); + QTest::addColumn<QString>("testQString"); + QTest::addColumn<bool>("testBool"); + QTest::addColumn<int>("testInt"); + QTest::addColumn<double>("testDouble"); + QTest::addColumn<float>("testFloat"); + QTest::addColumn<QRect>("testQRect"); + QTest::addColumn<QRectF>("testQRectF"); + QTest::addColumn<QPoint>("testQPoint"); + QTest::addColumn<QPointF>("testQPointF"); + QTest::addColumn<QSize>("testQSize"); + QTest::addColumn<QSizeF>("testQSizeF"); + QTest::addColumn<QLine>("testQLine"); + QTest::addColumn<QLineF>("testQLineF"); + QTest::addColumn<QChar>("testQChar"); + QTest::addColumn<CustomConvertibleType>("testCustom"); + + QTest::newRow("default") << true + << QString::fromLatin1("string") << true << 15 + << double(3.14) << float(3.6) << QRect(1, 2, 3, 4) + << QRectF(1.4, 1.9, 10.9, 40.2) << QPoint(12, 34) + << QPointF(9.2, 2.7) << QSize(4, 9) << QSizeF(3.3, 9.8) + << QLine(3, 9, 29, 4) << QLineF(38.9, 28.9, 102.3, 0.0) + << QChar('Q') << CustomConvertibleType(QString::fromLatin1("test")); + QTest::newRow("not ok") << false + << QString::fromLatin1("string") << true << 15 + << double(3.14) << float(3.6) << QRect(1, 2, 3, 4) + << QRectF(1.4, 1.9, 10.9, 40.2) << QPoint(12, 34) + << QPointF(9.2, 2.7) << QSize(4, 9) << QSizeF(3.3, 9.8) + << QLine(3, 9, 29, 4) << QLineF() + << QChar('Q') << CustomConvertibleType(42); +} + +void tst_QMetaType::convertCustomType() +{ + QFETCH(bool, ok); + CustomConvertibleType::s_ok = ok; + + CustomConvertibleType t; + QVariant v = QVariant::fromValue(t); + QFETCH(QString, testQString); + CustomConvertibleType::s_value = testQString; + QCOMPARE(v.toString(), ok ? testQString : QString()); + QCOMPARE(v.value<QString>(), ok ? testQString : QString()); + QVERIFY(CustomConvertibleType::s_value.canConvert<CustomConvertibleType>()); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toString()), testQString); + + QFETCH(bool, testBool); + CustomConvertibleType::s_value = testBool; + QCOMPARE(v.toBool(), testBool); + QCOMPARE(v.value<bool>(), testBool); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toBool()), testBool); + + QFETCH(int, testInt); + CustomConvertibleType::s_value = testInt; + QCOMPARE(v.toInt(), ok ? testInt : 0); + QCOMPARE(v.value<int>(), ok ? testInt : 0); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toInt()), testInt); + + QFETCH(double, testDouble); + CustomConvertibleType::s_value = testDouble; + QCOMPARE(v.toDouble(), testDouble); + QCOMPARE(v.value<double>(), testDouble); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toDouble()), testDouble); + + QFETCH(float, testFloat); + CustomConvertibleType::s_value = testFloat; + QCOMPARE(v.toFloat(), ok ? testFloat : 0.0); + QCOMPARE(v.value<float>(), ok ? testFloat : 0.0); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toFloat()), testFloat); + + QFETCH(QRect, testQRect); + CustomConvertibleType::s_value = testQRect; + QCOMPARE(v.toRect(), testQRect); + QCOMPARE(v.value<QRect>(), testQRect); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toRect()), testQRect); + + QFETCH(QRectF, testQRectF); + CustomConvertibleType::s_value = testQRectF; + QCOMPARE(v.toRectF(), ok ? testQRectF : QRectF()); + QCOMPARE(v.value<QRectF>(), ok ? testQRectF : QRectF()); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toRectF()), testQRectF); + + QFETCH(QPoint, testQPoint); + CustomConvertibleType::s_value = testQPoint; + QCOMPARE(v.toPoint(), testQPoint); + QCOMPARE(v.value<QPoint>(), testQPoint); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toPoint()), testQPoint); + + QFETCH(QPointF, testQPointF); + CustomConvertibleType::s_value = testQPointF; + QCOMPARE(v.toPointF(), ok ? testQPointF : QPointF()); + QCOMPARE(v.value<QPointF>(), ok ? testQPointF : QPointF()); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toPointF()), testQPointF); + + QFETCH(QSize, testQSize); + CustomConvertibleType::s_value = testQSize; + QCOMPARE(v.toSize(), testQSize); + QCOMPARE(v.value<QSize>(), testQSize); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toSize()), testQSize); + + QFETCH(QSizeF, testQSizeF); + CustomConvertibleType::s_value = testQSizeF; + QCOMPARE(v.toSizeF(), ok ? testQSizeF : QSizeF()); + QCOMPARE(v.value<QSizeF>(), ok ? testQSizeF : QSizeF()); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toSizeF()), testQSizeF); + + QFETCH(QLine, testQLine); + CustomConvertibleType::s_value = testQLine; + QCOMPARE(v.toLine(), testQLine); + QCOMPARE(v.value<QLine>(), testQLine); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toLine()), testQLine); + + QFETCH(QLineF, testQLineF); + CustomConvertibleType::s_value = testQLineF; + QCOMPARE(v.toLineF(), ok ? testQLineF : QLineF()); + QCOMPARE(v.value<QLineF>(), ok ? testQLineF : QLineF()); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toLineF()), testQLineF); + + QFETCH(QChar, testQChar); + CustomConvertibleType::s_value = testQChar; + QCOMPARE(v.toChar(), testQChar); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toChar()), testQChar); + + QFETCH(CustomConvertibleType, testCustom); + v = QVariant::fromValue(testCustom); + QVERIFY(v.canConvert(::qMetaTypeId<CustomConvertibleType2>())); + QCOMPARE(v.value<CustomConvertibleType2>().m_foo, testCustom.m_foo); +} + +void tst_QMetaType::compareCustomType_data() +{ + QMetaType::registerComparators<CustomConvertibleType>(); + + QTest::addColumn<QVariantList>("unsorted"); + QTest::addColumn<QVariantList>("sorted"); + + QTest::newRow("int") << (QVariantList() << 37 << 458 << 1 << 243 << -4 << 383) + << (QVariantList() << -4 << 1 << 37 << 243 << 383 << 458); + + QTest::newRow("dobule") << (QVariantList() << 4934.93 << 0.0 << 302.39 << -39.0) + << (QVariantList() << -39.0 << 0.0 << 302.39 << 4934.93); + + QTest::newRow("QString") << (QVariantList() << "Hello" << "World" << "this" << "is" << "a" << "test") + << (QVariantList() << "a" << "Hello" << "is" << "test" << "this" << "World"); + + QTest::newRow("QTime") << (QVariantList() << QTime(14, 39) << QTime(0, 0) << QTime(18, 18) << QTime(9, 27)) + << (QVariantList() << QTime(0, 0) << QTime(9, 27) << QTime(14, 39) << QTime(18, 18)); + + QTest::newRow("QDate") << (QVariantList() << QDate(2013, 3, 23) << QDate(1900, 12, 1) << QDate(2001, 2, 2) << QDate(1982, 12, 16)) + << (QVariantList() << QDate(1900, 12, 1) << QDate(1982, 12, 16) << QDate(2001, 2, 2) << QDate(2013, 3, 23)); + + QTest::newRow("mixed") << (QVariantList() << "Hello" << "World" << QChar('a') << 38 << QChar('z') << -39 << 4.6) + << (QVariantList() << -39 << 4.6 << 38 << QChar('a') << "Hello" << "World" << QChar('z')); + + QTest::newRow("custom") << (QVariantList() << QVariant::fromValue(CustomConvertibleType(1)) << QVariant::fromValue(CustomConvertibleType(100)) << QVariant::fromValue(CustomConvertibleType(50))) + << (QVariantList() << QVariant::fromValue(CustomConvertibleType(1)) << QVariant::fromValue(CustomConvertibleType(50)) << QVariant::fromValue(CustomConvertibleType(100))); +} + +void tst_QMetaType::compareCustomType() +{ + QFETCH(QVariantList, unsorted); + QFETCH(QVariantList, sorted); + qSort(unsorted); + QCOMPARE(unsorted, sorted); +} + +struct MessageHandlerCustom : public MessageHandler +{ + MessageHandlerCustom(const int typeId) + : MessageHandler(typeId, handler) + {} + static void handler(QtMsgType, const QMessageLogContext &, const QString &msg) + { + QCOMPARE(msg.trimmed(), expectedMessage.trimmed()); + } + static QString expectedMessage; +}; + +QString MessageHandlerCustom::expectedMessage; + +void tst_QMetaType::customDebugStream() +{ + MessageHandlerCustom handler(::qMetaTypeId<CustomDebugStreamableType>()); + QVariant v1 = QVariant::fromValue(CustomDebugStreamableType()); + handler.expectedMessage = "QVariant(CustomDebugStreamableType, )"; + qDebug() << v1; + + QMetaType::registerConverter<CustomDebugStreamableType, QString>(&CustomDebugStreamableType::toString); + handler.expectedMessage = "QVariant(CustomDebugStreamableType, \"test\")"; + qDebug() << v1; + + QMetaType::registerDebugStreamOperator<CustomDebugStreamableType>(); + handler.expectedMessage = "QVariant(CustomDebugStreamableType, string-content)"; + qDebug() << v1; +} + // Compile-time test, it should be possible to register function pointer types class Undefined; @@ -1781,11 +2189,15 @@ typedef Undefined (*UndefinedFunction0)(); typedef Undefined (*UndefinedFunction1)(Undefined); typedef Undefined (*UndefinedFunction2)(Undefined, Undefined); typedef Undefined (*UndefinedFunction3)(Undefined, Undefined, Undefined); +typedef Undefined (*UndefinedFunction4)(Undefined, Undefined, Undefined, Undefined, Undefined, Undefined, Undefined, Undefined); Q_DECLARE_METATYPE(UndefinedFunction0); Q_DECLARE_METATYPE(UndefinedFunction1); Q_DECLARE_METATYPE(UndefinedFunction2); Q_DECLARE_METATYPE(UndefinedFunction3); +#ifdef Q_COMPILER_VARIADIC_TEMPLATES +Q_DECLARE_METATYPE(UndefinedFunction4); +#endif QTEST_MAIN(tst_QMetaType) #include "tst_qmetatype.moc" diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 1cdf39018b..ff453d767b 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -146,6 +146,7 @@ private slots: void connectFunctorOverloads(); void disconnectDoesNotLeakFunctor(); void connectBase(); + void qmlConnect(); }; struct QObjectCreatedOnShutdown @@ -2061,7 +2062,7 @@ void tst_QObject::metamethod() m = mobj->method(mobj->indexOfMethod("signal5()")); QVERIFY(m.methodSignature() == "signal5()"); QVERIFY(m.methodType() == QMetaMethod::Signal); - QVERIFY(m.access() == QMetaMethod::Protected); + QVERIFY(m.access() == QMetaMethod::Public); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY((m.attributes() & QMetaMethod::Compatibility)); @@ -5879,6 +5880,56 @@ void tst_QObject::connectBase() QCOMPARE( r1.count_slot3, 1 ); } +struct QmlReceiver : public QtPrivate::QSlotObjectBase +{ + int callCount; + void *magic; + + QmlReceiver() + : QtPrivate::QSlotObjectBase(&impl) + , callCount(0) + , magic(0) + {} + + static void impl(int which, QSlotObjectBase *this_, QObject *, void **metaArgs, bool *ret) + { + switch (which) { + case Destroy: delete static_cast<QmlReceiver*>(this_); return; + case Call: static_cast<QmlReceiver*>(this_)->callCount++; return; + case Compare: *ret = static_cast<QmlReceiver*>(this_)->magic == metaArgs[0]; return; + case NumOperations: break; + } + } +}; + +void tst_QObject::qmlConnect() +{ +#ifdef QT_BUILD_INTERNAL + SenderObject sender; + QmlReceiver *receiver = new QmlReceiver; + receiver->magic = receiver; + receiver->ref(); + + QVERIFY(QObjectPrivate::connect(&sender, sender.metaObject()->indexOfSignal("signal1()"), + receiver, Qt::AutoConnection)); + + QCOMPARE(receiver->callCount, 0); + sender.emitSignal1(); + QCOMPARE(receiver->callCount, 1); + + void *a[] = { + receiver + }; + QVERIFY(QObjectPrivate::disconnect(&sender, sender.metaObject()->indexOfSignal("signal1()"), reinterpret_cast<void**>(&a))); + + sender.emitSignal1(); + QCOMPARE(receiver->callCount, 1); + + receiver->destroyIfLastRef(); +#else + QSKIP("Needs QT_BUILD_INTERNAL"); +#endif +} QTEST_MAIN(tst_QObject) #include "tst_qobject.moc" diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 4d862f4fc5..52319f47fd 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -154,6 +154,8 @@ private slots: void qvariant_cast_QObject_data(); void qvariant_cast_QObject(); void qvariant_cast_QObject_derived(); + void qvariant_cast_QObject_wrapper(); + void qvariant_cast_QSharedPointerQObject(); void toLocale(); @@ -242,6 +244,9 @@ private slots: void saveNewBuiltinWithOldStream(); void implicitConstruction(); + + void iterateContainerElements(); + void pairElements(); private: void dataStream_data(QDataStream::Version version); void loadQVariantFromDataStream(QDataStream::Version version); @@ -990,8 +995,8 @@ void tst_QVariant::toString_data() QTest::newRow( "float" ) << QVariant( 123.456f ) << QString( "123.456" ); QTest::newRow( "bool" ) << QVariant( true ) << QString( "true" ); QTest::newRow( "qdate" ) << QVariant( QDate( 2002, 1, 1 ) ) << QString( "2002-01-01" ); - QTest::newRow( "qtime" ) << QVariant( QTime( 12, 34, 56 ) ) << QString( "12:34:56" ); - QTest::newRow( "qdatetime" ) << QVariant( QDateTime( QDate( 2002, 1, 1 ), QTime( 12, 34, 56 ) ) ) << QString( "2002-01-01T12:34:56" ); + QTest::newRow( "qtime" ) << QVariant( QTime( 12, 34, 56 ) ) << QString( "12:34:56.000" ); + QTest::newRow( "qdatetime" ) << QVariant( QDateTime( QDate( 2002, 1, 1 ), QTime( 12, 34, 56 ) ) ) << QString( "2002-01-01T12:34:56.000" ); QTest::newRow( "llong" ) << QVariant( (qlonglong)Q_INT64_C(123456789012) ) << QString( "123456789012" ); QTest::newRow("QJsonValue") << QVariant(QJsonValue(QString("hello"))) << QString("hello"); @@ -2232,6 +2237,182 @@ void tst_QVariant::qvariant_cast_QObject_derived() } } +struct QObjectWrapper +{ + explicit QObjectWrapper(QObject *o = 0) : obj(o) {} + + QObject* getObject() const { + return obj; + } +private: + QObject *obj; +}; + +Q_DECLARE_METATYPE(QObjectWrapper) + +struct Converter +{ + Converter() {} + + QObject* operator()(const QObjectWrapper &f) const + { + return f.getObject(); + } +}; + +namespace MyNS { + +template<typename T> +class SmartPointer +{ + T* pointer; +public: + typedef T element_type; + explicit SmartPointer(T *t = 0) + : pointer(t) + { + } + + T* operator->() const { return pointer; } +}; + +template<typename T> +struct SequentialContainer +{ + typedef T value_type; + typedef const T* const_iterator; + T t; + const_iterator begin() const { return &t; } + const_iterator end() const { return &t + 1; } +}; + +template<typename T, typename U> +struct AssociativeContainer : public std::map<T, U> +{ +}; + +} + +Q_DECLARE_SMART_POINTER_METATYPE(MyNS::SmartPointer) + +Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(MyNS::SequentialContainer) +Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(MyNS::AssociativeContainer) + +// Test that explicit declaration does not degrade features. +Q_DECLARE_METATYPE(MyNS::SmartPointer<int>) +Q_DECLARE_METATYPE(MyNS::SmartPointer<QIODevice>) +Q_DECLARE_METATYPE(QSharedPointer<QIODevice>) + +void tst_QVariant::qvariant_cast_QObject_wrapper() +{ + QMetaType::registerConverter<QObjectWrapper, QObject*>(&QObjectWrapper::getObject); + + CustomQObjectDerived *object = new CustomQObjectDerived(this); + QObjectWrapper wrapper(object); + QVariant v = QVariant::fromValue(wrapper); + QCOMPARE(v.value<QObject*>(), object); + v.convert(qMetaTypeId<QObject*>()); + QCOMPARE(v.value<QObject*>(), object); + + MyNS::SequentialContainer<int> sc; + sc.t = 47; + MyNS::AssociativeContainer<int, short> ac; + + QVariant::fromValue(sc); + QVariant::fromValue(ac); + + { + QFile *f = new QFile(this); + MyNS::SmartPointer<QFile> sp(f); + QVariant spVar = QVariant::fromValue(sp); + QVERIFY(spVar.canConvert<QObject*>()); + QCOMPARE(f, spVar.value<QObject*>()); + } + { + QFile *f = new QFile(this); + QPointer<QFile> sp(f); + QVariant spVar = QVariant::fromValue(sp); + QVERIFY(spVar.canConvert<QObject*>()); + QCOMPARE(f, spVar.value<QObject*>()); + } + { + QFile *f = new QFile(this); + QWeakPointer<QFile> sp(f); + QVariant spVar = QVariant::fromValue(sp); + QVERIFY(spVar.canConvert<QObject*>()); + QCOMPARE(f, spVar.value<QObject*>()); + } + { + QFile *f = new QFile(this); + QSharedPointer<QFile> sp(f); + QWeakPointer<QFile> wp = sp.toWeakRef(); + QVariant wpVar = QVariant::fromValue(wp); + QVERIFY(wpVar.canConvert<QObject*>()); + QCOMPARE(f, wpVar.value<QObject*>()); + } + { + QFile *f = new QFile(this); + QSharedPointer<QFile> sp(f); + QVariant spVar = QVariant::fromValue(sp); + QVERIFY(spVar.canConvert<QObject*>()); + QCOMPARE(f, spVar.value<QObject*>()); + } + { + QIODevice *f = new QFile(this); + MyNS::SmartPointer<QIODevice> sp(f); + QVariant spVar = QVariant::fromValue(sp); + QVERIFY(spVar.canConvert<QObject*>()); + QCOMPARE(f, spVar.value<QObject*>()); + } + { + QIODevice *f = new QFile(this); + QSharedPointer<QIODevice> sp(f); + QVariant spVar = QVariant::fromValue(sp); + QVERIFY(spVar.canConvert<QObject*>()); + QCOMPARE(f, spVar.value<QObject*>()); + } + + // Compile tests: + qRegisterMetaType<MyNS::SmartPointer<int> >(); + // Not declared as a metatype: + qRegisterMetaType<MyNS::SmartPointer<double> >("MyNS::SmartPointer<double>"); +} + +void tst_QVariant::qvariant_cast_QSharedPointerQObject() +{ + // ensure no problems between this form and the auto-registering in QVariant::fromValue + qRegisterMetaType<QSharedPointer<QObject> >("QSharedPointer<QObject>"); + + QObject *rawptr = new QObject; + QSharedPointer<QObject> strong(rawptr); + QWeakPointer<QObject> weak(strong); + QPointer<QObject> qptr(rawptr); + + QVariant v = QVariant::fromValue(strong); + QCOMPARE(v.value<QSharedPointer<QObject> >(), strong); + + // clear our QSP; the copy inside the variant should keep the object alive + strong.clear(); + + // check that the object didn't get deleted + QVERIFY(!weak.isNull()); + QVERIFY(!qptr.isNull()); + + strong = qvariant_cast<QSharedPointer<QObject> >(v); + QCOMPARE(strong.data(), rawptr); + QVERIFY(strong == weak); + + // now really delete the object and verify + strong.clear(); + v.clear(); + QVERIFY(weak.isNull()); + QVERIFY(qptr.isNull()); + + // compile test: + // QVariant::fromValue has already called this function + qRegisterMetaType<QSharedPointer<QObject> >(); +} + void tst_QVariant::convertToQUint8() const { /* qint8. */ @@ -3392,5 +3573,343 @@ void tst_QVariant::saveNewBuiltinWithOldStream() QCOMPARE(int(data.constData()[3]), 0); } +template<typename Container, typename Value_Type = typename Container::value_type> +struct ContainerAPI +{ + static void insert(Container &container, typename Container::value_type value) + { + container.push_back(value); + } + + static bool compare(const QVariant &variant, typename Container::value_type value) + { + return variant.value<typename Container::value_type>() == value; + } + static bool compare(QVariant variant, const QVariant &value) + { + return variant == value; + } +}; + +template<typename Container> +struct ContainerAPI<Container, QVariant> +{ + static void insert(Container &container, int value) + { + container.push_back(QVariant::fromValue(value)); + } + + static bool compare(QVariant variant, const QVariant &value) + { + return variant == value; + } +}; + +template<typename Container> +struct ContainerAPI<Container, QString> +{ + static void insert(Container &container, int value) + { + container.push_back(QString::number(value)); + } + + static bool compare(const QVariant &variant, QString value) + { + return variant.value<QString>() == value; + } + static bool compare(QVariant variant, const QVariant &value) + { + return variant == value; + } +}; + +// We have no built-in defines to check the stdlib features. +// #define TEST_FORWARD_LIST + +#ifdef TEST_FORWARD_LIST +#include <forward_list> + +Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(std::forward_list) + +// Test that explicit declaration does not degrade features. +Q_DECLARE_METATYPE(std::forward_list<int>) + +template<typename Value_Type> +struct ContainerAPI<std::forward_list<Value_Type> > +{ + static void insert(std::forward_list<Value_Type> &container, Value_Type value) + { + container.push_front(value); + } + static bool compare(const QVariant &variant, Value_Type value) + { + return variant.value<Value_Type>() == value; + } + static bool compare(QVariant variant, const QVariant &value) + { + return variant == value; + } +}; + +template<> +struct ContainerAPI<std::forward_list<QVariant> > +{ + static void insert(std::forward_list<QVariant> &container, int value) + { + container.push_front(QVariant::fromValue(value)); + } + + static bool compare(QVariant variant, const QVariant &value) + { + return variant == value; + } +}; + +template<> +struct ContainerAPI<std::forward_list<QString> > +{ + static void insert(std::forward_list<QString> &container, int value) + { + container.push_front(QString::number(value)); + } + static bool compare(const QVariant &variant, QString value) + { + return variant.value<QString>() == value; + } + static bool compare(QVariant variant, const QVariant &value) + { + return variant == value; + } +}; +#endif + +template<typename Container> +struct KeyGetter +{ + static const typename Container::key_type & get(const typename Container::const_iterator &it) + { + return it.key(); + } + static const typename Container::mapped_type & value(const typename Container::const_iterator &it) + { + return it.value(); + } +}; + +template<typename T, typename U> +struct KeyGetter<std::map<T, U> > +{ + static const T & get(const typename std::map<T, U>::const_iterator &it) + { + return it->first; + } + static const U & value(const typename std::map<T, U>::const_iterator &it) + { + return it->second; + } +}; + + +// We have no built-in defines to check the stdlib features. +// #define TEST_UNORDERED_MAP + +#ifdef TEST_UNORDERED_MAP +#include <unordered_map> +typedef std::unordered_map<int, bool> StdUnorderedMap_int_bool; + +Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(std::unordered_map) + +Q_DECLARE_METATYPE(StdUnorderedMap_int_bool) + +template<typename T, typename U> +struct KeyGetter<std::unordered_map<T, U> > +{ + static const T & get(const typename std::unordered_map<T, U>::const_iterator &it) + { + return it->first; + } + static const U & value(const typename std::unordered_map<T, U>::const_iterator &it) + { + return it->second; + } +}; +#endif + +void tst_QVariant::iterateContainerElements() +{ +#ifdef Q_COMPILER_RANGE_FOR + +#define TEST_RANGE_FOR(CONTAINER, VALUE_TYPE) \ + numSeen = 0; \ + containerIter = intList.begin(); \ + for (QVariant v : listIter) { \ + QVERIFY(ContainerAPI<CONTAINER<VALUE_TYPE > >::compare(v, *containerIter)); \ + QVERIFY(ContainerAPI<CONTAINER<VALUE_TYPE > >::compare(v, varList.at(numSeen))); \ + ++containerIter; \ + ++numSeen; \ + } \ + QCOMPARE(numSeen, (int)std::distance(intList.begin(), intList.end())); + +#else + +#define TEST_RANGE_FOR(CONTAINER, VALUE_TYPE) + +#endif + +#define TEST_SEQUENTIAL_ITERATION(CONTAINER, VALUE_TYPE) \ + { \ + int numSeen = 0; \ + CONTAINER<VALUE_TYPE > intList; \ + ContainerAPI<CONTAINER<VALUE_TYPE > >::insert(intList, 1); \ + ContainerAPI<CONTAINER<VALUE_TYPE > >::insert(intList, 2); \ + ContainerAPI<CONTAINER<VALUE_TYPE > >::insert(intList, 3); \ + \ + QVariant listVariant = QVariant::fromValue(intList); \ + QVERIFY(listVariant.canConvert<QVariantList>()); \ + QVariantList varList = listVariant.value<QVariantList>(); \ + QCOMPARE(varList.size(), (int)std::distance(intList.begin(), intList.end())); \ + QSequentialIterable listIter = listVariant.value<QSequentialIterable>(); \ + QCOMPARE(varList.size(), listIter.size()); \ + \ + CONTAINER<VALUE_TYPE >::iterator containerIter = intList.begin(); \ + const CONTAINER<VALUE_TYPE >::iterator containerEnd = intList.end(); \ + for (int i = 0; i < listIter.size(); ++i, ++containerIter, ++numSeen) \ + { \ + QVERIFY(ContainerAPI<CONTAINER<VALUE_TYPE > >::compare(listIter.at(i), *containerIter)); \ + QVERIFY(ContainerAPI<CONTAINER<VALUE_TYPE > >::compare(listIter.at(i), varList.at(i))); \ + } \ + QCOMPARE(numSeen, (int)std::distance(intList.begin(), intList.end())); \ + QCOMPARE(containerIter, containerEnd); \ + \ + containerIter = intList.begin(); \ + numSeen = 0; \ + Q_FOREACH (const QVariant &v, listIter) { \ + QVERIFY(ContainerAPI<CONTAINER<VALUE_TYPE > >::compare(v, *containerIter)); \ + QVERIFY(ContainerAPI<CONTAINER<VALUE_TYPE > >::compare(v, varList.at(numSeen))); \ + ++containerIter; \ + ++numSeen; \ + } \ + QCOMPARE(numSeen, (int)std::distance(intList.begin(), intList.end())); \ + TEST_RANGE_FOR(CONTAINER, VALUE_TYPE) \ + } + + TEST_SEQUENTIAL_ITERATION(QVector, int) + TEST_SEQUENTIAL_ITERATION(QVector, QVariant) + TEST_SEQUENTIAL_ITERATION(QVector, QString) + TEST_SEQUENTIAL_ITERATION(QQueue, int) + TEST_SEQUENTIAL_ITERATION(QQueue, QVariant) + TEST_SEQUENTIAL_ITERATION(QQueue, QString) + TEST_SEQUENTIAL_ITERATION(QList, int) + TEST_SEQUENTIAL_ITERATION(QList, QVariant) + TEST_SEQUENTIAL_ITERATION(QList, QString) + TEST_SEQUENTIAL_ITERATION(QStack, int) + TEST_SEQUENTIAL_ITERATION(QStack, QVariant) + TEST_SEQUENTIAL_ITERATION(QStack, QString) + TEST_SEQUENTIAL_ITERATION(std::vector, int) + TEST_SEQUENTIAL_ITERATION(std::vector, QVariant) + TEST_SEQUENTIAL_ITERATION(std::vector, QString) + TEST_SEQUENTIAL_ITERATION(std::list, int) + TEST_SEQUENTIAL_ITERATION(std::list, QVariant) + TEST_SEQUENTIAL_ITERATION(std::list, QString) + +#ifdef TEST_FORWARD_LIST + TEST_SEQUENTIAL_ITERATION(std::forward_list, int) + TEST_SEQUENTIAL_ITERATION(std::forward_list, QVariant) + TEST_SEQUENTIAL_ITERATION(std::forward_list, QString) +#endif + + { + QVariantList ints; + ints << 1 << 2 << 3; + QVariant var = QVariant::fromValue(ints); + QSequentialIterable iter = var.value<QSequentialIterable>(); + QSequentialIterable::const_iterator it = iter.begin(); + QSequentialIterable::const_iterator end = iter.end(); + QCOMPARE(ints.at(1), *(it + 1)); + int i = 0; + for ( ; it != end; ++it, ++i) { + QCOMPARE(ints.at(i), *it); + } + } + +#define TEST_ASSOCIATIVE_ITERATION(CONTAINER, KEY_TYPE, MAPPED_TYPE) \ + { \ + int numSeen = 0; \ + CONTAINER<KEY_TYPE, MAPPED_TYPE> mapping; \ + mapping[5] = true; \ + mapping[15] = false; \ + \ + QVariant mappingVariant = QVariant::fromValue(mapping); \ + QVariantMap varMap = mappingVariant.value<QVariantMap>(); \ + QVariantMap varHash = mappingVariant.value<QVariantMap>(); \ + QAssociativeIterable mappingIter = mappingVariant.value<QAssociativeIterable>(); \ + \ + CONTAINER<KEY_TYPE, MAPPED_TYPE>::const_iterator containerIter = mapping.begin(); \ + const CONTAINER<KEY_TYPE, MAPPED_TYPE>::const_iterator containerEnd = mapping.end(); \ + for ( ; containerIter != containerEnd; ++containerIter, ++numSeen) \ + { \ + MAPPED_TYPE expected = KeyGetter<CONTAINER<KEY_TYPE, MAPPED_TYPE> >::value(containerIter); \ + KEY_TYPE key = KeyGetter<CONTAINER<KEY_TYPE, MAPPED_TYPE> >::get(containerIter); \ + MAPPED_TYPE actual = mappingIter.value(key).value<MAPPED_TYPE >(); \ + QCOMPARE(varMap.value(QString::number(key)).value<MAPPED_TYPE>(), expected); \ + QCOMPARE(varHash.value(QString::number(key)).value<MAPPED_TYPE>(), expected); \ + QCOMPARE(actual, expected); \ + } \ + QCOMPARE(numSeen, (int)std::distance(mapping.begin(), mapping.end())); \ + QCOMPARE(containerIter, containerEnd); \ + \ + } + + TEST_ASSOCIATIVE_ITERATION(QHash, int, bool) + TEST_ASSOCIATIVE_ITERATION(QMap, int, bool) + TEST_ASSOCIATIVE_ITERATION(std::map, int, bool) +#ifdef TEST_UNORDERED_MAP + TEST_ASSOCIATIVE_ITERATION(std::unordered_map, int, bool) +#endif + + { + QMap<int, QString> mapping; + mapping.insert(1, "one"); + mapping.insert(2, "two"); + mapping.insert(3, "three"); + QVariant var = QVariant::fromValue(mapping); + QAssociativeIterable iter = var.value<QAssociativeIterable>(); + QAssociativeIterable::const_iterator it = iter.begin(); + QAssociativeIterable::const_iterator end = iter.end(); + QCOMPARE(*(mapping.begin() + 1), (*(it + 1)).toString()); + int i = 0; + for ( ; it != end; ++it, ++i) { + QCOMPARE(*(mapping.begin() + i), (*it).toString()); + } + } +} + +void tst_QVariant::pairElements() +{ + typedef QPair<QVariant, QVariant> QVariantPair; + +#define TEST_PAIR_ELEMENT_ACCESS(PAIR, T1, T2, VALUE1, VALUE2) \ + { \ + PAIR<T1, T2> p(VALUE1, VALUE2); \ + QVariant v = QVariant::fromValue(p); \ + \ + QVERIFY(v.canConvert<QVariantPair>()); \ + QVariantPair pi = v.value<QVariantPair>(); \ + QCOMPARE(pi.first, QVariant::fromValue(VALUE1)); \ + QCOMPARE(pi.second, QVariant::fromValue(VALUE2)); \ + } + + TEST_PAIR_ELEMENT_ACCESS(QPair, int, int, 4, 5) + TEST_PAIR_ELEMENT_ACCESS(std::pair, int, int, 4, 5) + TEST_PAIR_ELEMENT_ACCESS(QPair, QString, QString, QStringLiteral("one"), QStringLiteral("two")) + TEST_PAIR_ELEMENT_ACCESS(std::pair, QString, QString, QStringLiteral("one"), QStringLiteral("two")) + TEST_PAIR_ELEMENT_ACCESS(QPair, QVariant, QVariant, 4, 5) + TEST_PAIR_ELEMENT_ACCESS(std::pair, QVariant, QVariant, 4, 5) + TEST_PAIR_ELEMENT_ACCESS(QPair, QVariant, int, 41, 15) + TEST_PAIR_ELEMENT_ACCESS(std::pair, QVariant, int, 34, 65) + TEST_PAIR_ELEMENT_ACCESS(QPair, int, QVariant, 24, 25) + TEST_PAIR_ELEMENT_ACCESS(std::pair, int, QVariant, 44, 15) +} + QTEST_MAIN(tst_QVariant) #include "tst_qvariant.moc" diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp index 07d3c5c7b8..258f167a20 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp +++ b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp @@ -766,6 +766,7 @@ void tst_QMimeDatabase::fromThreads() // sync dtor blocks waiting for finished } +#ifndef QT_NO_PROCESS static bool runUpdateMimeDatabase(const QString &path) // TODO make it a QMimeDatabase method? { const QString umdCommand = QString::fromLatin1("update-mime-database"); @@ -799,6 +800,7 @@ static bool waitAndRunUpdateMimeDatabase(const QString &path) } return runUpdateMimeDatabase(path); } +#endif // !QT_NO_PROCESS static void checkHasMimeType(const QString &mimeType) { @@ -821,6 +823,9 @@ QT_END_NAMESPACE void tst_QMimeDatabase::installNewGlobalMimeType() { +#ifdef QT_NO_PROCESS + QSKIP("This test requires QProcess support"); +#else qmime_secondsBetweenChecks = 0; QMimeDatabase db; @@ -862,10 +867,14 @@ void tst_QMimeDatabase::installNewGlobalMimeType() QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(), QString::fromLatin1("application/octet-stream")); QVERIFY(!db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid()); +#endif // !QT_NO_PROCESS } void tst_QMimeDatabase::installNewLocalMimeType() { +#ifdef QT_NO_PROCESS + QSKIP("This test requires QProcess support"); +#else qmime_secondsBetweenChecks = 0; QMimeDatabase db; @@ -913,6 +922,7 @@ void tst_QMimeDatabase::installNewLocalMimeType() QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(), QString::fromLatin1("application/octet-stream")); QVERIFY(!db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid()); +#endif } QTEST_GUILESS_MAIN(tst_QMimeDatabase) diff --git a/tests/auto/corelib/plugin/qpluginloader/fakeplugin.cpp b/tests/auto/corelib/plugin/qpluginloader/fakeplugin.cpp new file mode 100644 index 0000000000..d5c933e3af --- /dev/null +++ b/tests/auto/corelib/plugin/qpluginloader/fakeplugin.cpp @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Intel Corporation +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qplugin.h> + +#if QT_POINTER_SIZE == 8 +QT_PLUGIN_METADATA_SECTION void *const pluginSection = (void*)(0xc0ffeec0ffeeL); +#else +QT_PLUGIN_METADATA_SECTION void *const pluginSection = (void*)0xc0ffee; +#endif +QT_PLUGIN_METADATA_SECTION const char message[] = "QTMETADATA"; diff --git a/tests/auto/corelib/plugin/qpluginloader/machtest/generate-bad.pl b/tests/auto/corelib/plugin/qpluginloader/machtest/generate-bad.pl new file mode 100755 index 0000000000..ec0dd980a9 --- /dev/null +++ b/tests/auto/corelib/plugin/qpluginloader/machtest/generate-bad.pl @@ -0,0 +1,209 @@ +#!/usr/bin/perl +############################################################################# +## +## Copyright (C) 2013 Intel Corporation. +## Contact: http://www.qt-project.org/legal +## +## This file is the build configuration utility of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and Digia. For licensing terms and +## conditions see http://qt.digia.com/licensing. For further information +## use the contact form at http://qt.digia.com/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 2.1 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 2.1 requirements +## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +## +## In addition, as a special exception, Digia gives you certain additional +## rights. These rights are described in the Digia Qt LGPL Exception +## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3.0 as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL included in the +## packaging of this file. Please review the following information to +## ensure the GNU General Public License version 3.0 requirements will be +## met: http://www.gnu.org/copyleft/gpl.html. +## +## +## $QT_END_LICENSE$ +## +############################################################################# + +use strict; +use constant FAT_MAGIC => 0xcafebabe; +use constant MH_MAGIC => 0xfeedface; +use constant MH_MAGIC_64 => 0xfeedfacf; +use constant CPU_TYPE_X86 => 7; +use constant CPU_TYPE_X86_64 => CPU_TYPE_X86 | 0x01000000; +use constant CPU_SUBTYPE_I386_ALL => 3; +use constant MH_DYLIB => 6; +use constant LC_SEGMENT => 1; +use constant LC_SEGMENT_64 => 0x19; + +my $good = pack("(L7 L2 Z16 L8 Z16 Z16 L9 . L)>", + MH_MAGIC, CPU_TYPE_X86, CPU_SUBTYPE_I386_ALL, MH_DYLIB, # 0-3 + 1, # 4: ncmds + 4 * (37 - 6), # 5: sizeofcmds + 0, # 6: flags + + LC_SEGMENT, # 7: cmd + 4 * (37 - 6), # 8: cmdsize + '__TEXT', # 9-12: segname + 0, # 13: vmaddr + 0x1000, # 14: vmsize + 0, # 15: fileoff + 0x204, # 16: filesize + 7, # 17: maxprot (rwx) + 5, # 18: initprot (r-x) + 1, # 19: nsects + 0, # 20: flags + + 'qtmetadata', # 21-24: sectname + '__TEXT', # 25-28: segname + 0x200, # 29: addr + 4, # 30: size + 0x200, # 31: offset + 2, # 32: align (2^2) + 0, # 33: reloff + 0, # 34: nreloc + 0, # 35: flags + 0, # 36: reserved1 + 0, # 37: reserved2 + + 0x200, + 0xc0ffee # data +); + +my $good64 = pack("(L8 L2 Z16 Q4 L4 Z16 Z16 Q2 L8 . Q)>", + MH_MAGIC_64, CPU_TYPE_X86_64, CPU_SUBTYPE_I386_ALL, MH_DYLIB, # 0-3 + 1, # 4: ncmds + 4 * (45 - 7), # 5: sizeofcmds + 0, # 6: flags + 0, # 7: reserved + + LC_SEGMENT_64, # 8: cmd + 4 * (45 - 7), # 9: cmdsize + '__TEXT', # 10-13: segname + 0, # 14-15: vmaddr + 0x1000, # 16-17: vmsize + 0, # 18-19: fileoff + 0x208, # 20-21: filesize + 7, # 22: maxprot (rwx) + 5, # 23: initprot (r-x) + 1, # 24: nsects + 0, # 25: flags + + 'qtmetadata', # 26-29: sectname + '__TEXT', # 30-33: segname + 0x200, # 34-35: addr + 4, # 36-37: size + 0x200, # 38: offset + 3, # 39: align (2^3) + 0, # 40: reloff + 0, # 41: nreloc + 0, # 42: flags + 0, # 43: reserved1 + 0, # 44: reserved2 + 0, # 45: reserved3 + + 0x200, + 0xc0ffeec0ffee # data +); + +my $fat = pack("L>*", + FAT_MAGIC, # 1: magic + 2, # 2: nfat_arch + + CPU_TYPE_X86, # 3: cputype + CPU_SUBTYPE_I386_ALL, # 4: cpusubtype + 0x1000, # 5: offset + 0x1000, # 6: size + 12, # 7: align (2^12) + + CPU_TYPE_X86_64, # 8: cputype + CPU_SUBTYPE_I386_ALL, # 9: cpusubtype + 0x2000, # 10: offset + 0x1000, # 11: size + 12, # 12: align (2^12) +); + +my $buffer; + +our $badcount = 1; +sub generate($) { + open OUT, ">", "bad$badcount.dylib" or die("Could not open file bad$badcount.dylib: $!\n"); + binmode OUT; + print OUT $_[0]; + close OUT; + ++$badcount; +} + +# Bad file 1-2 +# Except that the cmdsize fields are null +$buffer = $good; +vec($buffer, 5, 32) = 0; +generate $buffer; + +$buffer = $good; +vec($buffer, 8, 32) = 0; +generate $buffer; + +# Bad file 3-4: same as above but 64-bit +$buffer = $good64; +vec($buffer, 5, 32) = 0; +generate $buffer; + +$buffer = $good64; +vec($buffer, 9, 32) = 0; +generate $buffer; + +# Bad file 5-8: same as 1-4, but set cmdsize to bigger than file +$buffer = $good; +vec($buffer, 5, 32) = 0x1000; +generate $buffer; + +$buffer = $good; +vec($buffer, 8, 32) = 0x1000; +generate $buffer; + +$buffer = $good64; +vec($buffer, 5, 32) = 0x1000; +generate $buffer; + +$buffer = $good64; +vec($buffer, 9, 32) = 0x1000; +generate $buffer; + +# Bad file 9-10: overflow size+offset +$buffer = $good; +vec($buffer, 30, 32) = 0xffffffe0; +generate $buffer; + +$buffer = $good64; +vec($buffer, 36, 32) = 0xffffffff; +vec($buffer, 37, 32) = 0xffffffe0; +generate $buffer; + +# Bad file 11: FAT binary with just the header +generate $fat; + +# Bad file 12: FAT binary where the Mach contents don't match the FAT directory +$buffer = pack("a4096 a4096 a4096", $fat, $good64, $good); +generate $buffer; + +# Bad file 13: FAT binary with overflowing size +$buffer = pack("a4096 a4096 a4096", $fat, $good, $good64); +vec($buffer, 5, 32) = 0xfffffffe0; +vec($buffer, 10, 32) = 0xfffffffe0; +generate $buffer; diff --git a/tests/auto/corelib/plugin/qpluginloader/machtest/machtest.pro b/tests/auto/corelib/plugin/qpluginloader/machtest/machtest.pro new file mode 100644 index 0000000000..7acddc22ce --- /dev/null +++ b/tests/auto/corelib/plugin/qpluginloader/machtest/machtest.pro @@ -0,0 +1,56 @@ +TEMPLATE = aux +OTHER_FILES += \ + ppcconverter.pl \ + generate-bad.pl + +i386.target = good.i386.dylib +i386.commands = $(CXX) $(CXXFLAGS) -shared -arch i386 -o $@ -I$$[QT_INSTALL_HEADERS/get] $< +i386.depends += $$PWD/../fakeplugin.cpp +x86_64.target = good.x86_64.dylib +x86_64.commands = $(CXX) $(CXXFLAGS) -shared -arch x86_64 -o $@ -I$$[QT_INSTALL_HEADERS/get] $< +x86_64.depends += $$PWD/../fakeplugin.cpp + +# Current Mac OS X toolchains have no compiler for PPC anymore +# So we fake it by converting an x86-64 binary to (little-endian!) PPC64 +ppc64.target = good.ppc64.dylib +ppc64.commands = $$PWD/ppcconverter.pl $< $@ +ppc64.depends = x86_64 $$PWD/ppcconverter.pl + +# Generate a fat binary with three architectures +fat_all.target = good.fat.all.dylib +fat_all.commands = lipo -create -output $@ \ + -arch ppc64 $$ppc64.target \ + -arch i386 $$i386.target \ + -arch x86_64 $$x86_64.target +fat_all.depends += i386 x86_64 ppc64 + +fat_no_i386.target = good.fat.no-i386.dylib +fat_no_i386.commands = lipo -create -output $@ -arch x86_64 $$x86_64.target -arch ppc64 $$ppc64.target +fat_no_i386.depends += x86_64 ppc64 + +fat_no_x86_64.target = good.fat.no-x86_64.dylib +fat_no_x86_64.commands = lipo -create -output $@ -arch i386 $$i386.target -arch ppc64 $$ppc64.target +fat_no_x86_64.depends += i386 ppc64 + +fat_stub_i386.target = good.fat.stub-i386.dylib +fat_stub_i386.commands = lipo -create -output $@ -arch ppc64 $$ppc64.target -arch_blank i386 +fat_stub_i386.depends += x86_64 ppc64 + +fat_stub_x86_64.target = good.fat.stub-x86_64.dylib +fat_stub_x86_64.commands = lipo -create -output $@ -arch ppc64 $$ppc64.target -arch_blank x86_64 +fat_stub_x86_64.depends += i386 ppc64 + +bad.commands = $$PWD/generate-bad.pl +bad.depends += $$PWD/generate-bad.pl + +MYTARGETS = $$fat_all.depends fat_all fat_no_x86_64 fat_no_i386 \ + fat_stub_i386 fat_stub_x86_64 bad +all.depends += $$MYTARGETS +QMAKE_EXTRA_TARGETS += $$MYTARGETS all + +QMAKE_CLEAN += $$i386.target $$x86_64.target $$ppc64.target $$fat_all.target \ + $$fat_no_i386.target $$fat_no_x86_64.target \ + $$fat_stub_i386.target $$fat_stub_x86_64.target \ + "bad*.dylib" + + diff --git a/tests/auto/corelib/plugin/qpluginloader/machtest/ppcconverter.pl b/tests/auto/corelib/plugin/qpluginloader/machtest/ppcconverter.pl new file mode 100755 index 0000000000..86943161b7 --- /dev/null +++ b/tests/auto/corelib/plugin/qpluginloader/machtest/ppcconverter.pl @@ -0,0 +1,112 @@ +#!/usr/bin/perl +############################################################################# +## +## Copyright (C) 2013 Intel Corporation. +## Contact: http://www.qt-project.org/legal +## +## This file is the build configuration utility of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and Digia. For licensing terms and +## conditions see http://qt.digia.com/licensing. For further information +## use the contact form at http://qt.digia.com/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 2.1 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 2.1 requirements +## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +## +## In addition, as a special exception, Digia gives you certain additional +## rights. These rights are described in the Digia Qt LGPL Exception +## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3.0 as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL included in the +## packaging of this file. Please review the following information to +## ensure the GNU General Public License version 3.0 requirements will be +## met: http://www.gnu.org/copyleft/gpl.html. +## +## +## $QT_END_LICENSE$ +## +############################################################################# + +# Changes the Mach-O file type header to PowerPC. +# +# The header is (from mach-o/loader.h): +# struct mach_header { +# uint32_t magic; /* mach magic number identifier */ +# cpu_type_t cputype; /* cpu specifier */ +# cpu_subtype_t cpusubtype; /* machine specifier */ +# uint32_t filetype; /* type of file */ +# uint32_t ncmds; /* number of load commands */ +# uint32_t sizeofcmds; /* the size of all the load commands */ +# uint32_t flags; /* flags */ +# }; +# +# The 64-bit header is identical in the first three fields, except for a different +# magic number. We will not touch the magic number, we'll just reset the cputype +# field to the PowerPC type and the subtype field to zero. +# +# We will not change the file's endianness. That means we might create a little-endian +# PowerPC binary, which could not be run in real life. +# +# We will also not change the 64-bit ABI flag, which is found in the cputype's high +# byte. That means we'll create a PPC64 binary if fed a 64-bit input. +# +use strict; +use constant MH_MAGIC => 0xfeedface; +use constant MH_CIGAM => 0xcefaedfe; +use constant MH_MAGIC_64 => 0xfeedfacf; +use constant MH_CIGAM_64 => 0xcffaedfe; +use constant CPU_TYPE_POWERPC => 18; +use constant CPU_SUBTYPE_POWERPC_ALL => 0; + +my $infile = shift @ARGV or die("Missing input filename"); +my $outfile = shift @ARGV or die("Missing output filename"); + +open IN, "<$infile" or die("Can't open $infile for reading: $!\n"); +open OUT, ">$outfile" or die("Can't open $outfile for writing: $!\n"); + +binmode IN; +binmode OUT; + +# Read the first 12 bytes, which includes the interesting fields of the header +my $buffer; +read(IN, $buffer, 12); + +my $magic = vec($buffer, 0, 32); +if ($magic == MH_MAGIC || $magic == MH_MAGIC_64) { + # Big endian + # The low byte of cputype is at offset 7 + vec($buffer, 7, 8) = CPU_TYPE_POWERPC; +} elsif ($magic == MH_CIGAM || $magic == MH_CIGAM_64) { + # Little endian + # The low byte of cpytype is at offset 4 + vec($buffer, 4, 8) = CPU_TYPE_POWERPC; +} else { + $magic = ''; + $magic .= sprintf("%02X ", $_) for unpack("CCCC", $buffer); + die("Invalid input. Unknown magic $magic\n"); +} +vec($buffer, 2, 32) = CPU_SUBTYPE_POWERPC_ALL; + +print OUT $buffer; + +# Copy the rest +while (!eof(IN)) { + read(IN, $buffer, 4096) and + print OUT $buffer or + die("Problem copying: $!\n"); +} +close(IN); +close(OUT); diff --git a/tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro b/tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro index 0cba19887e..8d117793bf 100644 --- a/tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro +++ b/tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro @@ -5,6 +5,7 @@ SUBDIRS = lib \ theplugin \ tst !win32: !mac: SUBDIRS += almostplugin +macx-*: SUBDIRS += machtest TARGET = tst_qpluginloader # no special install rule for subdir diff --git a/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro b/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro index a7a9661a54..3894c90ae3 100644 --- a/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro +++ b/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro @@ -2,7 +2,8 @@ CONFIG += testcase CONFIG += parallel_test TARGET = ../tst_qpluginloader QT = core testlib -SOURCES = ../tst_qpluginloader.cpp +contains(QT_CONFIG, private_tests): QT += core-private +SOURCES = ../tst_qpluginloader.cpp ../fakeplugin.cpp HEADERS = ../theplugin/plugininterface.h CONFIG -= app_bundle @@ -14,5 +15,5 @@ win32 { } } -TESTDATA += ../elftest +TESTDATA += ../elftest ../machtest DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp index cef4f53101..351e3a23e0 100644 --- a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp +++ b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp @@ -44,6 +44,10 @@ #include <qpluginloader.h> #include "theplugin/plugininterface.h" +#if defined(QT_BUILD_INTERNAL) && defined(Q_OF_MACH_O) +# include <QtCore/private/qmachparser_p.h> +#endif + // Helper macros to let us know if some suffixes are valid #define bundle_VALID false #define dylib_VALID false @@ -118,6 +122,8 @@ private slots: void deleteinstanceOnUnload(); void loadDebugObj(); void loadCorruptElf(); + void loadMachO_data(); + void loadMachO(); #if defined (Q_OS_UNIX) void loadGarbage(); #endif @@ -311,6 +317,78 @@ void tst_QPluginLoader::loadCorruptElf() #endif } +void tst_QPluginLoader::loadMachO_data() +{ +#ifdef Q_OF_MACH_O + QTest::addColumn<int>("parseResult"); + + QTest::newRow("/dev/null") << int(QMachOParser::NotSuitable); + QTest::newRow("elftest/debugobj.so") << int(QMachOParser::NotSuitable); + QTest::newRow("tst_qpluginloader.cpp") << int(QMachOParser::NotSuitable); + QTest::newRow("tst_qpluginloader") << int(QMachOParser::NotSuitable); + +# ifdef Q_PROCESSOR_X86_64 + QTest::newRow("machtest/good.x86_64.dylib") << int(QMachOParser::QtMetaDataSection); + QTest::newRow("machtest/good.i386.dylib") << int(QMachOParser::NotSuitable); + QTest::newRow("machtest/good.fat.no-x86_64.dylib") << int(QMachOParser::NotSuitable); + QTest::newRow("machtest/good.fat.no-i386.dylib") << int(QMachOParser::QtMetaDataSection); +# elif defined(Q_PROCESSOR_X86_32) + QTest::newRow("machtest/good.i386.dylib") << int(QMachOParser::QtMetaDataSection); + QTest::newRow("machtest/good.x86_64.dylib") << int(QMachOParser::NotSuitable); + QTest::newRow("machtest/good.fat.no-i386.dylib") << int(QMachOParser::NotSuitable); + QTest::newRow("machtest/good.fat.no-x86_64.dylib") << int(QMachOParser::QtMetaDataSection); +# endif +# ifndef Q_PROCESSOR_POWER_64 + QTest::newRow("machtest/good.ppc64.dylib") << int(QMachOParser::NotSuitable); +# endif + + QTest::newRow("machtest/good.fat.all.dylib") << int(QMachOParser::QtMetaDataSection); + QTest::newRow("machtest/good.fat.stub-x86_64.dylib") << int(QMachOParser::NotSuitable); + QTest::newRow("machtest/good.fat.stub-i386.dylib") << int(QMachOParser::NotSuitable); + + QDir d(QFINDTESTDATA("machtest")); + QStringList badlist = d.entryList(QStringList() << "bad*.dylib"); + foreach (const QString &bad, badlist) + QTest::newRow(qPrintable("machtest/" + bad)) << int(QMachOParser::NotSuitable); +#endif +} + +void tst_QPluginLoader::loadMachO() +{ +#ifdef Q_OF_MACH_O + QFile f(QFINDTESTDATA(QTest::currentDataTag())); + QVERIFY(f.open(QIODevice::ReadOnly)); + QByteArray data = f.readAll(); + + long pos; + ulong len; + QString errorString; + int r = QMachOParser::parse(data.constData(), data.size(), f.fileName(), &errorString, &pos, &len); + + QFETCH(int, parseResult); + QCOMPARE(r, parseResult); + + if (r == QMachOParser::NotSuitable) + return; + + QVERIFY(pos > 0); + QVERIFY(len >= sizeof(void*)); + QVERIFY(pos + long(len) < data.size()); + QCOMPARE(pos & (sizeof(void*) - 1), 0UL); + + void *value = *(void**)(data.constData() + pos); + QCOMPARE(value, sizeof(void*) > 4 ? (void*)(0xc0ffeec0ffeeL) : (void*)0xc0ffee); + + // now that we know it's valid, let's try to make it invalid + ulong offeredlen = pos; + do { + --offeredlen; + r = QMachOParser::parse(data.constData(), offeredlen, f.fileName(), &errorString, &pos, &len); + QVERIFY2(r == QMachOParser::NotSuitable, qPrintable(QString("Failed at size 0x%1").arg(offeredlen, 0, 16))); + } while (offeredlen); +#endif +} + #if defined (Q_OS_UNIX) void tst_QPluginLoader::loadGarbage() { diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp index 1ee628dde5..5cc0e5bdb4 100644 --- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp +++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp @@ -106,6 +106,8 @@ private slots: void customEventDispatcher(); + void requestTermination(); + #ifndef Q_OS_WINCE void stressTest(); #endif @@ -1345,5 +1347,43 @@ void tst_QThread::quitLock() QVERIFY(exitThreadCalled); } +class StopableJob : public QObject +{ + Q_OBJECT +public: + StopableJob (QSemaphore &sem) : sem(sem) {} + QSemaphore &sem; +public Q_SLOTS: + void run() { + sem.release(); + while (!thread()->isInterruptionRequested()) + QTest::qSleep(10); + sem.release(); + Q_EMIT finished(); + } +Q_SIGNALS: + void finished(); +}; + +void tst_QThread::requestTermination() +{ + QThread thread; + QVERIFY(!thread.isInterruptionRequested()); + QSemaphore sem; + StopableJob *j = new StopableJob(sem); + j->moveToThread(&thread); + connect(&thread, &QThread::started, j, &StopableJob::run); + connect(j, &StopableJob::finished, &thread, &QThread::quit, Qt::DirectConnection); + connect(&thread, &QThread::finished, j, &QObject::deleteLater); + thread.start(); + QVERIFY(!thread.isInterruptionRequested()); + sem.acquire(); + QVERIFY(!thread.wait(1000)); + thread.requestInterruption(); + sem.acquire(); + QVERIFY(thread.wait(1000)); + QVERIFY(!thread.isInterruptionRequested()); +} + QTEST_MAIN(tst_QThread) #include "tst_qthread.moc" diff --git a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp index 3c0e132a0a..4a9932798c 100644 --- a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp +++ b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp @@ -90,6 +90,7 @@ private slots: void reserveThread(); void releaseThread_data(); void releaseThread(); + void reserveAndStart(); void start(); void tryStart(); void tryStartPeakThreadCount(); @@ -97,6 +98,7 @@ private slots: void priorityStart_data(); void priorityStart(); void waitForDone(); + void clear(); void waitForDoneTimeout(); void destroyingWaitsForTasksToFinish(); void stressTest(); @@ -214,22 +216,22 @@ void tst_QThreadPool::waitcomplete() QCOMPARE(testFunctionCount, runs); } -volatile bool ran; +QAtomicInt ran; // bool class TestTask : public QRunnable { public: void run() { - ran = true; + ran.store(true); } }; void tst_QThreadPool::runTask() { QThreadPool manager; - ran = false; + ran.store(false); manager.start(new TestTask()); - QTRY_VERIFY(ran); + QTRY_VERIFY(ran.load()); } /* @@ -237,19 +239,19 @@ void tst_QThreadPool::runTask() */ void tst_QThreadPool::singleton() { - ran = false; + ran.store(false); QThreadPool::globalInstance()->start(new TestTask()); - QTRY_VERIFY(ran); + QTRY_VERIFY(ran.load()); } -int *value = 0; +QAtomicInt *value = 0; class IntAccessor : public QRunnable { public: void run() { for (int i = 0; i < 100; ++i) { - ++(*value); + value->ref(); QTest::qSleep(1); } } @@ -261,7 +263,7 @@ public: */ void tst_QThreadPool::destruction() { - value = new int; + value = new QAtomicInt; QThreadPool *threadManager = new QThreadPool(); threadManager->start(new IntAccessor()); threadManager->start(new IntAccessor()); @@ -629,6 +631,65 @@ void tst_QThreadPool::releaseThread() threadpool->setMaxThreadCount(savedLimit); } +void tst_QThreadPool::reserveAndStart() // QTBUG-21051 +{ + class WaitingTask : public QRunnable + { + public: + QAtomicInt count; + QSemaphore waitForStarted; + QSemaphore waitBeforeDone; + + WaitingTask() { setAutoDelete(false); } + + void run() + { + count.ref(); + waitForStarted.release(); + waitBeforeDone.acquire(); + } + }; + + // Set up + QThreadPool *threadpool = QThreadPool::globalInstance(); + int savedLimit = threadpool->maxThreadCount(); + threadpool->setMaxThreadCount(1); + QCOMPARE(threadpool->activeThreadCount(), 0); + + // reserve + threadpool->reserveThread(); + QCOMPARE(threadpool->activeThreadCount(), 1); + + // start a task, to get a running thread + WaitingTask *task = new WaitingTask; + threadpool->start(task); + QCOMPARE(threadpool->activeThreadCount(), 2); + task->waitForStarted.acquire(); + task->waitBeforeDone.release(); + QTRY_COMPARE(task->count.load(), 1); + QTRY_COMPARE(threadpool->activeThreadCount(), 1); + + // now the thread is waiting, but tryStart() will fail since activeThreadCount() >= maxThreadCount() + QVERIFY(!threadpool->tryStart(task)); + QTRY_COMPARE(threadpool->activeThreadCount(), 1); + + // start() will therefore do a failing tryStart(), followed by enqueueTask() + // which will actually wake up the waiting thread. + threadpool->start(task); + QTRY_COMPARE(threadpool->activeThreadCount(), 2); + task->waitForStarted.acquire(); + task->waitBeforeDone.release(); + QTRY_COMPARE(task->count.load(), 2); + QTRY_COMPARE(threadpool->activeThreadCount(), 1); + + threadpool->releaseThread(); + QTRY_COMPARE(threadpool->activeThreadCount(), 0); + + delete task; + + threadpool->setMaxThreadCount(savedLimit); +} + QAtomicInt count; class CountingRunnable : public QRunnable { @@ -681,8 +742,8 @@ void tst_QThreadPool::tryStart() } QMutex mutex; -int activeThreads = 0; -int peakActiveThreads = 0; +QAtomicInt activeThreads; +QAtomicInt peakActiveThreads; void tst_QThreadPool::tryStartPeakThreadCount() { class CounterTask : public QRunnable @@ -694,14 +755,14 @@ void tst_QThreadPool::tryStartPeakThreadCount() { { QMutexLocker lock(&mutex); - ++activeThreads; - peakActiveThreads = qMax(peakActiveThreads, activeThreads); + activeThreads.ref(); + peakActiveThreads.store(qMax(peakActiveThreads.load(), activeThreads.load())); } QTest::qWait(100); { QMutexLocker lock(&mutex); - --activeThreads; + activeThreads.deref(); } } }; @@ -713,13 +774,13 @@ void tst_QThreadPool::tryStartPeakThreadCount() if (threadPool.tryStart(&task) == false) QTest::qWait(10); } - QCOMPARE(peakActiveThreads, QThread::idealThreadCount()); + QCOMPARE(peakActiveThreads.load(), QThread::idealThreadCount()); for (int i = 0; i < 20; ++i) { if (threadPool.tryStart(&task) == false) QTest::qWait(10); } - QCOMPARE(peakActiveThreads, QThread::idealThreadCount()); + QCOMPARE(peakActiveThreads.load(), QThread::idealThreadCount()); } void tst_QThreadPool::tryStartCount() @@ -855,6 +916,34 @@ void tst_QThreadPool::waitForDoneTimeout() QVERIFY(threadPool.waitForDone(400)); } +void tst_QThreadPool::clear() +{ + QSemaphore sem(0); + class BlockingRunnable : public QRunnable + { + public: + QSemaphore & sem; + BlockingRunnable(QSemaphore & sem) : sem(sem){} + void run() + { + sem.acquire(); + count.ref(); + } + }; + + QThreadPool threadPool; + threadPool.setMaxThreadCount(10); + int runs = 2 * threadPool.maxThreadCount(); + count.store(0); + for (int i = 0; i <= runs; i++) { + threadPool.start(new BlockingRunnable(sem)); + } + threadPool.clear(); + sem.release(threadPool.maxThreadCount()); + threadPool.waitForDone(); + QCOMPARE(count.load(), threadPool.maxThreadCount()); +} + void tst_QThreadPool::destroyingWaitsForTasksToFinish() { QTime total, pass; diff --git a/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp b/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp index 72bf5c58ca..144bc62b1b 100644 --- a/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp +++ b/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp @@ -44,6 +44,7 @@ #include <iostream> #include <iomanip> #include <sstream> +#include <iterator> #include <algorithm> #include <qalgorithms.h> #include <QStringList> @@ -78,10 +79,22 @@ private slots: void qCountContainer() const; void binaryFindOnLargeContainer() const; -#if Q_TEST_PERFORMANCE + void popCount08_data() { popCount_data_impl(sizeof(quint8 )); } + void popCount16_data() { popCount_data_impl(sizeof(quint16)); } + void popCount32_data() { popCount_data_impl(sizeof(quint32)); } + void popCount64_data() { popCount_data_impl(sizeof(quint64)); } + void popCount08() { popCount_impl<quint8 >(); } + void popCount16() { popCount_impl<quint16>(); } + void popCount32() { popCount_impl<quint32>(); } + void popCount64() { popCount_impl<quint64>(); } + private: +#if Q_TEST_PERFORMANCE void performance(); #endif + void popCount_data_impl(size_t sizeof_T_Int); + template <typename T_Int> + void popCount_impl(); }; class TestInt @@ -834,6 +847,12 @@ void tst_QAlgorithms::qCountContainer() const class RAI { public: + typedef int difference_type; + typedef int value_type; + typedef std::random_access_iterator_tag iterator_category; + typedef int *pointer; + typedef int &reference; + RAI(int searched = 5, int hidePos = 4, int len = 10) : curPos_(0) , length_(len) @@ -1007,6 +1026,72 @@ void tst_QAlgorithms::binaryFindOnLargeContainer() const QCOMPARE(foundIt.pos(), 1073987655); } +// alternative implementation of qPopulationCount for comparison: +static const uint bitsSetInNibble[] = { + 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4, +}; +Q_STATIC_ASSERT(sizeof bitsSetInNibble / sizeof *bitsSetInNibble == 16); + +static Q_DECL_CONSTEXPR uint bitsSetInByte(quint8 byte) +{ + return bitsSetInNibble[byte & 0xF] + bitsSetInNibble[byte >> 4]; +} +static Q_DECL_CONSTEXPR uint bitsSetInShort(quint16 word) +{ + return bitsSetInByte(word & 0xFF) + bitsSetInByte(word >> 8); +} +static Q_DECL_CONSTEXPR uint bitsSetInInt(quint32 word) +{ + return bitsSetInShort(word & 0xFFFF) + bitsSetInShort(word >> 16); +} +static Q_DECL_CONSTEXPR uint bitsSetInInt64(quint64 word) +{ + return bitsSetInInt(word & 0xFFFFFFFF) + bitsSetInInt(word >> 32); +} + + +void tst_QAlgorithms::popCount_data_impl(size_t sizeof_T_Int) +{ + using namespace QTest; + addColumn<quint64>("input"); + addColumn<uint>("expected"); + + for (uint i = 0; i < UCHAR_MAX; ++i) { + const uchar byte = static_cast<uchar>(i); + const uint bits = bitsSetInByte(byte); + const quint64 value = static_cast<quint64>(byte); + const quint64 input = value << ((i % sizeof_T_Int) * 8U); + newRow(qPrintable(QString().sprintf("0x%016llx", input))) << input << bits; + } + + // and some random ones: + if (sizeof_T_Int >= 8) + for (size_t i = 0; i < 1000; ++i) { + const quint64 input = quint64(qrand()) << 32 | quint32(qrand()); + newRow(qPrintable(QString().sprintf("0x%016llx", input))) << input << bitsSetInInt64(input); + } + else if (sizeof_T_Int >= 2) + for (size_t i = 0; i < 1000 ; ++i) { + const quint32 input = qrand(); + if (sizeof_T_Int >= 4) + newRow(qPrintable(QString().sprintf("0x%08x", input))) << quint64(input) << bitsSetInInt(input); + else + newRow(qPrintable(QString().sprintf("0x%04x", quint16(input & 0xFFFF)))) << quint64(input & 0xFFFF) << bitsSetInShort(input & 0xFFFF); + } +} + +template <typename T_Int> +void tst_QAlgorithms::popCount_impl() +{ + QFETCH(quint64, input); + QFETCH(uint, expected); + + const T_Int value = static_cast<T_Int>(input); + + QCOMPARE(qPopulationCount(value), expected); +} + QTEST_APPLESS_MAIN(tst_QAlgorithms) #include "tst_qalgorithms.moc" diff --git a/tests/auto/corelib/tools/qcommandlineparser/qcommandlineparser.pro b/tests/auto/corelib/tools/qcommandlineparser/qcommandlineparser.pro new file mode 100644 index 0000000000..a9aedc4c0d --- /dev/null +++ b/tests/auto/corelib/tools/qcommandlineparser/qcommandlineparser.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS += tst_qcommandlineparser.pro testhelper/qcommandlineparser_test_helper.pro diff --git a/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp new file mode 100644 index 0000000000..07f8ddfc8e --- /dev/null +++ b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2013 David Faure <faure@kde.org> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QDebug> +#include <QCoreApplication> +#include <QCommandLineParser> + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + app.setApplicationVersion("1.0"); + + // Test for QCoreApplication::arguments() + const QStringList incomingArgs = QCoreApplication::arguments(); + for (int i = 0; i < argc; ++i) { + if (incomingArgs.at(i) != QLatin1String(argv[i])) + qDebug() << "ERROR: arguments[" << i << "] was" << incomingArgs.at(i) << "expected" << argv[i]; + } + + QCommandLineParser parser; + parser.setApplicationDescription("Test helper"); + parser.addHelpOption(); + parser.addVersionOption(); + parser.addPositionalArgument("parsingMode", "The parsing mode to test."); + parser.addPositionalArgument("command", "The command to execute."); + parser.addOption(QCommandLineOption("load", "Load file from URL.", "url")); + parser.addOption(QCommandLineOption(QStringList() << "o" << "output", "Set output file.", "file")); + parser.addOption(QCommandLineOption("D", "Define macro.", "key=value")); + + // An option with a longer description, to test wrapping + QCommandLineOption noImplicitIncludesOption(QStringList() << QStringLiteral("n") << QStringLiteral("no-implicit-includes")); + noImplicitIncludesOption.setDescription(QStringLiteral("Disable automatic generation of implicit #include-directives.")); + parser.addOption(noImplicitIncludesOption); + + // This program supports different options depending on the "command" (first argument). + // Call parse() to find out the positional arguments. + parser.parse(QCoreApplication::arguments()); + + QStringList args = parser.positionalArguments(); + if (args.isEmpty()) + parser.showHelp(1); + parser.setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode(args.takeFirst().toInt())); + const QString command = args.isEmpty() ? QString() : args.first(); + if (command == "resize") { + parser.clearPositionalArguments(); + parser.addPositionalArgument("resize", "Resize the object to a new size.", "resize [resize_options]"); + parser.addOption(QCommandLineOption("size", "New size.", "size")); + parser.process(app); + const QString size = parser.value("size"); + printf("Resizing %s to %s and saving to %s\n", qPrintable(parser.value("load")), qPrintable(size), qPrintable(parser.value("o"))); + } else { + // Call process again, to handle unknown options this time. + parser.process(app); + } + + printf("Positional arguments: %s\n", qPrintable(parser.positionalArguments().join(","))); + printf("Macros: %s\n", qPrintable(parser.values("D").join(","))); + + return 0; +} + diff --git a/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.pro b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.pro new file mode 100644 index 0000000000..dce1ac0d37 --- /dev/null +++ b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.pro @@ -0,0 +1,6 @@ +CONFIG += console +CONFIG -= app_bundle +QT = core +DESTDIR = ./ + +SOURCES += qcommandlineparser_test_helper.cpp diff --git a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp new file mode 100644 index 0000000000..9219ff72df --- /dev/null +++ b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp @@ -0,0 +1,559 @@ +/**************************************************************************** +** +** Copyright (C) 2013 David Faure <faure@kde.org> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/QCommandLineParser> + +Q_DECLARE_METATYPE(char**) + +class tst_QCommandLineParser : public QObject +{ + Q_OBJECT + +private slots: + void parsingModes_data(); + + // In-process tests + void testInvalidOptions(); + void testPositionalArguments(); + void testBooleanOption_data(); + void testBooleanOption(); + void testMultipleNames_data(); + void testMultipleNames(); + void testSingleValueOption_data(); + void testSingleValueOption(); + void testValueNotSet(); + void testMultipleValuesOption(); + void testUnknownOptionErrorHandling_data(); + void testUnknownOptionErrorHandling(); + void testDoubleDash_data(); + void testDoubleDash(); + void testProcessNotCalled(); + void testEmptyArgsList(); + void testMissingOptionValue(); + void testStdinArgument_data(); + void testStdinArgument(); + void testSingleDashWordOptionModes_data(); + void testSingleDashWordOptionModes(); + + // QProcess-based tests using qcommandlineparser_test_helper + void testVersionOption(); + void testHelpOption_data(); + void testHelpOption(); + void testQuoteEscaping(); +}; + +static char *empty_argv[] = { const_cast<char*>("tst_qcommandlineparser") }; +static int empty_argc = 1; + +Q_DECLARE_METATYPE(QCommandLineParser::SingleDashWordOptionMode) + +void tst_QCommandLineParser::parsingModes_data() +{ + QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>("parsingMode"); + + QTest::newRow("collapsed") << QCommandLineParser::ParseAsCompactedShortOptions; + QTest::newRow("implicitlylong") << QCommandLineParser::ParseAsLongOptions; +} + +void tst_QCommandLineParser::testInvalidOptions() +{ + QCoreApplication app(empty_argc, empty_argv); + QCommandLineParser parser; + QTest::ignoreMessage(QtWarningMsg, "QCommandLineOption: Option names cannot start with a '-'"); + parser.addOption(QCommandLineOption(QStringLiteral("-v"), QStringLiteral("Displays version information."))); +} + +void tst_QCommandLineParser::testPositionalArguments() +{ + QCoreApplication app(empty_argc, empty_argv); + QCommandLineParser parser; + QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "file.txt")); + QCOMPARE(parser.positionalArguments(), QStringList() << QStringLiteral("file.txt")); +} + +void tst_QCommandLineParser::testBooleanOption_data() +{ + QTest::addColumn<QStringList>("args"); + QTest::addColumn<QStringList>("expectedOptionNames"); + QTest::addColumn<bool>("expectedIsSet"); + + QTest::newRow("set") << (QStringList() << "tst_qcommandlineparser" << "-b") << (QStringList() << "b") << true; + QTest::newRow("unset") << (QStringList() << "tst_qcommandlineparser") << QStringList() << false; +} + +void tst_QCommandLineParser::testBooleanOption() +{ + QFETCH(QStringList, args); + QFETCH(QStringList, expectedOptionNames); + QFETCH(bool, expectedIsSet); + QCoreApplication app(empty_argc, empty_argv); + QCommandLineParser parser; + QVERIFY(parser.addOption(QCommandLineOption(QStringLiteral("b"), QStringLiteral("a boolean option")))); + QVERIFY(parser.parse(args)); + QCOMPARE(parser.optionNames(), expectedOptionNames); + QCOMPARE(parser.isSet("b"), expectedIsSet); + QCOMPARE(parser.values("b"), QStringList()); + QCOMPARE(parser.positionalArguments(), QStringList()); + // Should warn on typos + QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: option not defined: \"c\""); + QVERIFY(!parser.isSet("c")); +} + +void tst_QCommandLineParser::testMultipleNames_data() +{ + QTest::addColumn<QStringList>("args"); + QTest::addColumn<QStringList>("expectedOptionNames"); + + QTest::newRow("short") << (QStringList() << "tst_qcommandlineparser" << "-v") << (QStringList() << "v"); + QTest::newRow("long") << (QStringList() << "tst_qcommandlineparser" << "--version") << (QStringList() << "version"); + QTest::newRow("not_set") << (QStringList() << "tst_qcommandlineparser") << QStringList(); +} + +void tst_QCommandLineParser::testMultipleNames() +{ + QFETCH(QStringList, args); + QFETCH(QStringList, expectedOptionNames); + QCoreApplication app(empty_argc, empty_argv); + QCommandLineOption option(QStringList() << "v" << "version", QStringLiteral("Show version information")); + QCOMPARE(option.names(), QStringList() << "v" << "version"); + QCommandLineParser parser; + QVERIFY(parser.addOption(option)); + QVERIFY(parser.parse(args)); + QCOMPARE(parser.optionNames(), expectedOptionNames); + const bool expectedIsSet = !expectedOptionNames.isEmpty(); + QCOMPARE(parser.isSet("v"), expectedIsSet); + QCOMPARE(parser.isSet("version"), expectedIsSet); +} + +void tst_QCommandLineParser::testSingleValueOption_data() +{ + QTest::addColumn<QStringList>("args"); + QTest::addColumn<QStringList>("defaults"); + QTest::addColumn<bool>("expectedIsSet"); + + QTest::newRow("short") << (QStringList() << "tst" << "-s" << "oxygen") << QStringList() << true; + QTest::newRow("long") << (QStringList() << "tst" << "--style" << "oxygen") << QStringList() << true; + QTest::newRow("longequal") << (QStringList() << "tst" << "--style=oxygen") << QStringList() << true; + QTest::newRow("default") << (QStringList() << "tst") << (QStringList() << "oxygen") << false; +} + +void tst_QCommandLineParser::testSingleValueOption() +{ + QFETCH(QStringList, args); + QFETCH(QStringList, defaults); + QFETCH(bool, expectedIsSet); + QCoreApplication app(empty_argc, empty_argv); + QCommandLineParser parser; + QCommandLineOption option(QStringList() << "s" << "style", QStringLiteral("style name"), "styleName"); + option.setDefaultValues(defaults); + QVERIFY(parser.addOption(option)); + for (int mode = 0; mode < 2; ++mode) { + parser.setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode(mode)); + QVERIFY(parser.parse(args)); + QCOMPARE(parser.isSet("s"), expectedIsSet); + QCOMPARE(parser.isSet("style"), expectedIsSet); + QCOMPARE(parser.isSet(option), expectedIsSet); + QCOMPARE(parser.value("s"), QString("oxygen")); + QCOMPARE(parser.value("style"), QString("oxygen")); + QCOMPARE(parser.values("s"), QStringList() << "oxygen"); + QCOMPARE(parser.values("style"), QStringList() << "oxygen"); + QCOMPARE(parser.values(option), QStringList() << "oxygen"); + QCOMPARE(parser.positionalArguments(), QStringList()); + } + // Should warn on typos + QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: option not defined: \"c\""); + QVERIFY(parser.values("c").isEmpty()); +} + +void tst_QCommandLineParser::testValueNotSet() +{ + QCoreApplication app(empty_argc, empty_argv); + // Not set, no default value + QCommandLineParser parser; + QCommandLineOption option(QStringList() << "s" << "style", QStringLiteral("style name")); + option.setValueName("styleName"); + QVERIFY(parser.addOption(option)); + QVERIFY(parser.parse(QStringList() << "tst")); + QCOMPARE(parser.optionNames(), QStringList()); + QVERIFY(!parser.isSet("s")); + QVERIFY(!parser.isSet("style")); + QCOMPARE(parser.value("s"), QString()); + QCOMPARE(parser.value("style"), QString()); + QCOMPARE(parser.values("s"), QStringList()); + QCOMPARE(parser.values("style"), QStringList()); +} + +void tst_QCommandLineParser::testMultipleValuesOption() +{ + QCoreApplication app(empty_argc, empty_argv); + QCommandLineOption option(QStringLiteral("param"), QStringLiteral("Pass parameter to the backend.")); + option.setValueName("key=value"); + QCommandLineParser parser; + QVERIFY(parser.addOption(option)); + { + QVERIFY(parser.parse(QStringList() << "tst" << "--param" << "key1=value1")); + QVERIFY(parser.isSet("param")); + QCOMPARE(parser.values("param"), QStringList() << "key1=value1"); + QCOMPARE(parser.value("param"), QString("key1=value1")); + } + { + QVERIFY(parser.parse(QStringList() << "tst" << "--param" << "key1=value1" << "--param" << "key2=value2")); + QVERIFY(parser.isSet("param")); + QCOMPARE(parser.values("param"), QStringList() << "key1=value1" << "key2=value2"); + QCOMPARE(parser.value("param"), QString("key2=value2")); + } + + QString expected = + "Usage: tst_qcommandlineparser [options]\n" + "\n" + "Options:\n" + " --param <key=value> Pass parameter to the backend.\n"; + + const QString exeName = QCoreApplication::instance()->arguments().first(); // e.g. debug\tst_qcommandlineparser.exe on Windows + expected.replace(QStringLiteral("tst_qcommandlineparser"), exeName); + QCOMPARE(parser.helpText(), expected); +} + +void tst_QCommandLineParser::testUnknownOptionErrorHandling_data() +{ + QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>("parsingMode"); + QTest::addColumn<QStringList>("args"); + QTest::addColumn<QStringList>("expectedUnknownOptionNames"); + QTest::addColumn<QString>("expectedErrorText"); + + const QStringList args_hello = QStringList() << "tst_qcommandlineparser" << "--hello"; + const QString error_hello("Unknown option 'hello'."); + QTest::newRow("unknown_name_collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << args_hello << QStringList("hello") << error_hello; + QTest::newRow("unknown_name_long") << QCommandLineParser::ParseAsLongOptions << args_hello << QStringList("hello") << error_hello; + + const QStringList args_value = QStringList() << "tst_qcommandlineparser" << "-b=1"; + QTest::newRow("bool_with_value_collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << args_value << QStringList() << QString("Unexpected value after '-b'."); + QTest::newRow("bool_with_value_long") << QCommandLineParser::ParseAsLongOptions << args_value << QStringList() << QString("Unexpected value after '-b'."); + + const QStringList args_dash_long = QStringList() << "tst_qcommandlineparser" << "-bool"; + const QString error_bool("Unknown options: o, o, l."); + QTest::newRow("unknown_name_long_collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << args_dash_long << (QStringList() << "o" << "o" << "l") << error_bool; +} + +void tst_QCommandLineParser::testUnknownOptionErrorHandling() +{ + QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode); + QFETCH(QStringList, args); + QFETCH(QStringList, expectedUnknownOptionNames); + QFETCH(QString, expectedErrorText); + + QCoreApplication app(empty_argc, empty_argv); + QCommandLineParser parser; + parser.setSingleDashWordOptionMode(parsingMode); + QVERIFY(parser.addOption(QCommandLineOption(QStringList() << "b" << "bool", QStringLiteral("a boolean option")))); + QCOMPARE(parser.parse(args), expectedErrorText.isEmpty()); + QCOMPARE(parser.unknownOptionNames(), expectedUnknownOptionNames); + QCOMPARE(parser.errorText(), expectedErrorText); +} + +void tst_QCommandLineParser::testDoubleDash_data() +{ + parsingModes_data(); +} + +void tst_QCommandLineParser::testDoubleDash() +{ + QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode); + + QCoreApplication app(empty_argc, empty_argv); + QCommandLineParser parser; + parser.addOption(QCommandLineOption(QStringList() << "o" << "output", QStringLiteral("Output file"), QStringLiteral("filename"))); + parser.setSingleDashWordOptionMode(parsingMode); + QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--output" << "foo")); + QCOMPARE(parser.value("output"), QString("foo")); + QCOMPARE(parser.positionalArguments(), QStringList()); + QCOMPARE(parser.unknownOptionNames(), QStringList()); + QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--" << "--output" << "bar" << "-b" << "bleh")); + QCOMPARE(parser.value("output"), QString()); + QCOMPARE(parser.positionalArguments(), QStringList() << "--output" << "bar" << "-b" << "bleh"); + QCOMPARE(parser.unknownOptionNames(), QStringList()); +} + +void tst_QCommandLineParser::testProcessNotCalled() +{ + QCoreApplication app(empty_argc, empty_argv); + QCommandLineParser parser; + QVERIFY(parser.addOption(QCommandLineOption(QStringLiteral("b"), QStringLiteral("a boolean option")))); + QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: call process() or parse() before isSet"); + QVERIFY(!parser.isSet("b")); + QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: call process() or parse() before values"); + QCOMPARE(parser.values("b"), QStringList()); +} + +void tst_QCommandLineParser::testEmptyArgsList() +{ + QCoreApplication app(empty_argc, empty_argv); + QCommandLineParser parser; + QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: argument list cannot be empty, it should contain at least the executable name"); + QVERIFY(!parser.parse(QStringList())); // invalid call, argv[0] is missing +} + +void tst_QCommandLineParser::testMissingOptionValue() +{ + QCoreApplication app(empty_argc, empty_argv); + QCommandLineParser parser; + parser.addOption(QCommandLineOption(QStringLiteral("option"), QStringLiteral("An option"), "value")); + QVERIFY(!parser.parse(QStringList() << "argv0" << "--option")); // the user forgot to pass a value for --option + QCOMPARE(parser.value("option"), QString()); + QCOMPARE(parser.errorText(), QString("Missing value after '--option'.")); +} + +void tst_QCommandLineParser::testStdinArgument_data() +{ + parsingModes_data(); +} + +void tst_QCommandLineParser::testStdinArgument() +{ + QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode); + + QCoreApplication app(empty_argc, empty_argv); + QCommandLineParser parser; + parser.setSingleDashWordOptionMode(parsingMode); + parser.addOption(QCommandLineOption(QStringList() << "i" << "input", QStringLiteral("Input file."), QStringLiteral("filename"))); + parser.addOption(QCommandLineOption("b", QStringLiteral("Boolean option."))); + QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--input" << "-")); + QCOMPARE(parser.value("input"), QString("-")); + QCOMPARE(parser.positionalArguments(), QStringList()); + QCOMPARE(parser.unknownOptionNames(), QStringList()); + + QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--input" << "-" << "-b" << "arg")); + QCOMPARE(parser.value("input"), QString("-")); + QVERIFY(parser.isSet("b")); + QCOMPARE(parser.positionalArguments(), QStringList() << "arg"); + QCOMPARE(parser.unknownOptionNames(), QStringList()); + + QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "-")); + QCOMPARE(parser.value("input"), QString()); + QVERIFY(!parser.isSet("b")); + QCOMPARE(parser.positionalArguments(), QStringList() << "-"); + QCOMPARE(parser.unknownOptionNames(), QStringList()); +} + +void tst_QCommandLineParser::testSingleDashWordOptionModes_data() +{ + QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>("parsingMode"); + QTest::addColumn<QStringList>("commandLine"); + QTest::addColumn<QStringList>("expectedOptionNames"); + QTest::addColumn<QStringList>("expectedOptionValues"); + + QTest::newRow("collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-abc" << "val") + << (QStringList() << "a" << "b" << "c") << (QStringList() << QString() << QString() << "val"); + QTest::newRow("collapsed_with_equalsign_value") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-abc=val") + << (QStringList() << "a" << "b" << "c") << (QStringList() << QString() << QString() << "val"); + QTest::newRow("collapsed_explicit_longoption") << QCommandLineParser::ParseAsCompactedShortOptions << QStringList("--nn") + << QStringList("nn") << QStringList(); + QTest::newRow("collapsed_longoption_value") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "--abc" << "val") + << QStringList("abc") << QStringList("val"); + QTest::newRow("compiler") << QCommandLineParser::ParseAsCompactedShortOptions << QStringList("-cab") + << QStringList("c") << QStringList("ab"); + QTest::newRow("compiler_with_space") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-c" << "val") + << QStringList("c") << QStringList("val"); + + QTest::newRow("implicitlylong") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-abc" << "val") + << QStringList("abc") << QStringList("val"); + QTest::newRow("implicitlylong_equal") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-abc=val") + << QStringList("abc") << QStringList("val"); + QTest::newRow("implicitlylong_longoption") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "--nn") + << QStringList("nn") << QStringList(); + QTest::newRow("implicitlylong_longoption_value") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "--abc" << "val") + << QStringList("abc") << QStringList("val"); + QTest::newRow("implicitlylong_with_space") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-c" << "val") + << QStringList("c") << QStringList("val"); +} + +void tst_QCommandLineParser::testSingleDashWordOptionModes() +{ + QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode); + QFETCH(QStringList, commandLine); + QFETCH(QStringList, expectedOptionNames); + QFETCH(QStringList, expectedOptionValues); + + commandLine.prepend("tst_QCommandLineParser"); + + QCoreApplication app(empty_argc, empty_argv); + QCommandLineParser parser; + parser.setSingleDashWordOptionMode(parsingMode); + parser.addOption(QCommandLineOption("a", QStringLiteral("a option."))); + parser.addOption(QCommandLineOption("b", QStringLiteral("b option."))); + parser.addOption(QCommandLineOption(QStringList() << "c" << "abc", QStringLiteral("c option."), QStringLiteral("value"))); + parser.addOption(QCommandLineOption("nn", QStringLiteral("nn option."))); + QVERIFY(parser.parse(commandLine)); + QCOMPARE(parser.optionNames(), expectedOptionNames); + for (int i = 0; i < expectedOptionValues.count(); ++i) + QCOMPARE(parser.value(parser.optionNames().at(i)), expectedOptionValues.at(i)); + QCOMPARE(parser.unknownOptionNames(), QStringList()); +} + +void tst_QCommandLineParser::testVersionOption() +{ +#ifdef QT_NO_PROCESS + QSKIP("This test requires QProcess support"); +#else +#ifdef Q_OS_WINCE + QSKIP("Reading and writing to a process is not supported on Qt/CE"); +#endif + QCoreApplication app(empty_argc, empty_argv); + QProcess process; + process.start("testhelper/qcommandlineparser_test_helper", QStringList() << "0" << "--version"); + QVERIFY(process.waitForFinished(5000)); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); + QString output = process.readAll(); +#ifdef Q_OS_WIN + output.replace(QStringLiteral("\r\n"), QStringLiteral("\n")); +#endif + QCOMPARE(output, QString("qcommandlineparser_test_helper 1.0\n")); +#endif // !QT_NO_PROCESS +} + +void tst_QCommandLineParser::testHelpOption_data() +{ + QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>("parsingMode"); + QTest::addColumn<QString>("expectedHelpOutput"); + + QString expectedOutput = + "Usage: testhelper/qcommandlineparser_test_helper [options] parsingMode command\n" + "Test helper\n" + "\n" + "Options:\n" + " -h, --help Displays this help.\n" + " -v, --version Displays version information.\n" + " --load <url> Load file from URL.\n" + " -o, --output <file> Set output file.\n" + " -D <key=value> Define macro.\n" + " -n, --no-implicit-includes Disable automatic generation of implicit #include\n" + " -directives.\n" + "\n" + "Arguments:\n" + " parsingMode The parsing mode to test.\n" + " command The command to execute.\n"; +#ifdef Q_OS_WIN + expectedOutput.replace(" -h, --help Displays this help.\n", + " -?, -h, --help Displays this help.\n"); + expectedOutput.replace("testhelper/", "testhelper\\"); +#endif + + QTest::newRow("collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << expectedOutput; + QTest::newRow("long") << QCommandLineParser::ParseAsLongOptions << expectedOutput; +} + +void tst_QCommandLineParser::testHelpOption() +{ +#ifdef QT_NO_PROCESS + QSKIP("This test requires QProcess support"); +#else +#ifdef Q_OS_WINCE + QSKIP("Reading and writing to a process is not supported on Qt/CE"); +#endif + + QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode); + QFETCH(QString, expectedHelpOutput); + QCoreApplication app(empty_argc, empty_argv); + QProcess process; + process.start("testhelper/qcommandlineparser_test_helper", QStringList() << QString::number(parsingMode) << "--help"); + QVERIFY(process.waitForFinished(5000)); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); + QString output = process.readAll(); +#ifdef Q_OS_WIN + output.replace(QStringLiteral("\r\n"), QStringLiteral("\n")); +#endif + QCOMPARE(output, expectedHelpOutput); + + process.start("testhelper/qcommandlineparser_test_helper", QStringList() << "0" << "resize" << "--help"); + QVERIFY(process.waitForFinished(5000)); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); + output = process.readAll(); +#ifdef Q_OS_WIN + output.replace(QStringLiteral("\r\n"), QStringLiteral("\n")); +#endif + QByteArray expectedResizeHelp = + "Usage: testhelper/qcommandlineparser_test_helper [options] resize [resize_options]\n" + "Test helper\n" + "\n" + "Options:\n" + " -h, --help Displays this help.\n" + " -v, --version Displays version information.\n" + " --load <url> Load file from URL.\n" + " -o, --output <file> Set output file.\n" + " -D <key=value> Define macro.\n" + " -n, --no-implicit-includes Disable automatic generation of implicit #include\n" + " -directives.\n" + " --size <size> New size.\n" + "\n" + "Arguments:\n" + " resize Resize the object to a new size.\n"; +#ifdef Q_OS_WIN + expectedResizeHelp.replace(" -h, --help Displays this help.\n", + " -?, -h, --help Displays this help.\n"); + expectedResizeHelp.replace("testhelper/", "testhelper\\"); +#endif + QCOMPARE(output, QString(expectedResizeHelp)); +#endif // !QT_NO_PROCESS +} + +void tst_QCommandLineParser::testQuoteEscaping() +{ + QCoreApplication app(empty_argc, empty_argv); + QProcess process; + process.start("testhelper/qcommandlineparser_test_helper", QStringList() << + QString::number(QCommandLineParser::ParseAsCompactedShortOptions) << + "-DKEY1=\"VALUE1\"" << "-DKEY2=\\\"VALUE2\\\"" << + "-DQTBUG-15379=C:\\path\\'file.ext" << + "-DQTBUG-30628=C:\\temp\\'file'.ext"); + QVERIFY(process.waitForFinished(5000)); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); + QString output = process.readAll(); + QVERIFY2(!output.contains("ERROR"), qPrintable(output)); + QVERIFY2(output.contains("KEY1=\"VALUE1\""), qPrintable(output)); + QVERIFY2(output.contains("KEY2=\\\"VALUE2\\\""), qPrintable(output)); + QVERIFY2(output.contains("QTBUG-15379=C:\\path\\'file.ext"), qPrintable(output)); + QVERIFY2(output.contains("QTBUG-30628=C:\\temp\\'file'.ext"), qPrintable(output)); +} + +QTEST_APPLESS_MAIN(tst_QCommandLineParser) +#include "tst_qcommandlineparser.moc" + diff --git a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.pro b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.pro new file mode 100644 index 0000000000..6d3e6d677f --- /dev/null +++ b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.pro @@ -0,0 +1,4 @@ +CONFIG += testcase parallel_test +TARGET = tst_qcommandlineparser +QT = core testlib +SOURCES = tst_qcommandlineparser.cpp diff --git a/tests/auto/corelib/tools/qdate/tst_qdate.cpp b/tests/auto/corelib/tools/qdate/tst_qdate.cpp index 310528ba27..728a4244a1 100644 --- a/tests/auto/corelib/tools/qdate/tst_qdate.cpp +++ b/tests/auto/corelib/tools/qdate/tst_qdate.cpp @@ -944,6 +944,61 @@ void tst_QDate::fromStringDateFormat_data() QTest::newRow("iso2") << QDate(1999, 11, 14).toString(Qt::ISODate) << Qt::ISODate << QDate(1999, 11, 14); QTest::newRow("iso3") << QString("0999-01-01") << Qt::ISODate << QDate(999, 1, 1); QTest::newRow("iso3b") << QString("0999-01-01") << Qt::ISODate << QDate(999, 1, 1); + + // Test Qt::RFC2822Date format (RFC 2822). + QTest::newRow("RFC 2822") << QString::fromLatin1("13 Feb 1987 13:24:51 +0100") + << Qt::RFC2822Date << QDate(1987, 2, 13); + QTest::newRow("RFC 2822 with day") << QString::fromLatin1("Thu, 01 Jan 1970 00:12:34 +0000") + << Qt::RFC2822Date << QDate(1970, 1, 1); + // No timezone + QTest::newRow("RFC 2822 no timezone") << QString::fromLatin1("01 Jan 1970 00:12:34") + << Qt::RFC2822Date << QDate(1970, 1, 1); + // No time specified + QTest::newRow("RFC 2822 date only") << QString::fromLatin1("01 Nov 2002") + << Qt::RFC2822Date << QDate(2002, 11, 1); + QTest::newRow("RFC 2822 with day date only") << QString::fromLatin1("Fri, 01 Nov 2002") + << Qt::RFC2822Date << QDate(2002, 11, 1); + // Test invalid month, day, year + QTest::newRow("RFC 2822 invalid month name") << QString::fromLatin1("13 Fev 1987 13:24:51 +0100") + << Qt::RFC2822Date << QDate(); + QTest::newRow("RFC 2822 invalid day") << QString::fromLatin1("36 Fev 1987 13:24:51 +0100") + << Qt::RFC2822Date << QDate(); + QTest::newRow("RFC 2822 invalid year") << QString::fromLatin1("13 Fev 0000 13:24:51 +0100") + << Qt::RFC2822Date << QDate(); + // Test invalid characters (should ignore invalid characters at end of string). + QTest::newRow("RFC 2822 invalid character at end") << QString::fromLatin1("01 Jan 2012 08:00:00 +0100!") + << Qt::RFC2822Date << QDate(2012, 1, 1); + QTest::newRow("RFC 2822 invalid character at front") << QString::fromLatin1("!01 Jan 2012 08:00:00 +0000") + << Qt::RFC2822Date << QDate(); + QTest::newRow("RFC 2822 invalid character both ends") << QString::fromLatin1("!01 Jan 2012 08:00:00 +0000!") + << Qt::RFC2822Date << QDate(); + QTest::newRow("RFC 2822 invalid character at front, 2 at back") << QString::fromLatin1("!01 Jan 2012 08:00:00 +0000..") + << Qt::RFC2822Date << QDate(); + QTest::newRow("RFC 2822 invalid character 2 at front") << QString::fromLatin1("!!01 Jan 2012 08:00:00 +0000") + << Qt::RFC2822Date << QDate(); + + // Test Qt::RFC2822Date format (RFC 850 and 1036). + QTest::newRow("RFC 850 and 1036") << QString::fromLatin1("Fri Feb 13 13:24:51 1987 +0100") + << Qt::RFC2822Date << QDate(1987, 2, 13); + // No timezone + QTest::newRow("RFC 850 and 1036 no timezone") << QString::fromLatin1("Thu Jan 01 00:12:34 1970") + << Qt::RFC2822Date << QDate(1970, 1, 1); + // No time specified + QTest::newRow("RFC 850 and 1036 date only") << QString::fromLatin1("Fri Nov 01 2002") + << Qt::RFC2822Date << QDate(2002, 11, 1); + // Test invalid characters (should ignore invalid characters at end of string). + QTest::newRow("RFC 850 and 1036 invalid character at end") << QString::fromLatin1("Sun Jan 01 08:00:00 2012 +0100!") + << Qt::RFC2822Date << QDate(2012, 1, 1); + QTest::newRow("RFC 850 and 1036 invalid character at front") << QString::fromLatin1("!Sun Jan 01 08:00:00 2012 +0000") + << Qt::RFC2822Date << QDate(); + QTest::newRow("RFC 850 and 1036 invalid character both ends") << QString::fromLatin1("!Sun Jan 01 08:00:00 2012 +0000!") + << Qt::RFC2822Date << QDate(); + QTest::newRow("RFC 850 and 1036 invalid character at front, 2 at back") << QString::fromLatin1("!Sun Jan 01 08:00:00 2012 +0000..") + << Qt::RFC2822Date << QDate(); + QTest::newRow("RFC 850 and 1036 invalid character 2 at front") << QString::fromLatin1("!!Sun Jan 01 08:00:00 2012 +0000") + << Qt::RFC2822Date << QDate(); + + QTest::newRow("RFC empty") << QString::fromLatin1("") << Qt::RFC2822Date << QDate(); } void tst_QDate::fromStringDateFormat() @@ -1072,6 +1127,7 @@ void tst_QDate::toStringDateFormat_data() QTest::newRow("data3") << QDate(1974,12,1) << Qt::ISODate << QString("1974-12-01"); QTest::newRow("year < 0") << QDate(-1,1,1) << Qt::ISODate << QString(); QTest::newRow("year > 9999") << QDate(-1,1,1) << Qt::ISODate << QString(); + QTest::newRow("RFC2822Date") << QDate(1974,12,1) << Qt::RFC2822Date << QString("01 Dec 1974"); } void tst_QDate::toStringDateFormat() diff --git a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp index 0ec3f64020..bc489f6206 100644 --- a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp @@ -85,8 +85,11 @@ private slots: void fromMSecsSinceEpoch(); void toString_isoDate_data(); void toString_isoDate(); + void toString_textDate_data(); + void toString_textDate(); + void toString_rfcDate_data(); + void toString_rfcDate(); void toString_enumformat(); - void toString_strformat_data(); void toString_strformat(); void addDays(); void addMonths(); @@ -127,8 +130,11 @@ private slots: void fromStringToStringLocale_data(); void fromStringToStringLocale(); - void utcOffset(); - void setUtcOffset(); + void offsetFromUtc(); + void setOffsetFromUtc(); + void toOffsetFromUtc(); + + void timeZoneAbbreviation(); void getDate(); @@ -185,8 +191,11 @@ QDateTime tst_QDateTime::dt( const QString& str ) void tst_QDateTime::ctor() { QDateTime dt1(QDate(2004, 1, 2), QTime(1, 2, 3)); + QCOMPARE(dt1.timeSpec(), Qt::LocalTime); QDateTime dt2(QDate(2004, 1, 2), QTime(1, 2, 3), Qt::LocalTime); + QCOMPARE(dt2.timeSpec(), Qt::LocalTime); QDateTime dt3(QDate(2004, 1, 2), QTime(1, 2, 3), Qt::UTC); + QCOMPARE(dt3.timeSpec(), Qt::UTC); QVERIFY(dt1 == dt2); if (europeanTimeZone) { @@ -194,6 +203,34 @@ void tst_QDateTime::ctor() QVERIFY(dt1 < dt3); QVERIFY(dt1.addSecs(3600).toUTC() == dt3); } + + // Test OffsetFromUTC constructors + QDate offsetDate(2013, 1, 1); + QTime offsetTime(1, 2, 3); + + QDateTime offset1(offsetDate, offsetTime, Qt::OffsetFromUTC); + QCOMPARE(offset1.timeSpec(), Qt::UTC); + QCOMPARE(offset1.offsetFromUtc(), 0); + QCOMPARE(offset1.date(), offsetDate); + QCOMPARE(offset1.time(), offsetTime); + + QDateTime offset2(offsetDate, offsetTime, Qt::OffsetFromUTC, 0); + QCOMPARE(offset2.timeSpec(), Qt::UTC); + QCOMPARE(offset2.offsetFromUtc(), 0); + QCOMPARE(offset2.date(), offsetDate); + QCOMPARE(offset2.time(), offsetTime); + + QDateTime offset3(offsetDate, offsetTime, Qt::OffsetFromUTC, 60 * 60); + QCOMPARE(offset3.timeSpec(), Qt::OffsetFromUTC); + QCOMPARE(offset3.offsetFromUtc(), 60 * 60); + QCOMPARE(offset3.date(), offsetDate); + QCOMPARE(offset3.time(), offsetTime); + + QDateTime offset4(offsetDate, QTime(), Qt::OffsetFromUTC, 60 * 60); + QCOMPARE(offset4.timeSpec(), Qt::OffsetFromUTC); + QCOMPARE(offset4.offsetFromUtc(), 60 * 60); + QCOMPARE(offset4.date(), offsetDate); + QCOMPARE(offset4.time(), QTime(0, 0, 0)); } void tst_QDateTime::operator_eq() @@ -388,7 +425,10 @@ void tst_QDateTime::setTimeSpec() QCOMPARE(dateTime.date(), expectedDate); QCOMPARE(dateTime.time(), expectedTime); - QCOMPARE(dateTime.timeSpec(), newTimeSpec); + if (newTimeSpec == Qt::OffsetFromUTC) + QCOMPARE(dateTime.timeSpec(), Qt::UTC); + else + QCOMPARE(dateTime.timeSpec(), newTimeSpec); } void tst_QDateTime::setTime_t() @@ -396,10 +436,12 @@ void tst_QDateTime::setTime_t() QDateTime dt1; dt1.setTime_t(0); QCOMPARE(dt1.toUTC(), QDateTime(QDate(1970, 1, 1), QTime(), Qt::UTC)); + QCOMPARE(dt1.timeSpec(), Qt::LocalTime); dt1.setTimeSpec(Qt::UTC); dt1.setTime_t(0); QCOMPARE(dt1, QDateTime(QDate(1970, 1, 1), QTime(), Qt::UTC)); + QCOMPARE(dt1.timeSpec(), Qt::UTC); dt1.setTime_t(123456); QCOMPARE(dt1, QDateTime(QDate(1970, 1, 2), QTime(10, 17, 36), Qt::UTC)); @@ -432,6 +474,12 @@ void tst_QDateTime::setTime_t() dt2.setTime_t(0x7FFFFFFF); QCOMPARE(dt2, QDateTime(QDate(2038, 1, 19), QTime(4, 14, 7), Qt::LocalTime)); } + + dt1 = QDateTime(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::OffsetFromUTC, 60 * 60); + dt1.setTime_t(123456); + QCOMPARE(dt1, QDateTime(QDate(1970, 1, 2), QTime(10, 17, 36), Qt::UTC)); + QCOMPARE(dt1.timeSpec(), Qt::OffsetFromUTC); + QCOMPARE(dt1.offsetFromUtc(), 60 * 60); } void tst_QDateTime::setMSecsSinceEpoch_data() @@ -501,6 +549,8 @@ void tst_QDateTime::setMSecsSinceEpoch() dt.setMSecsSinceEpoch(msecs); QCOMPARE(dt, utc); + QCOMPARE(dt.timeSpec(), Qt::UTC); + if (europeanTimeZone) { QCOMPARE(dt.toLocalTime(), european); @@ -510,6 +560,7 @@ void tst_QDateTime::setMSecsSinceEpoch() localDt.setMSecsSinceEpoch(msecs); QCOMPARE(localDt, utc); + QCOMPARE(localDt.timeSpec(), Qt::LocalTime); } QCOMPARE(dt.toMSecsSinceEpoch(), msecs); @@ -533,53 +584,174 @@ void tst_QDateTime::fromMSecsSinceEpoch() QFETCH(QDateTime, utc); QFETCH(QDateTime, european); - QDateTime dt(QDateTime::fromMSecsSinceEpoch(msecs)); + QDateTime dtLocal = QDateTime::fromMSecsSinceEpoch(msecs, Qt::LocalTime); + QDateTime dtUtc = QDateTime::fromMSecsSinceEpoch(msecs, Qt::UTC); + QDateTime dtOffset = QDateTime::fromMSecsSinceEpoch(msecs, Qt::OffsetFromUTC, 60*60); - QCOMPARE(dt, utc); - if (europeanTimeZone) - QCOMPARE(dt.toLocalTime(), european); + QCOMPARE(dtLocal, utc); - QCOMPARE(dt.toMSecsSinceEpoch(), msecs); + QCOMPARE(dtUtc, utc); + QCOMPARE(dtUtc.date(), utc.date()); + QCOMPARE(dtUtc.time(), utc.time()); + + QCOMPARE(dtOffset, utc); + QCOMPARE(dtOffset.offsetFromUtc(), 60*60); + QCOMPARE(dtOffset.time(), utc.time().addMSecs(60*60*1000)); + + if (europeanTimeZone) { + QCOMPARE(dtLocal.toLocalTime(), european); + QCOMPARE(dtUtc.toLocalTime(), european); + QCOMPARE(dtOffset.toLocalTime(), european); + } else { + QSKIP("You must test using Central European (CET/CEST) time zone, e.g. TZ=Europe/Oslo"); + } + + QCOMPARE(dtLocal.toMSecsSinceEpoch(), msecs); + QCOMPARE(dtUtc.toMSecsSinceEpoch(), msecs); + QCOMPARE(dtOffset.toMSecsSinceEpoch(), msecs); if (quint64(msecs / 1000) < 0xFFFFFFFF) { - QCOMPARE(qint64(dt.toTime_t()), msecs / 1000); + QCOMPARE(qint64(dtLocal.toTime_t()), msecs / 1000); + QCOMPARE(qint64(dtUtc.toTime_t()), msecs / 1000); + QCOMPARE(qint64(dtOffset.toTime_t()), msecs / 1000); } QDateTime reference(QDate(1970, 1, 1), QTime(), Qt::UTC); - QCOMPARE(dt, reference.addMSecs(msecs)); + QCOMPARE(dtLocal, reference.addMSecs(msecs)); + QCOMPARE(dtUtc, reference.addMSecs(msecs)); + QCOMPARE(dtOffset, reference.addMSecs(msecs)); } void tst_QDateTime::toString_isoDate_data() { - QTest::addColumn<QDateTime>("dt"); - QTest::addColumn<QString>("formatted"); + QTest::addColumn<QDateTime>("datetime"); + QTest::addColumn<QString>("expected"); QTest::newRow("localtime") << QDateTime(QDate(1978, 11, 9), QTime(13, 28, 34)) - << QString("1978-11-09T13:28:34"); + << QString("1978-11-09T13:28:34.000"); QTest::newRow("UTC") << QDateTime(QDate(1978, 11, 9), QTime(13, 28, 34), Qt::UTC) - << QString("1978-11-09T13:28:34Z"); + << QString("1978-11-09T13:28:34.000Z"); QDateTime dt(QDate(1978, 11, 9), QTime(13, 28, 34)); - dt.setUtcOffset(19800); + dt.setOffsetFromUtc(19800); QTest::newRow("positive OffsetFromUTC") << dt - << QString("1978-11-09T13:28:34+05:30"); + << QString("1978-11-09T13:28:34.000+05:30"); dt.setUtcOffset(-7200); QTest::newRow("negative OffsetFromUTC") << dt - << QString("1978-11-09T13:28:34-02:00"); + << QString("1978-11-09T13:28:34.000-02:00"); QTest::newRow("invalid") << QDateTime(QDate(-1, 11, 9), QTime(13, 28, 34), Qt::UTC) << QString(); + QTest::newRow("999 milliseconds UTC") + << QDateTime(QDate(2000, 1, 1), QTime(13, 28, 34, 999), Qt::UTC) + << QString("2000-01-01T13:28:34.999Z"); } void tst_QDateTime::toString_isoDate() { + QFETCH(QDateTime, datetime); + QFETCH(QString, expected); + + QLocale oldLocale; + QLocale::setDefault(QLocale("en_US")); + + QString result = datetime.toString(Qt::ISODate); + QCOMPARE(result, expected); + + QDateTime resultDatetime = QDateTime::fromString(result, Qt::ISODate); + // If expecting invalid result the datetime may still be valid, i.e. year < 0 or > 9999 + if (!expected.isEmpty()) { + QCOMPARE(resultDatetime, datetime); + QCOMPARE(resultDatetime.date(), datetime.date()); + QCOMPARE(resultDatetime.time(), datetime.time()); + QCOMPARE(resultDatetime.timeSpec(), datetime.timeSpec()); + QCOMPARE(resultDatetime.utcOffset(), datetime.utcOffset()); + } else { + QCOMPARE(resultDatetime, QDateTime()); + } + + QLocale::setDefault(oldLocale); +} + +void tst_QDateTime::toString_textDate_data() +{ + QTest::addColumn<QDateTime>("datetime"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("localtime") << QDateTime(QDate(2013, 1, 2), QTime(1, 2, 3), Qt::LocalTime) + << QString("Wed Jan 2 01:02:03.000 2013"); + QTest::newRow("utc") << QDateTime(QDate(2013, 1, 2), QTime(1, 2, 3), Qt::UTC) + << QString("Wed Jan 2 01:02:03.000 2013 GMT"); + QTest::newRow("offset+") << QDateTime(QDate(2013, 1, 2), QTime(1, 2, 3), Qt::OffsetFromUTC, + 10 * 60 * 60) + << QString("Wed Jan 2 01:02:03.000 2013 GMT+1000"); + QTest::newRow("offset-") << QDateTime(QDate(2013, 1, 2), QTime(1, 2, 3), Qt::OffsetFromUTC, + -10 * 60 * 60) + << QString("Wed Jan 2 01:02:03.000 2013 GMT-1000"); + QTest::newRow("invalid") << QDateTime() + << QString(""); +} + +void tst_QDateTime::toString_textDate() +{ + QFETCH(QDateTime, datetime); + QFETCH(QString, expected); + + QLocale oldLocale; + QLocale::setDefault(QLocale("en_US")); + + QString result = datetime.toString(Qt::TextDate); + QCOMPARE(result, expected); + + QDateTime resultDatetime = QDateTime::fromString(result, Qt::TextDate); + QCOMPARE(resultDatetime, datetime); + QCOMPARE(resultDatetime.date(), datetime.date()); + QCOMPARE(resultDatetime.time(), datetime.time()); + QCOMPARE(resultDatetime.timeSpec(), datetime.timeSpec()); + QCOMPARE(resultDatetime.utcOffset(), datetime.utcOffset()); + + QLocale::setDefault(oldLocale); +} + +void tst_QDateTime::toString_rfcDate_data() +{ + QTest::addColumn<QDateTime>("dt"); + QTest::addColumn<QString>("formatted"); + + if (europeanTimeZone) { + QTest::newRow("localtime") + << QDateTime(QDate(1978, 11, 9), QTime(13, 28, 34)) + << QString("09 Nov 1978 13:28:34 +0100"); + } + QTest::newRow("UTC") + << QDateTime(QDate(1978, 11, 9), QTime(13, 28, 34), Qt::UTC) + << QString("09 Nov 1978 13:28:34 +0000"); + QDateTime dt(QDate(1978, 11, 9), QTime(13, 28, 34)); + dt.setUtcOffset(19800); + QTest::newRow("positive OffsetFromUTC") + << dt + << QString("09 Nov 1978 13:28:34 +0530"); + dt.setUtcOffset(-7200); + QTest::newRow("negative OffsetFromUTC") + << dt + << QString("09 Nov 1978 13:28:34 -0200"); + QTest::newRow("invalid") + << QDateTime(QDate(1978, 13, 9), QTime(13, 28, 34), Qt::UTC) + << QString(); + QTest::newRow("999 milliseconds UTC") + << QDateTime(QDate(2000, 1, 1), QTime(13, 28, 34, 999), Qt::UTC) + << QString("01 Jan 2000 13:28:34 +0000"); +} + +void tst_QDateTime::toString_rfcDate() +{ QFETCH(QDateTime, dt); QFETCH(QString, formatted); - QCOMPARE(dt.toString(Qt::ISODate), formatted); + QCOMPARE(dt.toString(Qt::RFC2822Date), formatted); } void tst_QDateTime::toString_enumformat() @@ -591,7 +763,7 @@ void tst_QDateTime::toString_enumformat() QVERIFY(!str1.isEmpty()); // It's locale dependent everywhere QString str2 = dt1.toString(Qt::ISODate); - QCOMPARE(str2, QString("1995-05-20T12:34:56")); + QCOMPARE(str2, QString("1995-05-20T12:34:56.000")); QString str3 = dt1.toString(Qt::LocalDate); QVERIFY(!str3.isEmpty()); @@ -630,6 +802,26 @@ void tst_QDateTime::addDays() QCOMPARE(dt.time(), QTime(12, 34, 56)); } + // Test preserves TimeSpec + QDateTime dt1(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::UTC); + QDateTime dt2 = dt1.addDays(2); + QCOMPARE(dt2.date(), QDate(2013, 1, 3)); + QCOMPARE(dt2.time(), QTime(0, 0, 0)); + QCOMPARE(dt2.timeSpec(), Qt::UTC); + + dt1 = QDateTime(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::LocalTime); + dt2 = dt1.addDays(2); + QCOMPARE(dt2.date(), QDate(2013, 1, 3)); + QCOMPARE(dt2.time(), QTime(0, 0, 0)); + QCOMPARE(dt2.timeSpec(), Qt::LocalTime); + + dt1 = QDateTime(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::OffsetFromUTC, 60*60); + dt2 = dt1.addDays(2); + QCOMPARE(dt2.date(), QDate(2013, 1, 3)); + QCOMPARE(dt2.time(), QTime(0, 0, 0)); + QCOMPARE(dt2.timeSpec(), Qt::OffsetFromUTC); + QCOMPARE(dt2.offsetFromUtc(), 60 * 60); + // ### test invalid QDateTime() } @@ -637,7 +829,7 @@ void tst_QDateTime::addDays() void tst_QDateTime::addMonths_data() { QTest::addColumn<int>("months"); - QTest::addColumn<QDate>("dt"); + QTest::addColumn<QDate>("resultDate"); QTest::newRow("-15") << -15 << QDate(2002, 10, 31); QTest::newRow("-14") << -14 << QDate(2002, 11, 30); @@ -675,20 +867,37 @@ void tst_QDateTime::addMonths_data() void tst_QDateTime::addMonths() { - QFETCH(QDate, dt); QFETCH(int, months); - - QDateTime start(QDate(2004, 1, 31), QTime(12, 34, 56)); - QCOMPARE(start.addMonths(months).date(), dt); - QCOMPARE(start.addMonths(months).time(), QTime(12, 34, 56)); + QFETCH(QDate, resultDate); + + QDate testDate(2004, 1, 31); + QTime testTime(12, 34, 56); + QDateTime start(testDate, testTime); + QDateTime end = start.addMonths(months); + QCOMPARE(end.date(), resultDate); + QCOMPARE(end.time(), testTime); + QCOMPARE(end.timeSpec(), Qt::LocalTime); + + start = QDateTime(testDate, testTime, Qt::UTC); + end = start.addMonths(months); + QCOMPARE(end.date(), resultDate); + QCOMPARE(end.time(), testTime); + QCOMPARE(end.timeSpec(), Qt::UTC); + + start = QDateTime(testDate, testTime, Qt::OffsetFromUTC, 60 * 60); + end = start.addMonths(months); + QCOMPARE(end.date(), resultDate); + QCOMPARE(end.time(), testTime); + QCOMPARE(end.timeSpec(), Qt::OffsetFromUTC); + QCOMPARE(end.offsetFromUtc(), 60 * 60); } void tst_QDateTime::addYears_data() { QTest::addColumn<int>("years1"); QTest::addColumn<int>("years2"); - QTest::addColumn<QDate>("start"); - QTest::addColumn<QDate>("dt"); + QTest::addColumn<QDate>("startDate"); + QTest::addColumn<QDate>("resultDate"); QTest::newRow("0") << 0 << 0 << QDate(1752, 9, 14) << QDate(1752, 9, 14); QTest::newRow("4000 - 4000") << 4000 << -4000 << QDate(1752, 9, 14) << QDate(1752, 9, 14); @@ -711,12 +920,28 @@ void tst_QDateTime::addYears() { QFETCH(int, years1); QFETCH(int, years2); - QFETCH(QDate, start); - QFETCH(QDate, dt); - - QDateTime startdt(start, QTime(14, 25, 36)); - QCOMPARE(startdt.addYears(years1).addYears(years2).date(), dt); - QCOMPARE(startdt.addYears(years1).addYears(years2).time(), QTime(14, 25, 36)); + QFETCH(QDate, startDate); + QFETCH(QDate, resultDate); + + QTime testTime(14, 25, 36); + QDateTime start(startDate, testTime); + QDateTime end = start.addYears(years1).addYears(years2); + QCOMPARE(end.date(), resultDate); + QCOMPARE(end.time(), testTime); + QCOMPARE(end.timeSpec(), Qt::LocalTime); + + start = QDateTime(startDate, testTime, Qt::UTC); + end = start.addYears(years1).addYears(years2); + QCOMPARE(end.date(), resultDate); + QCOMPARE(end.time(), testTime); + QCOMPARE(end.timeSpec(), Qt::UTC); + + start = QDateTime(startDate, testTime, Qt::OffsetFromUTC, 60 * 60); + end = start.addYears(years1).addYears(years2); + QCOMPARE(end.date(), resultDate); + QCOMPARE(end.time(), testTime); + QCOMPARE(end.timeSpec(), Qt::OffsetFromUTC); + QCOMPARE(end.offsetFromUtc(), 60 * 60); } void tst_QDateTime::addSecs_data() @@ -788,6 +1013,13 @@ void tst_QDateTime::addSecs_data() << QDateTime(QDate(1, 1, 1), QTime(0, 0, 0), Qt::UTC); QTest::newRow("invalid") << invalidDateTime() << 1 << invalidDateTime(); + + // Check Offset details are preserved + QTest::newRow("offset0") << QDateTime(QDate(2013, 1, 1), QTime(1, 2, 3), + Qt::OffsetFromUTC, 60 * 60) + << 60 * 60 + << QDateTime(QDate(2013, 1, 1), QTime(2, 2, 3), + Qt::OffsetFromUTC, 60 * 60); } void tst_QDateTime::addSecs() @@ -799,7 +1031,11 @@ void tst_QDateTime::addSecs() #ifdef Q_OS_IRIX QEXPECT_FAIL("cet4", "IRIX databases say 1970 had DST", Abort); #endif - QCOMPARE(dt.addSecs(nsecs), result); + QDateTime test = dt.addSecs(nsecs); + QCOMPARE(test, result); + QCOMPARE(test.timeSpec(), dt.timeSpec()); + if (test.timeSpec() == Qt::OffsetFromUTC) + QCOMPARE(test.offsetFromUtc(), dt.offsetFromUtc()); QCOMPARE(result.addSecs(-nsecs), dt); } @@ -817,14 +1053,18 @@ void tst_QDateTime::addMSecs() #ifdef Q_OS_IRIX QEXPECT_FAIL("cet4", "IRIX databases say 1970 had DST", Abort); #endif - QCOMPARE(dt.addMSecs(qint64(nsecs) * 1000), result); + QDateTime test = dt.addMSecs(qint64(nsecs) * 1000); + QCOMPARE(test, result); + QCOMPARE(test.timeSpec(), dt.timeSpec()); + if (test.timeSpec() == Qt::OffsetFromUTC) + QCOMPARE(test.offsetFromUtc(), dt.offsetFromUtc()); QCOMPARE(result.addMSecs(qint64(-nsecs) * 1000), dt); } void tst_QDateTime::toTimeSpec_data() { - QTest::addColumn<QDateTime>("utc"); - QTest::addColumn<QDateTime>("local"); + QTest::addColumn<QDateTime>("fromUtc"); + QTest::addColumn<QDateTime>("fromLocal"); QTime utcTime(4, 20, 30); QTime localStandardTime(5, 20, 30); @@ -903,18 +1143,59 @@ void tst_QDateTime::toTimeSpec_data() void tst_QDateTime::toTimeSpec() { if (europeanTimeZone) { - QFETCH(QDateTime, utc); - QFETCH(QDateTime, local); + QFETCH(QDateTime, fromUtc); + QFETCH(QDateTime, fromLocal); + + QDateTime utcToUtc = fromUtc.toTimeSpec(Qt::UTC); + QDateTime localToLocal = fromLocal.toTimeSpec(Qt::LocalTime); + QDateTime utcToLocal = fromUtc.toTimeSpec(Qt::LocalTime); + QDateTime localToUtc = fromLocal.toTimeSpec(Qt::UTC); + QDateTime utcToOffset = fromUtc.toTimeSpec(Qt::OffsetFromUTC); + QDateTime localToOffset = fromLocal.toTimeSpec(Qt::OffsetFromUTC); + + QCOMPARE(utcToUtc, fromUtc); + QCOMPARE(utcToUtc.date(), fromUtc.date()); + QCOMPARE(utcToUtc.time(), fromUtc.time()); + QCOMPARE(utcToUtc.timeSpec(), Qt::UTC); + + QCOMPARE(localToLocal, fromLocal); + QCOMPARE(localToLocal.date(), fromLocal.date()); + QCOMPARE(localToLocal.time(), fromLocal.time()); + QCOMPARE(localToLocal.timeSpec(), Qt::LocalTime); - QCOMPARE(utc.toTimeSpec(Qt::UTC), utc); - QCOMPARE(local.toTimeSpec(Qt::LocalTime), local); #ifdef Q_OS_IRIX QEXPECT_FAIL("summer2", "IRIX databases say 1970 had DST", Abort); #endif - QCOMPARE(utc.toTimeSpec(Qt::LocalTime), local); - QCOMPARE(local.toTimeSpec(Qt::UTC), utc); - QCOMPARE(utc.toTimeSpec(Qt::UTC), local.toTimeSpec(Qt::UTC)); - QCOMPARE(utc.toTimeSpec(Qt::LocalTime), local.toTimeSpec(Qt::LocalTime)); + QCOMPARE(utcToLocal, fromLocal); + QCOMPARE(utcToLocal.date(), fromLocal.date()); + QCOMPARE(utcToLocal.time(), fromLocal.time()); + QCOMPARE(utcToLocal.timeSpec(), Qt::LocalTime); + + QCOMPARE(localToUtc, fromUtc); + QCOMPARE(localToUtc.date(), fromUtc.date()); + QCOMPARE(localToUtc.time(), fromUtc.time()); + QCOMPARE(localToUtc.timeSpec(), Qt::UTC); + + QCOMPARE(utcToUtc, localToUtc); + QCOMPARE(utcToUtc.date(), localToUtc.date()); + QCOMPARE(utcToUtc.time(), localToUtc.time()); + QCOMPARE(utcToUtc.timeSpec(), Qt::UTC); + + QCOMPARE(utcToLocal, localToLocal); + QCOMPARE(utcToLocal.date(), localToLocal.date()); + QCOMPARE(utcToLocal.time(), localToLocal.time()); + QCOMPARE(utcToLocal.timeSpec(), Qt::LocalTime); + + // OffsetToUTC becomes UTC + QCOMPARE(utcToOffset, fromUtc); + QCOMPARE(utcToOffset.date(), fromUtc.date()); + QCOMPARE(utcToOffset.time(), fromUtc.time()); + QCOMPARE(utcToOffset.timeSpec(), Qt::UTC); + + QCOMPARE(localToOffset, fromUtc); + QCOMPARE(localToOffset.date(), fromUtc.date()); + QCOMPARE(localToOffset.time(), fromUtc.time()); + QCOMPARE(localToOffset.timeSpec(), Qt::UTC); } else { QSKIP("Not tested with timezone other than Central European (CET/CST)"); } @@ -928,15 +1209,15 @@ void tst_QDateTime::toLocalTime_data() void tst_QDateTime::toLocalTime() { if (europeanTimeZone) { - QFETCH(QDateTime, utc); - QFETCH(QDateTime, local); + QFETCH(QDateTime, fromUtc); + QFETCH(QDateTime, fromLocal); - QCOMPARE(local.toLocalTime(), local); + QCOMPARE(fromLocal.toLocalTime(), fromLocal); #ifdef Q_OS_IRIX QEXPECT_FAIL("summer2", "IRIX databases say 1970 had DST", Abort); #endif - QCOMPARE(utc.toLocalTime(), local); - QCOMPARE(utc.toLocalTime(), local.toLocalTime()); + QCOMPARE(fromUtc.toLocalTime(), fromLocal); + QCOMPARE(fromUtc.toLocalTime(), fromLocal.toLocalTime()); } else { QSKIP("Not tested with timezone other than Central European (CET/CST)"); } @@ -950,15 +1231,15 @@ void tst_QDateTime::toUTC_data() void tst_QDateTime::toUTC() { if (europeanTimeZone) { - QFETCH(QDateTime, utc); - QFETCH(QDateTime, local); + QFETCH(QDateTime, fromUtc); + QFETCH(QDateTime, fromLocal); - QCOMPARE(utc.toUTC(), utc); + QCOMPARE(fromUtc.toUTC(), fromUtc); #ifdef Q_OS_IRIX QEXPECT_FAIL("summer2", "IRIX databases say 1970 had DST", Abort); #endif - QCOMPARE(local.toUTC(), utc); - QCOMPARE(utc.toUTC(), local.toUTC()); + QCOMPARE(fromLocal.toUTC(), fromUtc); + QCOMPARE(fromUtc.toUTC(), fromLocal.toUTC()); } else { QSKIP("Not tested with timezone other than Central European (CET/CST)"); } @@ -1288,9 +1569,9 @@ void tst_QDateTime::operator_eqeq_data() QDateTime dateTime3b = dateTime3.addDays(-1); // Ensure that different times may be equal when considering timezone. QDateTime dateTime3c(dateTime3.addSecs(3600)); - dateTime3c.setUtcOffset(3600); + dateTime3c.setOffsetFromUtc(3600); QDateTime dateTime3d(dateTime3.addSecs(-3600)); - dateTime3d.setUtcOffset(-3600); + dateTime3d.setOffsetFromUtc(-3600); // Convert from UTC to local. QDateTime dateTime3e(dateTime3.date(), dateTime3.time()); @@ -1387,8 +1668,9 @@ void tst_QDateTime::operator_insert_extract() QFETCH(QString, deserialiseAs); QFETCH(QDataStream::Version, dataStreamVersion); - // Save the previous timezone so we can restore it afterwards, just in case. - QString previousTimeZone = qgetenv("TZ"); + // Save the previous timezone so we can restore it afterwards, otherwise later tests will break + QByteArray previousTimeZone = qgetenv("TZ"); + // Start off in a certain timezone. qputenv("TZ", serialiseAs.toLocal8Bit().constData()); tzset(); @@ -1469,69 +1751,23 @@ void tst_QDateTime::operator_insert_extract() } } - qputenv("TZ", previousTimeZone.toLocal8Bit().constData()); + if (previousTimeZone.isNull()) + qunsetenv("TZ"); + else + qputenv("TZ", previousTimeZone.constData()); tzset(); } #endif -void tst_QDateTime::toString_strformat_data() -{ - QTest::addColumn<QDateTime>("dt"); - QTest::addColumn<QString>("format"); - QTest::addColumn<QString>("str"); - - QTest::newRow( "datetime0" ) << QDateTime() << QString("dd-MM-yyyy hh:mm:ss") << QString(); - QTest::newRow( "datetime1" ) << QDateTime(QDate(1999, 12, 31), QTime(23, 59, 59, 999)) - << QString("dd-'mmddyy'MM-yyyy hh:mm:ss.zzz") - << QString("31-mmddyy12-1999 23:59:59.999"); - QTest::newRow( "datetime2" ) << QDateTime(QDate(1999, 12, 31), QTime(23, 59, 59, 999)) - << QString("dd-'apAP'MM-yyyy hh:mm:ss.zzz") - << QString("31-apAP12-1999 23:59:59.999"); - QTest::newRow( "datetime3" ) << QDateTime(QDate(1999, 12, 31), QTime(23, 59, 59, 999)) - << QString("Apdd-MM-yyyy hh:mm:ss.zzz") - << QString("PMp31-12-1999 11:59:59.999"); - QTest::newRow( "datetime4" ) << QDateTime(QDate(1999, 12, 31), QTime(23, 59, 59, 999)) - << QString("'ap'apdd-MM-yyyy 'AP'hh:mm:ss.zzz") - << QString("appm31-12-1999 AP11:59:59.999"); - QTest::newRow( "datetime5" ) << QDateTime(QDate(1999, 12, 31), QTime(23, 59, 59, 999)) - << QString("'''") << QString("'"); - QTest::newRow( "datetime6" ) << QDateTime(QDate(1999, 12, 31), QTime(23, 59, 59, 999)) - << QString("'ap") << QString("ap"); - QTest::newRow( "datetime7" ) << QDateTime(QDate(1999, 12, 31), QTime(23, 59, 59, 999)) - << QString("' ' 'hh' hh") << QString(" hh 23"); - QTest::newRow( "datetime8" ) << QDateTime(QDate(1999, 12, 31), QTime(23, 59, 59, 999)) - << QString("d'foobar'") << QString("31foobar"); - QTest::newRow( "datetime9" ) << QDateTime(QDate(1999, 12, 31), QTime(3, 59, 59, 999)) - << QString("hhhhh") << QString("03033"); - QTest::newRow( "datetime11" ) << QDateTime(QDate(1999, 12, 31), QTime(23, 59, 59, 999)) - << QString("HHHhhhAaAPap") << QString("23231111PMpmPMpm"); - QTest::newRow( "datetime12" ) << QDateTime(QDate(1999, 12, 31), QTime(3, 59, 59, 999)) - << QString("HHHhhhAaAPap") << QString("033033AMamAMam"); - QTest::newRow( "datetime13" ) << QDateTime(QDate(1974, 12, 1), QTime(14, 14, 20)) - << QString("hh''mm''ss dd''MM''yyyy") - << QString("14'14'20 01'12'1974"); - QTest::newRow( "single, 0 => 12 AM" ) << QDateTime(QDate(1999, 12, 31), QTime(0, 59, 59, 999)) - << QString("hAP") << QString("12AM"); - QTest::newRow( "double, 0 => 12 AM" ) << QDateTime(QDate(1999, 12, 31), QTime(0, 59, 59, 999)) - << QString("hhAP") << QString("12AM"); - QTest::newRow( "dddd" ) << QDateTime(QDate(1999, 12, 31), QTime(0, 59, 59, 999)) - << QString("dddd") << QString("Friday"); - QTest::newRow( "ddd" ) << QDateTime(QDate(1999, 12, 31), QTime(0, 59, 59, 999)) - << QString("ddd") << QString("Fri"); - QTest::newRow( "MMMM" ) << QDateTime(QDate(1999, 12, 31), QTime(0, 59, 59, 999)) - << QString("MMMM") << QString("December"); - QTest::newRow( "MMM" ) << QDateTime(QDate(1999, 12, 31), QTime(0, 59, 59, 999)) - << QString("MMM") << QString("Dec"); - QTest::newRow( "emtpy" ) << QDateTime(QDate(1999, 12, 31), QTime(0, 59, 59, 999)) - << QString("") << QString(""); -} - void tst_QDateTime::toString_strformat() { - QFETCH( QDateTime, dt ); - QFETCH( QString, format ); - QFETCH( QString, str ); - QCOMPARE( dt.toString( format ), str ); + // Most tests are in QLocale, just test that the api works. + QDate testDate(2013, 1, 1); + QTime testTime(1, 2, 3); + QDateTime testDateTime(testDate, testTime, Qt::UTC); + QCOMPARE(testDate.toString("yyyy-MM-dd"), QString("2013-01-01")); + QCOMPARE(testTime.toString("hh:mm:ss"), QString("01:02:03")); + QCOMPARE(testDateTime.toString("yyyy-MM-dd hh:mm:ss t"), QString("2013-01-01 01:02:03 UTC")); } void tst_QDateTime::fromStringDateFormat_data() @@ -1586,7 +1822,7 @@ void tst_QDateTime::fromStringDateFormat_data() QTest::newRow("text invalid month name") << QString::fromLatin1("Thu Jaz 1 1970 00:12:34") << Qt::TextDate << invalidDateTime(); QTest::newRow("text invalid date") << QString::fromLatin1("Thu Jan 32 1970 00:12:34") - << Qt::TextDate << QDateTime(invalidDate(), QTime(0, 12, 34), Qt::LocalTime); + << Qt::TextDate << invalidDateTime(); QTest::newRow("text invalid day #1") << QString::fromLatin1("Thu Jan XX 1970 00:12:34") << Qt::TextDate << invalidDateTime(); QTest::newRow("text invalid day #2") << QString::fromLatin1("Thu X. Jan 00:00:00 1970") @@ -1611,18 +1847,18 @@ void tst_QDateTime::fromStringDateFormat_data() << Qt::TextDate << invalidDateTime(); QTest::newRow("text invalid gmt minute") << QString::fromLatin1("Thu 1. Jan 1970 00:00:00 GMT+000X") << Qt::TextDate << invalidDateTime(); + QTest::newRow("text second fraction") << QString::fromLatin1("Mon 6. May 2013 01:02:03.456") + << Qt::TextDate << QDateTime(QDate(2013, 5, 6), QTime(1, 2, 3, 456)); // Test Qt::ISODate format. QTest::newRow("ISO +01:00") << QString::fromLatin1("1987-02-13T13:24:51+01:00") << Qt::ISODate << QDateTime(QDate(1987, 2, 13), QTime(12, 24, 51), Qt::UTC); QTest::newRow("ISO -01:00") << QString::fromLatin1("1987-02-13T13:24:51-01:00") << Qt::ISODate << QDateTime(QDate(1987, 2, 13), QTime(14, 24, 51), Qt::UTC); - // Not sure about these two... it will currently be created as LocalTime, but it - // should probably be UTC according to the ISO 8601 spec (see 4.2.5.1). QTest::newRow("ISO +0000") << QString::fromLatin1("1970-01-01T00:12:34+0000") - << Qt::ISODate << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::LocalTime); + << Qt::ISODate << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC); QTest::newRow("ISO +00:00") << QString::fromLatin1("1970-01-01T00:12:34+00:00") - << Qt::ISODate << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::LocalTime); + << Qt::ISODate << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC); // No time specified - defaults to Qt::LocalTime. QTest::newRow("ISO data3") << QString::fromLatin1("2002-10-01") << Qt::ISODate << QDateTime(QDate(2002, 10, 1), QTime(0, 0, 0, 0), Qt::LocalTime); @@ -1697,6 +1933,79 @@ void tst_QDateTime::fromStringDateFormat_data() QTest::newRow("ISO .99999 of a minute (comma)") << QString::fromLatin1("2012-01-01T08:00,99999") << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 59, 999), Qt::LocalTime); QTest::newRow("ISO empty") << QString::fromLatin1("") << Qt::ISODate << invalidDateTime(); + + // Test Qt::RFC2822Date format (RFC 2822). + QTest::newRow("RFC 2822 +0100") << QString::fromLatin1("13 Feb 1987 13:24:51 +0100") + << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(12, 24, 51), Qt::UTC); + QTest::newRow("RFC 2822 with day +0100") << QString::fromLatin1("Fri, 13 Feb 1987 13:24:51 +0100") + << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(12, 24, 51), Qt::UTC); + QTest::newRow("RFC 2822 -0100") << QString::fromLatin1("13 Feb 1987 13:24:51 -0100") + << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(14, 24, 51), Qt::UTC); + QTest::newRow("RFC 2822 with day -0100") << QString::fromLatin1("Fri, 13 Feb 1987 13:24:51 -0100") + << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(14, 24, 51), Qt::UTC); + QTest::newRow("RFC 2822 +0000") << QString::fromLatin1("01 Jan 1970 00:12:34 +0000") + << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC); + QTest::newRow("RFC 2822 with day +0000") << QString::fromLatin1("Thu, 01 Jan 1970 00:12:34 +0000") + << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC); + QTest::newRow("RFC 2822 +0000") << QString::fromLatin1("01 Jan 1970 00:12:34 +0000") + << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC); + QTest::newRow("RFC 2822 with day +0000") << QString::fromLatin1("Thu, 01 Jan 1970 00:12:34 +0000") + << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC); + // No timezone assume UTC + QTest::newRow("RFC 2822 no timezone") << QString::fromLatin1("01 Jan 1970 00:12:34") + << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC); + // No time specified + QTest::newRow("RFC 2822 date only") << QString::fromLatin1("01 Nov 2002") + << Qt::RFC2822Date << invalidDateTime(); + QTest::newRow("RFC 2822 with day date only") << QString::fromLatin1("Fri, 01 Nov 2002") + << Qt::RFC2822Date << invalidDateTime(); + // Test invalid month, day, year + QTest::newRow("RFC 2822 invalid month name") << QString::fromLatin1("13 Fev 1987 13:24:51 +0100") + << Qt::RFC2822Date << invalidDateTime(); + QTest::newRow("RFC 2822 invalid day") << QString::fromLatin1("36 Fev 1987 13:24:51 +0100") + << Qt::RFC2822Date << invalidDateTime(); + QTest::newRow("RFC 2822 invalid year") << QString::fromLatin1("13 Fev 0000 13:24:51 +0100") + << Qt::RFC2822Date << invalidDateTime(); + // Test invalid characters (should ignore invalid characters at end of string). + QTest::newRow("RFC 2822 invalid character at end") << QString::fromLatin1("01 Jan 2012 08:00:00 +0100!") + << Qt::RFC2822Date << QDateTime(QDate(2012, 1, 1), QTime(7, 0, 0, 0), Qt::UTC); + QTest::newRow("RFC 2822 invalid character at front") << QString::fromLatin1("!01 Jan 2012 08:00:00 +0000") + << Qt::RFC2822Date << invalidDateTime(); + QTest::newRow("RFC 2822 invalid character both ends") << QString::fromLatin1("!01 Jan 2012 08:00:00 +0000!") + << Qt::RFC2822Date << invalidDateTime(); + QTest::newRow("RFC 2822 invalid character at front, 2 at back") << QString::fromLatin1("!01 Jan 2012 08:00:00 +0000..") + << Qt::RFC2822Date << invalidDateTime(); + QTest::newRow("RFC 2822 invalid character 2 at front") << QString::fromLatin1("!!01 Jan 2012 08:00:00 +0000") + << Qt::RFC2822Date << invalidDateTime(); + + // Test Qt::RFC2822Date format (RFC 850 and 1036). + QTest::newRow("RFC 850 and 1036 +0100") << QString::fromLatin1("Fri Feb 13 13:24:51 1987 +0100") + << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(12, 24, 51), Qt::UTC); + QTest::newRow("RFC 850 and 1036 -0100") << QString::fromLatin1("Fri Feb 13 13:24:51 1987 -0100") + << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(14, 24, 51), Qt::UTC); + QTest::newRow("RFC 850 and 1036 +0000") << QString::fromLatin1("Thu Jan 01 00:12:34 1970 +0000") + << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC); + QTest::newRow("RFC 850 and 1036 +0000") << QString::fromLatin1("Thu Jan 01 00:12:34 1970 +0000") + << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC); + // No timezone assume UTC + QTest::newRow("RFC 850 and 1036 no timezone") << QString::fromLatin1("Thu Jan 01 00:12:34 1970") + << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC); + // No time specified + QTest::newRow("RFC 850 and 1036 date only") << QString::fromLatin1("Fri Nov 01 2002") + << Qt::RFC2822Date << invalidDateTime(); + // Test invalid characters (should ignore invalid characters at end of string). + QTest::newRow("RFC 850 and 1036 invalid character at end") << QString::fromLatin1("Sun Jan 01 08:00:00 2012 +0100!") + << Qt::RFC2822Date << QDateTime(QDate(2012, 1, 1), QTime(7, 0, 0, 0), Qt::UTC); + QTest::newRow("RFC 850 and 1036 invalid character at front") << QString::fromLatin1("!Sun Jan 01 08:00:00 2012 +0000") + << Qt::RFC2822Date << invalidDateTime(); + QTest::newRow("RFC 850 and 1036 invalid character both ends") << QString::fromLatin1("!Sun Jan 01 08:00:00 2012 +0000!") + << Qt::RFC2822Date << invalidDateTime(); + QTest::newRow("RFC 850 and 1036 invalid character at front, 2 at back") << QString::fromLatin1("!Sun Jan 01 08:00:00 2012 +0000..") + << Qt::RFC2822Date << invalidDateTime(); + QTest::newRow("RFC 850 and 1036 invalid character 2 at front") << QString::fromLatin1("!!Sun Jan 01 08:00:00 2012 +0000") + << Qt::RFC2822Date << invalidDateTime(); + + QTest::newRow("RFC empty") << QString::fromLatin1("") << Qt::RFC2822Date << invalidDateTime(); } void tst_QDateTime::fromStringDateFormat() @@ -1800,25 +2109,47 @@ void tst_QDateTime::fromStringToStringLocale() QLocale::setDefault(def); } -void tst_QDateTime::utcOffset() +void tst_QDateTime::offsetFromUtc() { /* Check default value. */ - QCOMPARE(QDateTime().utcOffset(), 0); + QCOMPARE(QDateTime().offsetFromUtc(), 0); + + // Offset constructor + QDateTime dt1(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, 60 * 60); + QCOMPARE(dt1.offsetFromUtc(), 60 * 60); + dt1 = QDateTime(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, -60 * 60); + QCOMPARE(dt1.offsetFromUtc(), -60 * 60); + + // UTC should be 0 offset + QDateTime dt2(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::UTC); + QCOMPARE(dt2.offsetFromUtc(), 0); + + // LocalTime should vary + if (europeanTimeZone) { + // Time definitely in Standard Time so 1 hour ahead + QDateTime dt3(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::LocalTime); + QCOMPARE(dt3.offsetFromUtc(), 1 * 60 * 60); + // Time definitely in Daylight Time so 2 hours ahead + QDateTime dt4(QDate(2013, 6, 1), QTime(0, 0, 0), Qt::LocalTime); + QCOMPARE(dt4.offsetFromUtc(), 2 * 60 * 60); + } else { + QSKIP("You must test using Central European (CET/CEST) time zone, e.g. TZ=Europe/Oslo"); + } } -void tst_QDateTime::setUtcOffset() +void tst_QDateTime::setOffsetFromUtc() { /* Basic tests. */ { QDateTime dt(QDateTime::currentDateTime()); dt.setTimeSpec(Qt::LocalTime); - dt.setUtcOffset(0); - QCOMPARE(dt.utcOffset(), 0); + dt.setOffsetFromUtc(0); + QCOMPARE(dt.offsetFromUtc(), 0); QCOMPARE(dt.timeSpec(), Qt::UTC); - dt.setUtcOffset(-100); - QCOMPARE(dt.utcOffset(), -100); + dt.setOffsetFromUtc(-100); + QCOMPARE(dt.offsetFromUtc(), -100); QCOMPARE(dt.timeSpec(), Qt::OffsetFromUTC); } @@ -1826,31 +2157,108 @@ void tst_QDateTime::setUtcOffset() { QDateTime dt(QDateTime::currentDateTime()); QDateTime dt2(dt); + int offset2 = dt2.offsetFromUtc(); - dt.setUtcOffset(501); + dt.setOffsetFromUtc(501); - QCOMPARE(dt.utcOffset(), 501); - QCOMPARE(dt2.utcOffset(), 0); + QCOMPARE(dt.offsetFromUtc(), 501); + QCOMPARE(dt2.offsetFromUtc(), offset2); } /* Check copying. */ { QDateTime dt(QDateTime::currentDateTime()); - dt.setUtcOffset(502); - QCOMPARE(dt.utcOffset(), 502); + dt.setOffsetFromUtc(502); + QCOMPARE(dt.offsetFromUtc(), 502); QDateTime dt2(dt); - QCOMPARE(dt2.utcOffset(), 502); + QCOMPARE(dt2.offsetFromUtc(), 502); } /* Check assignment. */ { QDateTime dt(QDateTime::currentDateTime()); - dt.setUtcOffset(502); + dt.setOffsetFromUtc(502); QDateTime dt2; dt2 = dt; - QCOMPARE(dt2.utcOffset(), 502); + QCOMPARE(dt2.offsetFromUtc(), 502); + } + + // Check spec persists + QDateTime dt1(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::OffsetFromUTC, 60 * 60); + dt1.setMSecsSinceEpoch(123456789); + QCOMPARE(dt1.timeSpec(), Qt::OffsetFromUTC); + QCOMPARE(dt1.offsetFromUtc(), 60 * 60); + dt1.setTime_t(123456789); + QCOMPARE(dt1.timeSpec(), Qt::OffsetFromUTC); + QCOMPARE(dt1.offsetFromUtc(), 60 * 60); + + // Check datastream serialises the offset seconds + QByteArray tmp; + { + QDataStream ds(&tmp, QIODevice::WriteOnly); + ds << dt1; + } + QDateTime dt2; + { + QDataStream ds(&tmp, QIODevice::ReadOnly); + ds >> dt2; + } + QCOMPARE(dt2, dt1); + QCOMPARE(dt2.timeSpec(), Qt::OffsetFromUTC); + QCOMPARE(dt2.offsetFromUtc(), 60 * 60); +} + +void tst_QDateTime::toOffsetFromUtc() +{ + QDateTime dt1(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::UTC); + + QDateTime dt2 = dt1.toOffsetFromUtc(60 * 60); + QCOMPARE(dt2, dt1); + QCOMPARE(dt2.timeSpec(), Qt::OffsetFromUTC); + QCOMPARE(dt2.date(), QDate(2013, 1, 1)); + QCOMPARE(dt2.time(), QTime(1, 0, 0)); + + dt2 = dt1.toOffsetFromUtc(0); + QCOMPARE(dt2, dt1); + QCOMPARE(dt2.timeSpec(), Qt::UTC); + QCOMPARE(dt2.date(), QDate(2013, 1, 1)); + QCOMPARE(dt2.time(), QTime(0, 0, 0)); + + dt2 = dt1.toTimeSpec(Qt::OffsetFromUTC); + QCOMPARE(dt2, dt1); + QCOMPARE(dt2.timeSpec(), Qt::UTC); + QCOMPARE(dt2.date(), QDate(2013, 1, 1)); + QCOMPARE(dt2.time(), QTime(0, 0, 0)); +} + +void tst_QDateTime::timeZoneAbbreviation() +{ + QDateTime dt1(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, 60 * 60); + QCOMPARE(dt1.timeZoneAbbreviation(), QString("UTC+01:00")); + QDateTime dt2(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, -60 * 60); + QCOMPARE(dt2.timeZoneAbbreviation(), QString("UTC-01:00")); + + QDateTime dt3(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::UTC); + QCOMPARE(dt3.timeZoneAbbreviation(), QString("UTC")); + + // LocalTime should vary + if (europeanTimeZone) { + // Time definitely in Standard Time + QDateTime dt4(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::LocalTime); +#ifdef Q_OS_WIN + QEXPECT_FAIL("", "Windows only returns long name (QTBUG-32759)", Continue); +#endif // Q_OS_WIN + QCOMPARE(dt4.timeZoneAbbreviation(), QString("CET")); + // Time definitely in Daylight Time + QDateTime dt5(QDate(2013, 6, 1), QTime(0, 0, 0), Qt::LocalTime); +#ifdef Q_OS_WIN + QEXPECT_FAIL("", "Windows only returns long name (QTBUG-32759)", Continue); +#endif // Q_OS_WIN + QCOMPARE(dt5.timeZoneAbbreviation(), QString("CEST")); + } else { + QSKIP("You must test using Central European (CET/CEST) time zone, e.g. TZ=Europe/Oslo"); } } @@ -1928,8 +2336,8 @@ void tst_QDateTime::utcOffsetLessThan() const QDateTime dt1(QDate(2002, 10, 10), QTime(0, 0, 0)); QDateTime dt2(dt1); - dt1.setUtcOffset(-(2 * 60 * 60)); // Minus two hours. - dt2.setUtcOffset(-(3 * 60 * 60)); // Minus three hours. + dt1.setOffsetFromUtc(-(2 * 60 * 60)); // Minus two hours. + dt2.setOffsetFromUtc(-(3 * 60 * 60)); // Minus three hours. QVERIFY(dt1 != dt2); QVERIFY(!(dt1 == dt2)); diff --git a/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp b/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp index fa747b3c18..3348b49110 100644 --- a/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp +++ b/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp @@ -730,14 +730,16 @@ double static inline _fast_cbrt(double d) void tst_QEasingCurve::testCbrtDouble() { - const qreal errorBound = 0.0001; + const double errorBound = 0.0001; for (int i = 0; i < 100000; i++) { double d = double(i) / 1000.0; double t = _fast_cbrt(d); const double t_cubic = t * t * t; - t = t * (t_cubic + d + d) / (t_cubic + t_cubic + d); + const double f = t_cubic + t_cubic + d; + if (f != 0.0) + t = t * (t_cubic + d + d) / f; double expected = pow(d, 1.0/3.0); @@ -754,14 +756,16 @@ void tst_QEasingCurve::testCbrtDouble() void tst_QEasingCurve::testCbrtFloat() { - const qreal errorBound = 0.0005; + const float errorBound = 0.0005; - for (int i = 1; i < 100000; i++) { + for (int i = 0; i < 100000; i++) { float f = float(i) / 1000.0f; float t = _fast_cbrt(f); const float t_cubic = t * t * t; - t = t * (t_cubic + f + f) / (t_cubic + t_cubic + f); + const float fac = t_cubic + t_cubic + f; + if (fac != 0.0f) + t = t * (t_cubic + f + f) / fac; float expected = pow(f, float(1.0/3.0)); diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index 71428310b8..af1c7aed15 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -78,6 +78,7 @@ private slots: void qthash_data(); void qthash(); + void eraseValidIteratorOnSharedHash(); }; struct Foo { @@ -1352,5 +1353,34 @@ void tst_QHash::qthash() QTEST(result, "hash"); } +void tst_QHash::eraseValidIteratorOnSharedHash() +{ + QHash<int, int> a, b; + a.insert(10, 10); + a.insertMulti(10, 25); + a.insertMulti(10, 30); + a.insert(20, 20); + a.insert(40, 40); + + QHash<int, int>::iterator i = a.begin(); + while (i.value() != 25) + ++i; + + b = a; + a.erase(i); + + QCOMPARE(b.size(), 5); + QCOMPARE(a.size(), 4); + + for (i = a.begin(); i != a.end(); ++i) + QVERIFY(i.value() != 25); + + int itemsWith10 = 0; + for (i = b.begin(); i != b.end(); ++i) + itemsWith10 += (i.key() == 10); + + QCOMPARE(itemsWith10, 3); +} + QTEST_APPLESS_MAIN(tst_QHash) #include "tst_qhash.moc" diff --git a/tests/auto/corelib/tools/qlinkedlist/qlinkedlist.pro b/tests/auto/corelib/tools/qlinkedlist/qlinkedlist.pro new file mode 100644 index 0000000000..439bf03707 --- /dev/null +++ b/tests/auto/corelib/tools/qlinkedlist/qlinkedlist.pro @@ -0,0 +1,5 @@ +CONFIG += testcase parallel_test +TARGET = tst_qlinkedlist +QT = core testlib +SOURCES = tst_qlinkedlist.cpp +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/tools/qlinkedlist/tst_qlinkedlist.cpp b/tests/auto/corelib/tools/qlinkedlist/tst_qlinkedlist.cpp new file mode 100644 index 0000000000..49b32d5534 --- /dev/null +++ b/tests/auto/corelib/tools/qlinkedlist/tst_qlinkedlist.cpp @@ -0,0 +1,1100 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QLinkedList> + +struct Movable +{ + Movable(char input = 'j') : i(input), state(Constructed) + { + ++liveCount; + } + Movable(const Movable &other) + : i(other.i) + , state(Constructed) + { + check(other.state, Constructed); + ++liveCount; + } + + ~Movable() + { + check(state, Constructed); + i = 0; + --liveCount; + state = Destructed; + } + + bool operator ==(const Movable &other) const + { + check(state, Constructed); + check(other.state, Constructed); + return i == other.i; + } + + Movable &operator=(const Movable &other) + { + check(state, Constructed); + check(other.state, Constructed); + i = other.i; + return *this; + } + char i; + + static int getLiveCount() { return liveCount; } +private: + static int liveCount; + + enum State { Constructed = 106, Destructed = 110 }; + State state; + + static void check(const State state1, const State state2) + { + QCOMPARE(int(state1), int(state2)); + } +}; + +int Movable::liveCount = 0; + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(Movable, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(Movable); + +Q_DECLARE_METATYPE(QLinkedList<int>); + + +int qHash(const Movable& movable) +{ + return qHash(movable.i); +} + +struct Complex +{ + Complex(int val = 0) + : value(val) + , checkSum(this) + { + ++liveCount; + } + + Complex(Complex const &other) + : value(other.value) + , checkSum(this) + { + ++liveCount; + } + + Complex &operator=(Complex const &other) + { + check(); other.check(); + + value = other.value; + return *this; + } + + ~Complex() + { + --liveCount; + check(); + } + + operator int() const { return value; } + + bool operator==(Complex const &other) const + { + check(); other.check(); + return value == other.value; + } + + void check() const + { + QVERIFY(this == checkSum); + } + + static int getLiveCount() { return liveCount; } +private: + static int liveCount; + + int value; + void *checkSum; +}; + +int Complex::liveCount = 0; + +Q_DECLARE_METATYPE(Complex); + +// Tests depend on the fact that: +Q_STATIC_ASSERT(!QTypeInfo<int>::isStatic); +Q_STATIC_ASSERT(!QTypeInfo<int>::isComplex); +Q_STATIC_ASSERT(!QTypeInfo<Movable>::isStatic); +Q_STATIC_ASSERT(QTypeInfo<Movable>::isComplex); +Q_STATIC_ASSERT(QTypeInfo<Complex>::isStatic); +Q_STATIC_ASSERT(QTypeInfo<Complex>::isComplex); + +class tst_QLinkedList : public QObject +{ + Q_OBJECT +private slots: + void eraseValidIteratorsOnSharedList() const; + void insertWithIteratorsOnSharedList() const; + void lengthInt() const; + void lengthMovable() const; + void lengthComplex() const; + void lengthSignature() const; + void firstInt() const; + void firstMovable() const; + void firstComplex() const; + void lastInt() const; + void lastMovable() const; + void lastComplex() const; + void beginInt() const; + void beginMovable() const; + void beginComplex() const; + void endInt() const; + void endMovable() const; + void endComplex() const; + void containsInt() const; + void containsMovable() const; + void containsComplex() const; + void countInt() const; + void countMovable() const; + void countComplex() const; + void emptyInt() const; + void emptyMovable() const; + void emptyComplex() const; + void endsWithInt() const; + void endsWithMovable() const; + void endsWithComplex() const; + void removeAllInt() const; + void removeAllMovable() const; + void removeAllComplex() const; + void removeOneInt() const; + void removeOneMovable() const; + void removeOneComplex() const; + void startsWithInt() const; + void startsWithMovable() const; + void startsWithComplex() const; + void takeFirstInt() const; + void takeFirstMovable() const; + void takeFirstComplex() const; + void takeLastInt() const; + void takeLastMovable() const; + void takeLastComplex() const; + void toStdListInt() const; + void toStdListMovable() const; + void toStdListComplex() const; + void testOperatorsInt() const; + void testOperatorsMovable() const; + void testOperatorsComplex() const; + void testSTLIteratorsInt() const; + void testSTLIteratorsMovable() const; + void testSTLIteratorsComplex() const; + + void initializeList() const; + + void constSharedNullInt() const; + void constSharedNullMovable() const; + void constSharedNullComplex() const; + + void setSharableInt() const; +private: + template<typename T> void length() const; + template<typename T> void first() const; + template<typename T> void last() const; + template<typename T> void begin() const; + template<typename T> void end() const; + template<typename T> void contains() const; + template<typename T> void count() const; + template<typename T> void empty() const; + template<typename T> void endsWith() const; + template<typename T> void move() const; + template<typename T> void removeAll() const; + template<typename T> void removeOne() const; + template<typename T> void startsWith() const; + template<typename T> void swap() const; + template<typename T> void takeFirst() const; + template<typename T> void takeLast() const; + template<typename T> void toStdList() const; + template<typename T> void value() const; + + template<typename T> void testOperators() const; + template<typename T> void testSTLIterators() const; + + template<typename T> void constSharedNull() const; + + int dummyForGuard; +}; + +template<typename T> struct SimpleValue +{ + static T at(int index) + { + return values[index % maxSize]; + } + static const uint maxSize = 7; + static const T values[maxSize]; +}; + +template<> +const int SimpleValue<int>::values[] = { 10, 20, 30, 40, 100, 101, 102 }; +template<> +const Movable SimpleValue<Movable>::values[] = { 10, 20, 30, 40, 100, 101, 102 }; +template<> +const Complex SimpleValue<Complex>::values[] = { 10, 20, 30, 40, 100, 101, 102 }; + +// Make some macros for the tests to use in order to be slightly more readable... +#define T_FOO SimpleValue<T>::at(0) +#define T_BAR SimpleValue<T>::at(1) +#define T_BAZ SimpleValue<T>::at(2) +#define T_CAT SimpleValue<T>::at(3) +#define T_DOG SimpleValue<T>::at(4) +#define T_BLAH SimpleValue<T>::at(5) +#define T_WEEE SimpleValue<T>::at(6) + +template<typename T> +void tst_QLinkedList::length() const +{ + /* Empty list. */ + { + const QLinkedList<T> list; + QCOMPARE(list.size(), 0); + } + + /* One entry. */ + { + QLinkedList<T> list; + list.append(T_FOO); + QCOMPARE(list.size(), 1); + } + + /* Two entries. */ + { + QLinkedList<T> list; + list.append(T_FOO); + list.append(T_BAR); + QCOMPARE(list.size(), 2); + } + + /* Three entries. */ + { + QLinkedList<T> list; + list.append(T_FOO); + list.append(T_BAR); + list.append(T_BAZ); + QCOMPARE(list.size(), 3); + } +} + +void tst_QLinkedList::eraseValidIteratorsOnSharedList() const +{ + QLinkedList<int> a, b; + a.append(5); + a.append(10); + a.append(20); + a.append(20); + a.append(20); + a.append(20); + a.append(30); + + QLinkedList<int>::iterator i = a.begin(); + ++i; + ++i; + ++i; + b = a; + QLinkedList<int>::iterator r = a.erase(i); + QCOMPARE(b.size(), 7); + QCOMPARE(a.size(), 6); + --r; + --r; + QCOMPARE(*r, 10); // Ensure that number 2 instance was removed; +} + +void tst_QLinkedList::insertWithIteratorsOnSharedList() const +{ + QLinkedList<int> a, b; + a.append(5); + a.append(10); + a.append(20); + QLinkedList<int>::iterator i = a.begin(); + ++i; + ++i; + b = a; + + QLinkedList<int>::iterator i2 = a.insert(i, 15); + QCOMPARE(b.size(), 3); + QCOMPARE(a.size(), 4); + --i2; + QCOMPARE(*i2, 10); +} + +void tst_QLinkedList::lengthInt() const +{ + length<int>(); +} + +void tst_QLinkedList::lengthMovable() const +{ + const int liveCount = Movable::getLiveCount(); + length<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::lengthComplex() const +{ + const int liveCount = Complex::getLiveCount(); + length<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +void tst_QLinkedList::lengthSignature() const +{ + /* Constness. */ + { + const QLinkedList<int> list; + /* The function should be const. */ + list.size(); + } +} + +template<typename T> +void tst_QLinkedList::first() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR; + + QCOMPARE(list.first(), T_FOO); + + // remove an item, make sure it still works + list.pop_front(); + QVERIFY(list.size() == 1); + QCOMPARE(list.first(), T_BAR); +} + +void tst_QLinkedList::firstInt() const +{ + first<int>(); +} + +void tst_QLinkedList::firstMovable() const +{ + const int liveCount = Movable::getLiveCount(); + first<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::firstComplex() const +{ + const int liveCount = Complex::getLiveCount(); + first<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::last() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR; + + QCOMPARE(list.last(), T_BAR); + + // remove an item, make sure it still works + list.pop_back(); + QVERIFY(list.size() == 1); + QCOMPARE(list.last(), T_FOO); +} + +void tst_QLinkedList::lastInt() const +{ + last<int>(); +} + +void tst_QLinkedList::lastMovable() const +{ + const int liveCount = Movable::getLiveCount(); + last<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::lastComplex() const +{ + const int liveCount = Complex::getLiveCount(); + last<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::begin() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR; + + QCOMPARE(*list.begin(), T_FOO); + + // remove an item, make sure it still works + list.pop_front(); + QVERIFY(list.size() == 1); + QCOMPARE(*list.begin(), T_BAR); +} + +void tst_QLinkedList::beginInt() const +{ + begin<int>(); +} + +void tst_QLinkedList::beginMovable() const +{ + const int liveCount = Movable::getLiveCount(); + begin<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::beginComplex() const +{ + const int liveCount = Complex::getLiveCount(); + begin<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::end() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR; + + QCOMPARE(*--list.end(), T_BAR); + + // remove an item, make sure it still works + list.pop_back(); + QVERIFY(list.size() == 1); + QCOMPARE(*--list.end(), T_FOO); +} + +void tst_QLinkedList::endInt() const +{ + end<int>(); +} + +void tst_QLinkedList::endMovable() const +{ + const int liveCount = Movable::getLiveCount(); + end<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::endComplex() const +{ + const int liveCount = Complex::getLiveCount(); + end<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::contains() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + QVERIFY(list.contains(T_FOO) == true); + QVERIFY(list.contains(T_BLAH) != true); + + // add it and make sure it matches + list.append(T_BLAH); + QVERIFY(list.contains(T_BLAH) == true); +} + +void tst_QLinkedList::containsInt() const +{ + contains<int>(); +} + +void tst_QLinkedList::containsMovable() const +{ + const int liveCount = Movable::getLiveCount(); + contains<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::containsComplex() const +{ + const int liveCount = Complex::getLiveCount(); + contains<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::count() const +{ + QLinkedList<T> list; + + // starts empty + QVERIFY(list.count() == 0); + + // goes up + list.append(T_FOO); + QVERIFY(list.count() == 1); + + // and up + list.append(T_BAR); + QVERIFY(list.count() == 2); + + // and down + list.pop_back(); + QVERIFY(list.count() == 1); + + // and empty. :) + list.pop_back(); + QVERIFY(list.count() == 0); +} + +void tst_QLinkedList::countInt() const +{ + count<int>(); +} + +void tst_QLinkedList::countMovable() const +{ + const int liveCount = Movable::getLiveCount(); + count<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::countComplex() const +{ + const int liveCount = Complex::getLiveCount(); + count<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::empty() const +{ + QLinkedList<T> list; + + // make sure it starts empty + QVERIFY(list.empty()); + + // and doesn't stay empty + list.append(T_FOO); + QVERIFY(!list.empty()); + + // and goes back to being empty + list.pop_back(); + QVERIFY(list.empty()); +} + +void tst_QLinkedList::emptyInt() const +{ + empty<int>(); +} + +void tst_QLinkedList::emptyMovable() const +{ + const int liveCount = Movable::getLiveCount(); + empty<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::emptyComplex() const +{ + const int liveCount = Complex::getLiveCount(); + empty<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::endsWith() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + // test it returns correctly in both cases + QVERIFY(list.endsWith(T_BAZ)); + QVERIFY(!list.endsWith(T_BAR)); + + // remove an item and make sure the end item changes + list.pop_back(); + QVERIFY(list.endsWith(T_BAR)); +} + +void tst_QLinkedList::endsWithInt() const +{ + endsWith<int>(); +} + +void tst_QLinkedList::endsWithMovable() const +{ + const int liveCount = Movable::getLiveCount(); + endsWith<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::endsWithComplex() const +{ + const int liveCount = Complex::getLiveCount(); + endsWith<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::removeAll() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + // remove one instance + list.removeAll(T_BAR); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAZ); + + // many instances + list << T_FOO << T_BAR << T_BAZ << T_FOO << T_BAR << T_BAZ << T_FOO << T_BAR << T_BAZ; + list.removeAll(T_BAR); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAZ << T_FOO << T_BAZ << T_FOO << T_BAZ << T_FOO << T_BAZ); + + // try remove something that doesn't exist + list.removeAll(T_WEEE); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAZ << T_FOO << T_BAZ << T_FOO << T_BAZ << T_FOO << T_BAZ); +} + +void tst_QLinkedList::removeAllInt() const +{ + removeAll<int>(); +} + +void tst_QLinkedList::removeAllMovable() const +{ + const int liveCount = Movable::getLiveCount(); + removeAll<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::removeAllComplex() const +{ + const int liveCount = Complex::getLiveCount(); + removeAll<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::removeOne() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + // middle + list.removeOne(T_BAR); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAZ); + + // start + list.removeOne(T_FOO); + QCOMPARE(list, QLinkedList<T>() << T_BAZ); + + // last + list.removeOne(T_BAZ); + QCOMPARE(list, QLinkedList<T>()); + + // make sure it really only removes one :) + list << T_FOO << T_FOO; + list.removeOne(T_FOO); + QCOMPARE(list, QLinkedList<T>() << T_FOO); + + // try remove something that doesn't exist + list.removeOne(T_WEEE); + QCOMPARE(list, QLinkedList<T>() << T_FOO); +} + +void tst_QLinkedList::removeOneInt() const +{ + removeOne<int>(); +} + +void tst_QLinkedList::removeOneMovable() const +{ + const int liveCount = Movable::getLiveCount(); + removeOne<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::removeOneComplex() const +{ + const int liveCount = Complex::getLiveCount(); + removeOne<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::startsWith() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + // make sure it starts ok + QVERIFY(list.startsWith(T_FOO)); + + // remove an item + list.removeFirst(); + QVERIFY(list.startsWith(T_BAR)); +} + +void tst_QLinkedList::startsWithInt() const +{ + startsWith<int>(); +} + +void tst_QLinkedList::startsWithMovable() const +{ + const int liveCount = Movable::getLiveCount(); + startsWith<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::startsWithComplex() const +{ + const int liveCount = Complex::getLiveCount(); + startsWith<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::takeFirst() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + QCOMPARE(list.takeFirst(), T_FOO); + QVERIFY(list.size() == 2); + QCOMPARE(list.takeFirst(), T_BAR); + QVERIFY(list.size() == 1); + QCOMPARE(list.takeFirst(), T_BAZ); + QVERIFY(list.size() == 0); +} + +void tst_QLinkedList::takeFirstInt() const +{ + takeFirst<int>(); +} + +void tst_QLinkedList::takeFirstMovable() const +{ + const int liveCount = Movable::getLiveCount(); + takeFirst<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::takeFirstComplex() const +{ + const int liveCount = Complex::getLiveCount(); + takeFirst<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::takeLast() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + QCOMPARE(list.takeLast(), T_BAZ); + QCOMPARE(list.takeLast(), T_BAR); + QCOMPARE(list.takeLast(), T_FOO); +} + +void tst_QLinkedList::takeLastInt() const +{ + takeLast<int>(); +} + +void tst_QLinkedList::takeLastMovable() const +{ + const int liveCount = Movable::getLiveCount(); + takeLast<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::takeLastComplex() const +{ + const int liveCount = Complex::getLiveCount(); + takeLast<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::toStdList() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + // yuck. + std::list<T> slist; + slist.push_back(T_FOO); + slist.push_back(T_BAR); + slist.push_back(T_BAZ); + + QCOMPARE(list.toStdList(), slist); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAR << T_BAZ); +} + +void tst_QLinkedList::toStdListInt() const +{ + toStdList<int>(); +} + +void tst_QLinkedList::toStdListMovable() const +{ + const int liveCount = Movable::getLiveCount(); + toStdList<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::toStdListComplex() const +{ + const int liveCount = Complex::getLiveCount(); + toStdList<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::testOperators() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + QLinkedList<T> listtwo; + listtwo << T_FOO << T_BAR << T_BAZ; + + // test equal + QVERIFY(list == listtwo); + + // not equal + listtwo.append(T_CAT); + QVERIFY(list != listtwo); + + // += + list += listtwo; + QVERIFY(list.size() == 7); + QVERIFY(listtwo.size() == 4); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAR << T_BAZ + << T_FOO << T_BAR << T_BAZ << T_CAT); + + // = + list = listtwo; + QCOMPARE(list, listtwo); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAR << T_BAZ << T_CAT); +} + +void tst_QLinkedList::testOperatorsInt() const +{ + testOperators<int>(); +} + +void tst_QLinkedList::testOperatorsMovable() const +{ + const int liveCount = Movable::getLiveCount(); + testOperators<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::testOperatorsComplex() const +{ + const int liveCount = Complex::getLiveCount(); + testOperators<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::testSTLIterators() const +{ + QLinkedList<T> list; + + // create a list + list << T_FOO << T_BAR << T_BAZ; + typename QLinkedList<T>::iterator it = list.begin(); + QCOMPARE(*it, T_FOO); it++; + QCOMPARE(*it, T_BAR); it++; + QCOMPARE(*it, T_BAZ); it++; + QCOMPARE(it, list.end()); it--; + + // walk backwards + QCOMPARE(*it, T_BAZ); it--; + QCOMPARE(*it, T_BAR); it--; + QCOMPARE(*it, T_FOO); + + // test erase + it = list.erase(it); + QVERIFY(list.size() == 2); + QCOMPARE(*it, T_BAR); + + // test multiple erase + it = list.erase(it, it + 2); + QVERIFY(list.size() == 0); + QCOMPARE(it, list.end()); + + // insert again + it = list.insert(it, T_FOO); + QVERIFY(list.size() == 1); + QCOMPARE(*it, T_FOO); + + // insert again + it = list.insert(it, T_BAR); + QVERIFY(list.size() == 2); + QCOMPARE(*it++, T_BAR); + QCOMPARE(*it, T_FOO); +} + +void tst_QLinkedList::testSTLIteratorsInt() const +{ + testSTLIterators<int>(); +} + +void tst_QLinkedList::testSTLIteratorsMovable() const +{ + const int liveCount = Movable::getLiveCount(); + testSTLIterators<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::testSTLIteratorsComplex() const +{ + const int liveCount = Complex::getLiveCount(); + testSTLIterators<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +void tst_QLinkedList::initializeList() const +{ +#ifdef Q_COMPILER_INITIALIZER_LISTS + QLinkedList<int> v1 { 2, 3, 4 }; + QCOMPARE(v1, QLinkedList<int>() << 2 << 3 << 4); + QCOMPARE(v1, (QLinkedList<int> { 2, 3, 4})); + + QLinkedList<QLinkedList<int>> v2{ v1, { 1 }, QLinkedList<int>(), { 2, 3, 4 } }; + QLinkedList<QLinkedList<int>> v3; + v3 << v1 << (QLinkedList<int>() << 1) << QLinkedList<int>() << v1; + QCOMPARE(v3, v2); +#endif +} + + +template<typename T> +void tst_QLinkedList::constSharedNull() const +{ + QLinkedList<T> list1; + list1.setSharable(false); + QVERIFY(list1.isDetached()); + + QLinkedList<T> list2; + list2.setSharable(true); + QVERIFY(!list2.isDetached()); +} + +void tst_QLinkedList::constSharedNullInt() const +{ + constSharedNull<int>(); +} + +void tst_QLinkedList::constSharedNullMovable() const +{ + const int liveCount = Movable::getLiveCount(); + constSharedNull<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::constSharedNullComplex() const +{ + const int liveCount = Complex::getLiveCount(); + constSharedNull<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + + +void tst_QLinkedList::setSharableInt() const +{ + QLinkedList<int> orglist; + orglist << 0 << 1 << 2 << 3 << 4 << 5; + int size = 6; + + QLinkedList<int> list; + list = orglist; + + QVERIFY(!list.isDetached()); + list.setSharable(true); + + QCOMPARE(list.size(), size); + + { + QLinkedList<int> copy(list); + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(list)); + } + + list.setSharable(false); + QVERIFY(list.isDetached() || list.isSharedWith(QLinkedList<int>())); + + { + QLinkedList<int> copy(list); + + QVERIFY(copy.isDetached() || copy.isSharedWith(QLinkedList<int>())); + QCOMPARE(copy.size(), size); + QCOMPARE(copy, list); + } + + list.setSharable(true); + + { + QLinkedList<int> copy(list); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(list)); + } + + QLinkedList<int>::const_iterator it = list.constBegin(); + for (int i = 0; i < list.size(); ++i) { + QCOMPARE(int(*it), i); + ++it; + } + + QCOMPARE(list.size(), size); +} + +QTEST_APPLESS_MAIN(tst_QLinkedList) +#include "tst_qlinkedlist.moc" diff --git a/tests/auto/corelib/tools/qlist/tst_qlist.cpp b/tests/auto/corelib/tools/qlist/tst_qlist.cpp index 787aa094a8..2c9bf9d4c9 100644 --- a/tests/auto/corelib/tools/qlist/tst_qlist.cpp +++ b/tests/auto/corelib/tools/qlist/tst_qlist.cpp @@ -276,6 +276,8 @@ private slots: void setSharableInt() const; void setSharableComplex_data() const; void setSharableComplex() const; + void eraseValidIteratorsOnSharedList() const; + void insertWithValidIteratorsOnSharedList() const; private: template<typename T> void length() const; template<typename T> void append() const; @@ -1620,5 +1622,52 @@ void tst_QList::setSharableComplex() const runSetSharableTest<Complex>(); } +void tst_QList::eraseValidIteratorsOnSharedList() const +{ + QList<int> a, b; + a.push_back(10); + a.push_back(20); + a.push_back(30); + QList<int>::iterator i = a.begin(); + ++i; + b = a; + a.erase(i); + QCOMPARE(b.size(), 3); + QCOMPARE(a.size(), 2); + QCOMPARE(a.at(0), 10); + QCOMPARE(a.at(1), 30); + + a.push_back(40); + a.push_back(50); + a.push_back(60); + QCOMPARE(a.size(), 5); + i = a.begin(); + b = a; + ++i; + QList<int>::iterator j = i; + ++j; + ++j; + a.erase(i, j); // remove 3 elements + QCOMPARE(b.size(), 5); + QCOMPARE(a.size(), 3); + QCOMPARE(a.at(0), 10); + QCOMPARE(a.at(1), 50); +} + +void tst_QList::insertWithValidIteratorsOnSharedList() const +{ + QList<int> a, b; + a.push_back(10); + a.push_back(20); + a.push_back(30); + QList<int>::iterator i = a.begin(); + ++i; + b = a; + a.insert(i, 15); + QCOMPARE(a.size(), b.size() + 1); + QCOMPARE(b.at(1), 20); + QCOMPARE(a.at(1), 15); +} + QTEST_APPLESS_MAIN(tst_QList) #include "tst_qlist.moc" diff --git a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp index 0c4dde4b1a..ea0e90a503 100644 --- a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp @@ -163,6 +163,7 @@ private slots: void formatTime_data(); void formatDateTime(); void formatDateTime_data(); + void formatTimeZone(); void toDateTime_data(); void toDateTime(); void negativeNumbers(); @@ -199,6 +200,7 @@ private slots: private: QString m_decimal, m_thousand, m_sdate, m_ldate, m_time; QString m_sysapp; + bool europeanTimeZone; #ifdef Q_OS_BLACKBERRY int m_languageFd; @@ -208,6 +210,11 @@ private: tst_QLocale::tst_QLocale() { qRegisterMetaType<QLocale::FormatType>("QLocale::FormatType"); + + // Test if in Central European Time zone + uint x1 = QDateTime(QDate(1990, 1, 1), QTime()).toTime_t(); + uint x2 = QDateTime(QDate(1990, 6, 1), QTime()).toTime_t(); + europeanTimeZone = (x1 == 631148400 && x2 == 644191200); } void tst_QLocale::initTestCase() @@ -1198,6 +1205,59 @@ void tst_QLocale::formatDateTime_data() QTest::newRow("28no_NO") << "no_NO" << QDateTime() << "'\"yymm\"'" << ""; + QDateTime testLongHour(QDate(1999, 12, 31), QTime(23, 59, 59, 999)); + QDateTime testShortHour(QDate(1999, 12, 31), QTime(3, 59, 59, 999)); + QDateTime testZeroHour(QDate(1999, 12, 31), QTime(0, 59, 59, 999)); + + QTest::newRow("datetime0") << "en_US" << QDateTime() + << QString("dd-MM-yyyy hh:mm:ss") << QString(); + QTest::newRow("datetime1") << "en_US" << testLongHour + << QString("dd-'mmddyy'MM-yyyy hh:mm:ss.zzz") + << QString("31-mmddyy12-1999 23:59:59.999"); + QTest::newRow("datetime2") << "en_US" << testLongHour + << QString("dd-'apAP'MM-yyyy hh:mm:ss.zzz") + << QString("31-apAP12-1999 23:59:59.999"); + QTest::newRow("datetime3") << "en_US" << testLongHour + << QString("Apdd-MM-yyyy hh:mm:ss.zzz") + << QString("PMp31-12-1999 11:59:59.999"); + QTest::newRow("datetime4") << "en_US" << testLongHour + << QString("'ap'apdd-MM-yyyy 'AP'hh:mm:ss.zzz") + << QString("appm31-12-1999 AP11:59:59.999"); + QTest::newRow("datetime5") << "en_US" << testLongHour + << QString("'''") << QString("'"); + QTest::newRow("datetime6") << "en_US" << testLongHour + << QString("'ap") << QString("ap"); + QTest::newRow("datetime7") << "en_US" << testLongHour + << QString("' ' 'hh' hh") << QString(" hh 23"); + QTest::newRow("datetime8") << "en_US" << testLongHour + << QString("d'foobar'") << QString("31foobar"); + QTest::newRow("datetime9") << "en_US" << testShortHour + << QString("hhhhh") << QString("03033"); + QTest::newRow("datetime11") << "en_US" << testLongHour + << QString("HHHhhhAaAPap") << QString("23231111PMpmPMpm"); + QTest::newRow("datetime12") << "en_US" << testShortHour + << QString("HHHhhhAaAPap") << QString("033033AMamAMam"); + QTest::newRow("datetime13") << "en_US" << QDateTime(QDate(1974, 12, 1), QTime(14, 14, 20)) + << QString("hh''mm''ss dd''MM''yyyy") + << QString("14'14'20 01'12'1974"); + QTest::newRow("AM no p") << "en_US" << testZeroHour + << QString("hhAX") << QString("12AMX"); + QTest::newRow("AM no p, x 2") << "en_US" << testShortHour + << QString("hhhhhaA") << QString("03033amAM"); + QTest::newRow("am 0 hour") << "en_US" << testZeroHour + << QString("hAP") << QString("12AM"); + QTest::newRow("AM zero hour") << "en_US" << testZeroHour + << QString("hhAP") << QString("12AM"); + QTest::newRow("dddd") << "en_US" << testZeroHour + << QString("dddd") << QString("Friday"); + QTest::newRow("ddd") << "en_US" << testZeroHour + << QString("ddd") << QString("Fri"); + QTest::newRow("MMMM") << "en_US" << testZeroHour + << QString("MMMM") << QString("December"); + QTest::newRow("MMM") << "en_US" << testZeroHour + << QString("MMM") << QString("Dec"); + QTest::newRow("empty") << "en_US" << testZeroHour + << QString("") << QString(""); } void tst_QLocale::formatDateTime() @@ -1211,6 +1271,46 @@ void tst_QLocale::formatDateTime() QCOMPARE(l.toString(dateTime, format), result); } +void tst_QLocale::formatTimeZone() +{ + QLocale enUS("en_US"); + + QDateTime dt1(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, 60 * 60); + QCOMPARE(enUS.toString(dt1, "t"), QString("UTC+01:00")); + + QDateTime dt2(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, -60 * 60); + QCOMPARE(enUS.toString(dt2, "t"), QString("UTC-01:00")); + + QDateTime dt3(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::UTC); + QCOMPARE(enUS.toString(dt3, "t"), QString("UTC")); + + // LocalTime should vary + if (europeanTimeZone) { + // Time definitely in Standard Time + QDateTime dt4(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::LocalTime); +#ifdef Q_OS_WIN + QEXPECT_FAIL("", "Windows only returns long name (QTBUG-32759)", Continue); +#endif // Q_OS_WIN + QCOMPARE(enUS.toString(dt4, "t"), QString("CET")); + + // Time definitely in Daylight Time + QDateTime dt5(QDate(2013, 6, 1), QTime(0, 0, 0), Qt::LocalTime); +#ifdef Q_OS_WIN + QEXPECT_FAIL("", "Windows only returns long name (QTBUG-32759)", Continue); +#endif // Q_OS_WIN + QCOMPARE(enUS.toString(dt5, "t"), QString("CEST")); + } else { + QSKIP("You must test using Central European (CET/CEST) time zone, e.g. TZ=Europe/Oslo"); + } + + // Current datetime should return current abbreviation + QCOMPARE(enUS.toString(QDateTime::currentDateTime(), "t"), + QDateTime::currentDateTime().timeZoneAbbreviation()); + + // Time on its own will always be current local time zone + QCOMPARE(enUS.toString(QTime(1, 2, 3), "t"), QDateTime::currentDateTime().timeZoneAbbreviation()); +} + void tst_QLocale::toDateTime_data() { QTest::addColumn<QString>("localeName"); diff --git a/tests/auto/corelib/tools/qmap/tst_qmap.cpp b/tests/auto/corelib/tools/qmap/tst_qmap.cpp index 0742f19a5e..dea657f842 100644 --- a/tests/auto/corelib/tools/qmap/tst_qmap.cpp +++ b/tests/auto/corelib/tools/qmap/tst_qmap.cpp @@ -59,6 +59,7 @@ private slots: void count(); void clear(); void beginEnd(); + void firstLast(); void key(); void swap(); @@ -87,6 +88,7 @@ private slots: void initializerList(); void testInsertWithHint(); void testInsertMultiWithHint(); + void eraseValidIteratorOnSharedMap(); }; typedef QMap<QString, QString> StringMap; @@ -376,6 +378,34 @@ void tst_QMap::beginEnd() QVERIFY( map.constBegin() != map2.constBegin() ); } +void tst_QMap::firstLast() +{ + // sample string->string map + StringMap map; + map.insert("0", "a"); + map.insert("1", "b"); + map.insert("5", "e"); + + QCOMPARE(map.firstKey(), QStringLiteral("0")); + QCOMPARE(map.lastKey(), QStringLiteral("5")); + QCOMPARE(map.first(), QStringLiteral("a")); + QCOMPARE(map.last(), QStringLiteral("e")); + + // const map + const StringMap const_map = map; + QCOMPARE(map.firstKey(), const_map.firstKey()); + QCOMPARE(map.lastKey(), const_map.lastKey()); + QCOMPARE(map.first(), const_map.first()); + QCOMPARE(map.last(), const_map.last()); + + map.take(map.firstKey()); + QCOMPARE(map.firstKey(), QStringLiteral("1")); + QCOMPARE(map.lastKey(), QStringLiteral("5")); + + map.take(map.lastKey()); + QCOMPARE(map.lastKey(), map.lastKey()); +} + void tst_QMap::key() { { @@ -1289,6 +1319,62 @@ void tst_QMap::testInsertMultiWithHint() QCOMPARE(map.size(), 14); } +void tst_QMap::eraseValidIteratorOnSharedMap() +{ + QMap<int, int> a, b; + a.insert(10, 10); + a.insertMulti(10, 40); + a.insertMulti(10, 25); + a.insertMulti(10, 30); + a.insert(20, 20); + + QMap<int, int>::iterator i = a.begin(); + while (i.value() != 25) + ++i; + + b = a; + a.erase(i); + + QCOMPARE(b.size(), 5); + QCOMPARE(a.size(), 4); + + for (i = a.begin(); i != a.end(); ++i) + QVERIFY(i.value() != 25); + + int itemsWith10 = 0; + for (i = b.begin(); i != b.end(); ++i) + itemsWith10 += (i.key() == 10); + + QCOMPARE(itemsWith10, 4); + + // Border cases + QMap <QString, QString> ms1, ms2, ms3; + ms1.insert("foo", "bar"); + ms1.insertMulti("foo", "quux"); + ms1.insertMulti("foo", "bar"); + + QMap <QString, QString>::iterator si = ms1.begin(); + ms2 = ms1; + ms1.erase(si); + si = ms1.begin(); + QCOMPARE(si.value(), QString("quux")); + ++si; + QCOMPARE(si.value(), QString("bar")); + + si = ms2.begin(); + ++si; + ++si; + ms3 = ms2; + ms2.erase(si); + si = ms2.begin(); + QCOMPARE(si.value(), QString("bar")); + ++si; + QCOMPARE(si.value(), QString("quux")); + + QCOMPARE(ms1.size(), 2); + QCOMPARE(ms2.size(), 2); + QCOMPARE(ms3.size(), 3); +} QTEST_APPLESS_MAIN(tst_QMap) #include "tst_qmap.moc" diff --git a/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp b/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp index 0c76faf613..d786d1a2ce 100644 --- a/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp @@ -79,6 +79,7 @@ static QString makespec() QT_BEGIN_NAMESPACE namespace QTest { +#ifndef QT_NO_PROCESS class QExternalProcess: public QProcess { protected: @@ -99,6 +100,7 @@ namespace QTest { } #endif }; +#endif // !QT_NO_PROCESS class QExternalTestPrivate { @@ -565,6 +567,7 @@ namespace QTest { bool QExternalTestPrivate::runQmake() { +#ifndef QT_NO_PROCESS if (temporaryDirPath.isEmpty()) qWarning() << "Temporary directory is expected to be non-empty"; @@ -607,10 +610,16 @@ namespace QTest { } return ok && exitCode == 0; +#else // QT_NO_PROCESS + return false; +#endif // QT_NO_PROCESS } bool QExternalTestPrivate::runMake(Target target) { +#ifdef QT_NO_PROCESS + return false; +#else if (temporaryDirPath.isEmpty()) qWarning() << "Temporary directory is expected to be non-empty"; @@ -666,6 +675,7 @@ namespace QTest { std_err += make.readAllStandardError(); return ok; +#endif // !QT_NO_PROCESS } bool QExternalTestPrivate::commonSetup(const QByteArray &body) diff --git a/tests/auto/corelib/tools/qstringref/tst_qstringref.cpp b/tests/auto/corelib/tools/qstringref/tst_qstringref.cpp index 1045d5929f..7bbcee8ab2 100644 --- a/tests/auto/corelib/tools/qstringref/tst_qstringref.cpp +++ b/tests/auto/corelib/tools/qstringref/tst_qstringref.cpp @@ -88,6 +88,9 @@ private slots: void integer_conversion_data(); void integer_conversion(); void trimmed(); + void left(); + void right(); + void mid(); }; static QStringRef emptyRef() @@ -1794,6 +1797,145 @@ void tst_QStringRef::trimmed() QCOMPARE(b.trimmed().compare(QStringLiteral("a")), 0); } +void tst_QStringRef::left() +{ + QString originalString = "OrginalString~"; + QStringRef ref = originalString.leftRef(originalString.size() - 1); + QCOMPARE(ref.toString(), QStringLiteral("OrginalString")); + + QVERIFY(ref.left(0).toString().isEmpty()); + QCOMPARE(ref.left(ref.size()).toString(), QStringLiteral("OrginalString")); + + QStringRef nullRef; + QVERIFY(nullRef.isNull()); + QVERIFY(nullRef.left(3).toString().isEmpty()); + QVERIFY(nullRef.left(0).toString().isEmpty()); + QVERIFY(nullRef.left(-1).toString().isEmpty()); + + QStringRef emptyRef(&originalString, 0, 0); + QVERIFY(emptyRef.isEmpty()); + QVERIFY(emptyRef.left(3).toString().isEmpty()); + QVERIFY(emptyRef.left(0).toString().isEmpty()); + QVERIFY(emptyRef.left(-1).toString().isEmpty()); + + QCOMPARE(ref.left(-1), ref); + QCOMPARE(ref.left(100), ref); +} + +void tst_QStringRef::right() +{ + QString originalString = "~OrginalString"; + QStringRef ref = originalString.rightRef(originalString.size() - 1); + QCOMPARE(ref.toString(), QLatin1String("OrginalString")); + + QCOMPARE(ref.right(ref.size() - 6).toString(), QLatin1String("String")); + QCOMPARE(ref.right(ref.size()).toString(), QLatin1String("OrginalString")); + QCOMPARE(ref.right(0).toString(), QLatin1String("OrginalString")); + + QStringRef nullRef; + QVERIFY(nullRef.isNull()); + QVERIFY(nullRef.right(3).toString().isEmpty()); + QVERIFY(nullRef.right(0).toString().isEmpty()); + QVERIFY(nullRef.right(-1).toString().isEmpty()); + + QStringRef emptyRef(&originalString, 0, 0); + QVERIFY(emptyRef.isEmpty()); + QVERIFY(emptyRef.right(3).toString().isEmpty()); + QVERIFY(emptyRef.right(0).toString().isEmpty()); + QVERIFY(emptyRef.right(-1).toString().isEmpty()); + + QCOMPARE(ref.right(-1), ref); + QCOMPARE(ref.right(100), ref); +} + +void tst_QStringRef::mid() +{ + QString orig = QStringLiteral("~ABCDEFGHIEfGEFG~"); // 15 + 2 chars + QStringRef a = orig.midRef(1, 15); + QCOMPARE(a.size(), orig.size() - 2); + + QCOMPARE(a.mid(3,3).toString(),(QString)"DEF"); + QCOMPARE(a.mid(0,0).toString(),(QString)""); + QVERIFY(!a.mid(15,0).toString().isNull()); + QVERIFY(a.mid(15,0).toString().isEmpty()); + QVERIFY(!a.mid(15,1).toString().isNull()); + QVERIFY(a.mid(15,1).toString().isEmpty()); + QVERIFY(a.mid(9999).toString().isEmpty()); + QVERIFY(a.mid(9999,1).toString().isEmpty()); + + QCOMPARE(a.mid(-1, 6), a.mid(0, 5)); + QVERIFY(a.mid(-100, 6).isEmpty()); + QVERIFY(a.mid(INT_MIN, 0).isEmpty()); + QCOMPARE(a.mid(INT_MIN, -1), a); + QVERIFY(a.mid(INT_MIN, INT_MAX).isNull()); + QVERIFY(a.mid(INT_MIN + 1, INT_MAX).isEmpty()); + QCOMPARE(a.mid(INT_MIN + 2, INT_MAX), a.left(1)); + QCOMPARE(a.mid(INT_MIN + a.size() + 1, INT_MAX), a); + QVERIFY(a.mid(INT_MAX).isNull()); + QVERIFY(a.mid(INT_MAX, INT_MAX).isNull()); + QCOMPARE(a.mid(-5, INT_MAX), a); + QCOMPARE(a.mid(-1, INT_MAX), a); + QCOMPARE(a.mid(0, INT_MAX), a); + QCOMPARE(a.mid(1, INT_MAX).toString(), QString("BCDEFGHIEfGEFG")); + QCOMPARE(a.mid(5, INT_MAX).toString(), QString("FGHIEfGEFG")); + QVERIFY(a.mid(20, INT_MAX).isNull()); + QCOMPARE(a.mid(-1, -1), a); + + QStringRef nullRef; + QVERIFY(nullRef.mid(3,3).toString().isEmpty()); + QVERIFY(nullRef.mid(0,0).toString().isEmpty()); + QVERIFY(nullRef.mid(9999,0).toString().isEmpty()); + QVERIFY(nullRef.mid(9999,1).toString().isEmpty()); + + QVERIFY(nullRef.mid(-1, 6).isNull()); + QVERIFY(nullRef.mid(-100, 6).isNull()); + QVERIFY(nullRef.mid(INT_MIN, 0).isNull()); + QVERIFY(nullRef.mid(INT_MIN, -1).isNull()); + QVERIFY(nullRef.mid(INT_MIN, INT_MAX).isNull()); + QVERIFY(nullRef.mid(INT_MIN + 1, INT_MAX).isNull()); + QVERIFY(nullRef.mid(INT_MIN + 2, INT_MAX).isNull()); + QVERIFY(nullRef.mid(INT_MIN + nullRef.size() + 1, INT_MAX).isNull()); + QVERIFY(nullRef.mid(INT_MAX).isNull()); + QVERIFY(nullRef.mid(INT_MAX, INT_MAX).isNull()); + QVERIFY(nullRef.mid(-5, INT_MAX).isNull()); + QVERIFY(nullRef.mid(-1, INT_MAX).isNull()); + QVERIFY(nullRef.mid(0, INT_MAX).isNull()); + QVERIFY(nullRef.mid(1, INT_MAX).isNull()); + QVERIFY(nullRef.mid(5, INT_MAX).isNull()); + QVERIFY(nullRef.mid(20, INT_MAX).isNull()); + QVERIFY(nullRef.mid(-1, -1).isNull()); + + QString ninePineapples = "~Nine pineapples~"; + QStringRef x = ninePineapples.midRef(1, ninePineapples.size() - 1); + QCOMPARE(x.mid(5, 4).toString(), QString("pine")); + QCOMPARE(x.mid(5).toString(), QString("pineapples~")); + + QCOMPARE(x.mid(-1, 6), x.mid(0, 5)); + QVERIFY(x.mid(-100, 6).isEmpty()); + QVERIFY(x.mid(INT_MIN, 0).isEmpty()); + QCOMPARE(x.mid(INT_MIN, -1).toString(), x.toString()); + QVERIFY(x.mid(INT_MIN, INT_MAX).isNull()); + QVERIFY(x.mid(INT_MIN + 1, INT_MAX).isEmpty()); + QCOMPARE(x.mid(INT_MIN + 2, INT_MAX), x.left(1)); + QCOMPARE(x.mid(INT_MIN + x.size() + 1, INT_MAX).toString(), x.toString()); + QVERIFY(x.mid(INT_MAX).isNull()); + QVERIFY(x.mid(INT_MAX, INT_MAX).isNull()); + QCOMPARE(x.mid(-5, INT_MAX).toString(), x.toString()); + QCOMPARE(x.mid(-1, INT_MAX).toString(), x.toString()); + QCOMPARE(x.mid(0, INT_MAX), x); + QCOMPARE(x.mid(1, INT_MAX).toString(), QString("ine pineapples~")); + QCOMPARE(x.mid(5, INT_MAX).toString(), QString("pineapples~")); + QVERIFY(x.mid(20, INT_MAX).isNull()); + QCOMPARE(x.mid(-1, -1), x); + + QStringRef emptyRef(&ninePineapples, 0, 0); + QVERIFY(emptyRef.mid(1).isEmpty()); + QVERIFY(emptyRef.mid(-1).isEmpty()); + QVERIFY(emptyRef.mid(0).isEmpty()); + QVERIFY(emptyRef.mid(0, 3).isEmpty()); + QVERIFY(emptyRef.mid(-10, 3).isEmpty()); +} + QTEST_APPLESS_MAIN(tst_QStringRef) #include "tst_qstringref.moc" diff --git a/tests/auto/corelib/tools/qtime/tst_qtime.cpp b/tests/auto/corelib/tools/qtime/tst_qtime.cpp index 97645ea7f6..864f90b380 100644 --- a/tests/auto/corelib/tools/qtime/tst_qtime.cpp +++ b/tests/auto/corelib/tools/qtime/tst_qtime.cpp @@ -575,9 +575,12 @@ void tst_QTime::fromStringDateFormat_data() QTest::newRow("TextDate - data1") << QString("10:12:34") << Qt::TextDate << QTime(10,12,34,0); QTest::newRow("TextDate - data2") << QString("19:03:54.998601") << Qt::TextDate << QTime(19, 3, 54, 999); QTest::newRow("TextDate - data3") << QString("19:03:54.999601") << Qt::TextDate << QTime(19, 3, 54, 999); + QTest::newRow("TextDate - data4") << QString("10:12") << Qt::TextDate << QTime(10, 12, 0, 0); QTest::newRow("TextDate - invalid, minutes") << QString::fromLatin1("23:XX:00") << Qt::TextDate << invalidTime(); + QTest::newRow("TextDate - invalid, minute fraction") << QString::fromLatin1("23:00.123456") << Qt::TextDate << invalidTime(); QTest::newRow("TextDate - invalid, seconds") << QString::fromLatin1("23:00:XX") << Qt::TextDate << invalidTime(); QTest::newRow("TextDate - invalid, milliseconds") << QString::fromLatin1("23:01:01:XXXX") << Qt::TextDate << QTime(23, 1, 1, 0); + QTest::newRow("TextDate - midnight 24") << QString("24:00:00") << Qt::TextDate << QTime(); QTest::newRow("IsoDate - valid, start of day, omit seconds") << QString::fromLatin1("00:00") << Qt::ISODate << QTime(0, 0, 0); QTest::newRow("IsoDate - valid, omit seconds") << QString::fromLatin1("22:21") << Qt::ISODate << QTime(22, 21, 0); @@ -599,7 +602,61 @@ void tst_QTime::fromStringDateFormat_data() QTest::newRow("IsoDate - data1") << QString("10:12:34") << Qt::ISODate << QTime(10,12,34,0); QTest::newRow("IsoDate - data2") << QString("19:03:54.998601") << Qt::ISODate << QTime(19, 3, 54, 999); QTest::newRow("IsoDate - data3") << QString("19:03:54.999601") << Qt::ISODate << QTime(19, 3, 54, 999); + QTest::newRow("IsoDate - midnight 24") << QString("24:00:00") << Qt::ISODate << QTime(0, 0, 0, 0); QTest::newRow("IsoDate - minute fraction midnight") << QString("24:00,0") << Qt::ISODate << QTime(0, 0, 0, 0); + + // Test Qt::RFC2822Date format (RFC 2822). + QTest::newRow("RFC 2822") << QString::fromLatin1("13 Feb 1987 13:24:51 +0100") + << Qt::RFC2822Date << QTime(13, 24, 51); + QTest::newRow("RFC 2822 with day") << QString::fromLatin1("Thu, 01 Jan 1970 00:12:34 +0000") + << Qt::RFC2822Date << QTime(0, 12, 34); + // No timezone + QTest::newRow("RFC 2822 no timezone") << QString::fromLatin1("01 Jan 1970 00:12:34") + << Qt::RFC2822Date << QTime(0, 12, 34); + // No time specified + QTest::newRow("RFC 2822 date only") << QString::fromLatin1("01 Nov 2002") + << Qt::RFC2822Date << invalidTime(); + QTest::newRow("RFC 2822 with day date only") << QString::fromLatin1("Fri, 01 Nov 2002") + << Qt::RFC2822Date << invalidTime(); + // Test invalid month, day, year + QTest::newRow("RFC 2822 invalid month name") << QString::fromLatin1("13 Fev 1987 13:24:51 +0100") + << Qt::RFC2822Date << QTime(13, 24, 51); + QTest::newRow("RFC 2822 invalid day") << QString::fromLatin1("36 Fev 1987 13:24:51 +0100") + << Qt::RFC2822Date << QTime(13, 24, 51); + QTest::newRow("RFC 2822 invalid year") << QString::fromLatin1("13 Fev 0000 13:24:51 +0100") + << Qt::RFC2822Date << QTime(13, 24, 51); + // Test invalid characters (should ignore invalid characters at end of string). + QTest::newRow("RFC 2822 invalid character at end") << QString::fromLatin1("01 Jan 2012 08:00:00 +0100!") + << Qt::RFC2822Date << QTime(8, 0, 0); + QTest::newRow("RFC 2822 invalid character at front") << QString::fromLatin1("!01 Jan 2012 08:00:00 +0000") + << Qt::RFC2822Date << invalidTime(); + QTest::newRow("RFC 2822 invalid character both ends") << QString::fromLatin1("!01 Jan 2012 08:00:00 +0000!") + << Qt::RFC2822Date << invalidTime(); + QTest::newRow("RFC 2822 invalid character at front, 2 at back") << QString::fromLatin1("!01 Jan 2012 08:00:00 +0000..") + << Qt::RFC2822Date << invalidTime(); + QTest::newRow("RFC 2822 invalid character 2 at front") << QString::fromLatin1("!!01 Jan 2012 08:00:00 +0000") + << Qt::RFC2822Date << invalidTime(); + + // Test Qt::RFC2822Date format (RFC 850 and 1036). + QTest::newRow("RFC 850 and 1036") << QString::fromLatin1("Fri Feb 13 13:24:51 1987 +0100") + << Qt::RFC2822Date << QTime(13, 24, 51); + // No timezone + QTest::newRow("RFC 850 and 1036 no timezone") << QString::fromLatin1("Thu Jan 01 00:12:34 1970") + << Qt::RFC2822Date << QTime(0, 12, 34); + // No time specified + QTest::newRow("RFC 850 and 1036 date only") << QString::fromLatin1("Fri Nov 01 2002") + << Qt::RFC2822Date << invalidTime(); + // Test invalid characters (should ignore invalid characters at end of string). + QTest::newRow("RFC 850 and 1036 invalid character at end") << QString::fromLatin1("Sun Jan 01 08:00:00 2012 +0100!") + << Qt::RFC2822Date << QTime(8, 0, 0); + QTest::newRow("RFC 850 and 1036 invalid character at front") << QString::fromLatin1("!Sun Jan 01 08:00:00 2012 +0000") + << Qt::RFC2822Date << invalidTime(); + QTest::newRow("RFC 850 and 1036 invalid character both ends") << QString::fromLatin1("!Sun Jan 01 08:00:00 2012 +0000!") + << Qt::RFC2822Date << invalidTime(); + QTest::newRow("RFC 850 and 1036 invalid character at front, 2 at back") << QString::fromLatin1("!Sun Jan 01 08:00:00 2012 +0000..") + << Qt::RFC2822Date << invalidTime(); + + QTest::newRow("RFC empty") << QString::fromLatin1("") << Qt::RFC2822Date << invalidTime(); } void tst_QTime::fromStringDateFormat() @@ -614,25 +671,28 @@ void tst_QTime::fromStringDateFormat() void tst_QTime::toStringDateFormat_data() { - // Since we can't define an element of Qt::DateFormat, str1 will be the string - // in TextDate format, and str2 will be the time in ISODate format. - - QTest::addColumn<QTime>("t"); - QTest::addColumn<QString>("str1"); - QTest::addColumn<QString>("str2"); - - QTest::newRow( "data0" ) << QTime(0,0,0,0) << QString("00:00:00") << QString("00:00:00"); - QTest::newRow( "data1" ) << QTime(10,12,34,0) << QString("10:12:34") << QString("10:12:34"); + QTest::addColumn<QTime>("time"); + QTest::addColumn<Qt::DateFormat>("format"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("00:00:00.000") << QTime(0, 0, 0, 0) << Qt::TextDate << QString("00:00:00.000"); + QTest::newRow("ISO 00:00:00.000") << QTime(0, 0, 0, 0) << Qt::ISODate << QString("00:00:00.000"); + QTest::newRow("Text 10:12:34.000") << QTime(10, 12, 34, 0) << Qt::TextDate << QString("10:12:34.000"); + QTest::newRow("ISO 10:12:34.000") << QTime(10, 12, 34, 0) << Qt::ISODate << QString("10:12:34.000"); + QTest::newRow("Text 10:12:34.001") << QTime(10, 12, 34, 001) << Qt::TextDate << QString("10:12:34.001"); + QTest::newRow("ISO 10:12:34.001") << QTime(10, 12, 34, 001) << Qt::ISODate << QString("10:12:34.001"); + QTest::newRow("Text 10:12:34.999") << QTime(10, 12, 34, 999) << Qt::TextDate << QString("10:12:34.999"); + QTest::newRow("ISO 10:12:34.999") << QTime(10, 12, 34, 999) << Qt::ISODate << QString("10:12:34.999"); + QTest::newRow("RFC2822Date") << QTime(10, 12, 34, 999) << Qt::RFC2822Date << QString("10:12:34"); } void tst_QTime::toStringDateFormat() { - QFETCH( QTime, t ); - QFETCH( QString, str1 ); - QFETCH( QString, str2 ); + QFETCH(QTime, time); + QFETCH(Qt::DateFormat, format); + QFETCH(QString, expected); - QCOMPARE( str1, t.toString( Qt::TextDate ) ); - QCOMPARE( str2, t.toString( Qt::ISODate ) ); + QCOMPARE(time.toString(format), expected); } void tst_QTime::toStringFormat_data() diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro index f8b2437d35..fbc1b996f7 100644 --- a/tests/auto/corelib/tools/tools.pro +++ b/tests/auto/corelib/tools/tools.pro @@ -8,6 +8,7 @@ SUBDIRS=\ qbytedatabuffer \ qcache \ qchar \ + qcommandlineparser \ qcontiguouscache \ qcryptographichash \ qdate \ @@ -18,6 +19,7 @@ SUBDIRS=\ qfreelist \ qhash \ qline \ + qlinkedlist \ qlist \ qlocale \ qmap \ |